View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
17   */
18  
19  package org.mycore.restapi;
20  
21  import java.io.IOException;
22  import java.net.URI;
23  import java.util.Set;
24  import java.util.stream.Collectors;
25  
26  import org.apache.commons.lang3.StringUtils;
27  import org.apache.logging.log4j.LogManager;
28  import org.apache.logging.log4j.Logger;
29  import org.apache.solr.client.solrj.response.QueryResponse;
30  import org.apache.solr.client.solrj.util.ClientUtils;
31  import org.apache.solr.common.SolrDocumentList;
32  import org.apache.solr.common.params.ModifiableSolrParams;
33  import org.mycore.common.MCRException;
34  import org.mycore.common.config.MCRConfiguration2;
35  import org.mycore.datamodel.metadata.MCRObjectID;
36  import org.mycore.solr.MCRSolrClientFactory;
37  
38  import jakarta.annotation.Priority;
39  import jakarta.servlet.http.HttpServletResponse;
40  import jakarta.ws.rs.BadRequestException;
41  import jakarta.ws.rs.NotFoundException;
42  import jakarta.ws.rs.Priorities;
43  import jakarta.ws.rs.container.ContainerRequestContext;
44  import jakarta.ws.rs.container.ContainerRequestFilter;
45  import jakarta.ws.rs.container.PreMatching;
46  import jakarta.ws.rs.container.ResourceInfo;
47  import jakarta.ws.rs.core.Context;
48  import jakarta.ws.rs.core.Response;
49  import jakarta.ws.rs.core.UriInfo;
50  import jakarta.ws.rs.ext.Provider;
51  
52  /**
53   * This prematching filter checks the given MCRObjectIDs in an REST API call beginning with /objects,
54   * normalizes them and sends a redirect if necessary.
55   * 
56   * @author Robert Stephan
57   *
58   */
59  @Provider
60  @PreMatching
61  @Priority(Priorities.AUTHORIZATION - 10)
62  public class MCRNormalizeMCRObjectIDsFilter implements ContainerRequestFilter {
63  
64      private static final Logger LOGGER = LogManager.getLogger();
65  
66      private static Set<String> SEARCHKEYS_FOR_OBJECTS = MCRConfiguration2
67          .getString("MCR.RestAPI.V2.AlternativeIdentifier.Objects.Keys").stream()
68          .flatMap(MCRConfiguration2::splitValue).collect(Collectors.toSet());
69  
70      private static Set<String> SEARCHKEYS_FOR_DERIVATES = MCRConfiguration2
71          .getString("MCR.RestAPI.V2.AlternativeIdentifier.Derivates.Keys").stream()
72          .flatMap(MCRConfiguration2::splitValue).collect(Collectors.toSet());
73  
74      @Context
75      ResourceInfo resourceInfo;
76  
77      @Context
78      HttpServletResponse response;
79  
80      @Override
81      public void filter(ContainerRequestContext requestContext) throws IOException {
82          UriInfo uriInfo = requestContext.getUriInfo();
83          String path = uriInfo.getPath().toString();
84          String[] pathParts = path.split("/", -1);
85          if (pathParts.length >= 2 && "objects".equals(pathParts[0])) {
86              String mcrid = pathParts[1];
87  
88              String mcridExtension = "";
89              if (mcrid.endsWith(".xml")) {
90                  mcridExtension = ".xml";
91                  mcrid = mcrid.substring(0, mcrid.length() - 4);
92              }
93              if (mcrid.endsWith(".json")) {
94                  mcridExtension = ".json";
95                  mcrid = mcrid.substring(0, mcrid.length() - 5);
96              }
97              try {
98                  if (!SEARCHKEYS_FOR_OBJECTS.isEmpty() && mcrid.contains(":")) {
99                      pathParts[1] = retrieveMCRObjIDfromSOLR(mcrid) + mcridExtension;
100                 } else {
101                     MCRObjectID mcrObjID = MCRObjectID.getInstance(mcrid);
102                     // set the properly formated mcrObjID back to URL
103                     pathParts[1] = mcrObjID.toString() + mcridExtension;
104                 }
105             } catch (MCRException ex) {
106                 // ignore
107 
108             }
109 
110             if (pathParts.length >= 4 && pathParts[2].equals("derivates")) {
111                 String derid = pathParts[3];
112 
113                 String deridExtension = "";
114                 if (derid.endsWith(".xml")) {
115                     deridExtension = ".xml";
116                     derid = derid.substring(0, mcrid.length() - 4);
117                 }
118                 if (derid.endsWith(".json")) {
119                     deridExtension = ".json";
120                     derid = derid.substring(0, mcrid.length() - 5);
121                 }
122                 try {
123                     if (!SEARCHKEYS_FOR_DERIVATES.isEmpty() && derid.contains(":")) {
124                         pathParts[3] = retrieveMCRDerIDfromSOLR(mcrid, derid) + deridExtension;
125                     } else {
126                         MCRObjectID mcrDerID = MCRObjectID.getInstance(derid);
127                         // set the properly formated mcrObjID back to URL
128                         pathParts[3] = mcrDerID.toString() + deridExtension;
129                     }
130                 } catch (MCRException ex) {
131                     // ignore
132                 }
133             }
134             String newPath = StringUtils.join(pathParts, "/");
135             if (!newPath.equals(path)) {
136                 String queryString = uriInfo.getRequestUri().getQuery();
137                 URI uri = uriInfo.getBaseUri().resolve(queryString == null ? newPath : newPath + "?" + queryString);
138                 requestContext.abortWith(Response.temporaryRedirect(uri).build());
139             }
140         }
141     }
142 
143     private String retrieveMCRDerIDfromSOLR(String mcrid, String derid) {
144         String key = derid.substring(0, derid.indexOf(":"));
145         String value = derid.substring(derid.indexOf(":") + 1);
146         if (SEARCHKEYS_FOR_DERIVATES.contains(key)) {
147             ModifiableSolrParams params = new ModifiableSolrParams();
148             params.set("start", 0);
149             params.set("rows", 1);
150             params.set("fl", "id");
151             params.set("fq", "objectKind:mycorederivate");
152             params.set("fq", "returnId:" + mcrid);
153             params.set("q", key + ":" + ClientUtils.escapeQueryChars(value));
154             params.set("sort", "derivateOrder asc");
155             QueryResponse solrResponse = null;
156             try {
157                 solrResponse = MCRSolrClientFactory.getMainSolrClient().query(params);
158             } catch (Exception e) {
159                 LOGGER.error("Error retrieving derivate id from SOLR", e);
160             }
161             if (solrResponse != null) {
162                 SolrDocumentList solrResults = solrResponse.getResults();
163                 if (solrResults.getNumFound() == 1) {
164                     return String.valueOf(solrResults.get(0).getFieldValue("id"));
165                 }
166                 if (solrResults.getNumFound() == 0) {
167                     throw new NotFoundException("No MyCoRe Derivate ID found for query " + derid);
168                 }
169                 if (solrResults.getNumFound() > 1) {
170                     throw new BadRequestException(
171                         "The query " + derid + " does not return a unique MyCoRe Derivate ID");
172                 }
173             }
174         }
175         return derid;
176     }
177 
178     private String retrieveMCRObjIDfromSOLR(String mcrid) {
179         String key = mcrid.substring(0, mcrid.indexOf(":"));
180         String value = mcrid.substring(mcrid.indexOf(":") + 1);
181         if (SEARCHKEYS_FOR_OBJECTS.contains(key)) {
182             ModifiableSolrParams params = new ModifiableSolrParams();
183             params.set("start", 0);
184             params.set("rows", 1);
185             params.set("fl", "id");
186             params.set("fq", "objectKind:mycoreobject");
187             params.set("q", key + ":" + ClientUtils.escapeQueryChars(value));
188             QueryResponse solrResponse = null;
189             try {
190                 solrResponse = MCRSolrClientFactory.getMainSolrClient().query(params);
191             } catch (Exception e) {
192                 LOGGER.error("Error retrieving object id from SOLR", e);
193             }
194             if (solrResponse != null) {
195                 SolrDocumentList solrResults = solrResponse.getResults();
196                 if (solrResults.getNumFound() == 1) {
197                     return String.valueOf(solrResults.get(0).getFieldValue("id"));
198                 }
199                 if (solrResults.getNumFound() == 0) {
200                     throw new NotFoundException("No MyCoRe ID found for query " + mcrid);
201                 }
202                 if (solrResults.getNumFound() > 1) {
203                     throw new BadRequestException("The query " + mcrid + " does not return a unique MyCoRe ID");
204                 }
205             }
206         }
207         return mcrid;
208     }
209 }