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.pi.purl;
20  
21  import java.io.BufferedReader;
22  import java.io.IOException;
23  import java.io.InputStreamReader;
24  import java.io.OutputStreamWriter;
25  import java.net.HttpURLConnection;
26  import java.net.URL;
27  import java.net.URLEncoder;
28  import java.nio.charset.StandardCharsets;
29  import java.util.List;
30  
31  import javax.xml.parsers.DocumentBuilder;
32  import javax.xml.parsers.DocumentBuilderFactory;
33  
34  import org.apache.logging.log4j.LogManager;
35  import org.apache.logging.log4j.Logger;
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Element;
38  
39  /**
40   * PURL Manager to register Persistent URLs on a PURL server
41   * <p>
42   * for further documentation see PURLZ Wiki:
43   * https://code.google.com/archive/p/persistenturls/wikis
44   * <p>
45   * Hint:
46   * -----
47   * Please check in your code that you do not register / override regular PURLs in test / development
48   * by checking:
49   * if (resolvingURL.contains("localhost")) {
50   * purl = "/test" + purl;
51   * }
52   *
53   * @author Robert Stephan
54   */
55  public class MCRPURLManager {
56      private static final Logger LOGGER = LogManager.getLogger();
57  
58      private static final String ADMIN_PATH = "/admin";
59  
60      private static final String PURL_PATH = ADMIN_PATH + "/purl";
61  
62      private static final String COOKIE_HEADER_PARAM = "Cookie";
63  
64      private String purlServerBaseURL;
65  
66      private String cookie = null;
67  
68      /**
69       * sets the session cookie, if the login was successful
70       *
71       * @param purlServerURL - the base URL of the PURL server
72       * @param user          - the PURL server user
73       * @param password      - the user's password
74       */
75      public void login(String purlServerURL, String user, String password) {
76          HttpURLConnection conn = null;
77          try {
78              purlServerBaseURL = purlServerURL;
79              // Get Cookie
80              URL url = new URL(purlServerBaseURL + ADMIN_PATH + "/login/login.bsh?referrer=/docs/index.html");
81              conn = (HttpURLConnection) url.openConnection();
82              conn.connect();
83  
84              conn.getHeaderFields()
85                  .getOrDefault("Set-Cookie", List.of())
86                  .forEach(cookie -> {
87                      this.cookie = cookie;
88                      LOGGER.debug("Cookie: " + cookie);
89                  });
90              conn.disconnect();
91  
92              // Login
93              String data = "id=" + URLEncoder.encode(user, StandardCharsets.UTF_8);
94              data += "&passwd=" + URLEncoder.encode(password, StandardCharsets.UTF_8);
95  
96              url = new URL(purlServerBaseURL + ADMIN_PATH + "/login/login-submit.bsh");
97              conn = (HttpURLConnection) url.openConnection();
98              conn.setRequestMethod("POST");
99              conn.setRequestProperty(COOKIE_HEADER_PARAM, cookie);
100 
101             conn.setDoOutput(true);
102             try (OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)) {
103                 wr.write(data);
104                 wr.flush();
105                 if (conn.getResponseCode() == 200) {
106                     LOGGER.info(conn.getRequestMethod() + " " + conn.getURL() + " -> " + conn.getResponseCode());
107                 } else {
108                     LOGGER.error(conn.getRequestMethod() + " " + conn.getURL() + " -> " + conn.getResponseCode());
109                 }
110 
111                 // Get the response
112                 try (BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream(),
113                     StandardCharsets.UTF_8))) {
114 
115                     String line;
116                     while ((line = rd.readLine()) != null) {
117                         if ("PURL User Login Failure".equals(line.trim())) {
118                             cookie = null;
119                             break;
120                         }
121 
122                     }
123                 }
124             }
125             conn.disconnect();
126 
127         } catch (IOException e) {
128             if (!e.getMessage().contains(
129                 "Server returned HTTP response code: 403 for URL: ")) {
130                 LOGGER.error(e);
131             }
132         } finally {
133             if (conn != null) {
134                 conn.disconnect();
135             }
136         }
137     }
138 
139     /**
140      * logout from PURL server
141      */
142     public void logout() {
143         HttpURLConnection conn = null;
144         int responseCode = -1;
145         try {
146             URL url = new URL(purlServerBaseURL + ADMIN_PATH + "/logout?referrer=/docs/index.html");
147             conn = (HttpURLConnection) url.openConnection();
148             conn.setRequestMethod("POST");
149             conn.setRequestProperty(COOKIE_HEADER_PARAM, cookie);
150 
151             conn.setDoOutput(true);
152             try (OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)) {
153                 wr.flush();
154             }
155             responseCode = conn.getResponseCode();
156             LOGGER.debug(conn.getRequestMethod() + " " + conn.getURL() + " -> " + responseCode);
157         } catch (IOException e) {
158             if (!e.getMessage().contains(
159                 "Server returned HTTP response code: 403 for URL: ")) {
160                 LOGGER.error(conn.getRequestMethod() + " " + conn.getURL() + " -> " + responseCode, e);
161             }
162         } finally {
163             if (conn != null) {
164                 conn.disconnect();
165             }
166         }
167     }
168 
169     // returns the response code
170 
171     /**
172      * register a new PURL
173      *
174      * @param purl        - the PURL
175      * @param target      the target URL
176      * @param type        - the PURL type
177      * @param maintainers - the maintainers
178      * @return the HTTP Status Code of the request
179      */
180     public int registerNewPURL(String purl, String target, String type, String maintainers) {
181         int response = 0;
182         HttpURLConnection conn = null;
183         try {
184             // opener.open("http://localhost:8080/admin/purl/net/test2",
185             // urllib.urlencode(dict(type="410", maintainers="admin"))).read().close() #
186             // Create a 410 purl
187 
188             URL url = new URL(purlServerBaseURL + PURL_PATH + purl);
189             LOGGER.debug(url.toString());
190 
191             String data = "target=" + URLEncoder.encode(target, StandardCharsets.UTF_8);
192             data += "&maintainers=" + maintainers;
193             data += "&type=" + type;
194 
195             LOGGER.debug(data);
196 
197             // Send data
198 
199             conn = (HttpURLConnection) url.openConnection();
200             conn.setRequestProperty(COOKIE_HEADER_PARAM, cookie);
201             conn.setRequestMethod("POST");
202 
203             conn.setDoOutput(true);
204 
205             try (OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8)) {
206                 wr.write(data);
207                 wr.flush();
208             }
209             response = conn.getResponseCode();
210 
211             if (response != 200 && conn.getErrorStream() != null && LOGGER.isErrorEnabled()) {
212                 try (BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getErrorStream(),
213                     StandardCharsets.UTF_8))) {
214                     String line;
215                     LOGGER.error(conn.getRequestMethod() + " " + conn.getURL() + " -> " + conn.getResponseCode());
216                     while ((line = rd.readLine()) != null) {
217                         LOGGER.error(line);
218                     }
219                 }
220             }
221         } catch (Exception e) {
222             LOGGER.error(e);
223         } finally {
224             if (conn != null) {
225                 conn.disconnect();
226             }
227         }
228         return response;
229     }
230 
231     /**
232      * updates an existing PURL
233      *
234      * @param purl        - the PURL (relative URL)
235      * @param target      - the target URL
236      * @param type        - the PURL type
237      * @param maintainers list of maintainers (PURL server users or groups)
238      * @return the HTTP Status Code of the request
239      */
240     public int updateExistingPURL(String purl, String target, String type, String maintainers) {
241         int response = 0;
242         HttpURLConnection conn = null;
243         try {
244             // opener.open("http://localhost:8080/admin/purl/net/test2",
245             // urllib.urlencode(dict(type="410", maintainers="admin"))).read().close() #
246             // Create a 410 purl
247 
248             String strURL = purlServerBaseURL + PURL_PATH + purl;
249             strURL += "?target=" + URLEncoder.encode(target, StandardCharsets.UTF_8) + "&maintainers=" + maintainers
250                 + "&type=" + type;
251 
252             URL url = new URL(strURL);
253             LOGGER.debug(url.toString());
254 
255             conn = (HttpURLConnection) url.openConnection();
256             conn.setRequestProperty(COOKIE_HEADER_PARAM, cookie);
257             conn.setRequestMethod("PUT");
258             response = conn.getResponseCode();
259 
260             if (response != 200 && conn.getErrorStream() != null && LOGGER.isErrorEnabled()) {
261                 try (BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getErrorStream(),
262                     StandardCharsets.UTF_8))) {
263                     String line = null;
264                     LOGGER.error(conn.getRequestMethod() + " " + conn.getURL() + " -> " + conn.getResponseCode());
265                     while ((line = rd.readLine()) != null) {
266                         LOGGER.error(line);
267                     }
268                 }
269             }
270         } catch (Exception e) {
271             LOGGER.error(e);
272         } finally {
273             if (conn != null) {
274                 conn.disconnect();
275             }
276         }
277         return response;
278     }
279 
280     /**
281      * deletes an existing PURL
282      *
283      * @param purl
284      * @return the HTTP Status Code of the request
285      */
286     public int deletePURL(String purl) {
287         int response = 0;
288         HttpURLConnection conn = null;
289         try {
290             URL url = new URL(purlServerBaseURL + PURL_PATH + purl);
291             LOGGER.debug(url.toString());
292 
293             conn = (HttpURLConnection) url.openConnection();
294             conn.setRequestProperty(COOKIE_HEADER_PARAM, cookie);
295             conn.setRequestMethod("DELETE");
296             response = conn.getResponseCode();
297 
298             if (response != 200 || conn.getErrorStream() != null && LOGGER.isErrorEnabled()) {
299                 try (BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getErrorStream(),
300                     StandardCharsets.UTF_8))) {
301                     String line = null;
302                     LOGGER.error(conn.getRequestMethod() + " " + conn.getURL() + " -> " + conn.getResponseCode());
303                     while ((line = rd.readLine()) != null) {
304                         LOGGER.error(line);
305                     }
306                 }
307             }
308         } catch (Exception e) {
309             LOGGER.error(e);
310         } finally {
311             if (conn != null) {
312                 conn.disconnect();
313             }
314         }
315         return response;
316     }
317 
318     /**
319      * check if a purl has the given target url
320      *
321      * @param purl      - the purl
322      * @param targetURL - the target URL
323      * @return true, if the target URL is registered at the given PURL
324      */
325     public boolean isPURLTargetURLUnchanged(String purl, String targetURL) {
326         HttpURLConnection conn = null;
327         try {
328             URL url = new URL(purlServerBaseURL + PURL_PATH + purl);
329             conn = (HttpURLConnection) url.openConnection();
330             int response = conn.getResponseCode();
331 
332             if (response == 200) {
333                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
334                 DocumentBuilder db = dbf.newDocumentBuilder();
335                 Document doc = db.parse(conn.getInputStream());
336                 /*
337                  * <purl status="1"> <id>/test/rosdok/ppn750527188</id> <type>302</type>
338                  * <maintainers><uid>rosdok</uid><uid>test</uid></maintainers>
339                  * <target><url>http://localhost:8080/rosdok/resolve/id/
340                  * rosdok_document_0000000259</url></target> </purl>
341                  */
342                 Element eTarget = (Element) doc.getDocumentElement().getElementsByTagName("target").item(0);
343                 Element eTargetUrl = (Element) eTarget.getElementsByTagName("url").item(0);
344                 return targetURL.equals(eTargetUrl.getTextContent().trim());
345             }
346         } catch (Exception e) {
347             LOGGER.error(e);
348         } finally {
349             if (conn != null) {
350                 conn.disconnect();
351             }
352         }
353         return false;
354     }
355 
356     /**
357      * return the PURL metadata
358      *
359      * @param purl      - the purl
360      * @return an XML document containing the metadata of the PURL
361      *        or null if the PURL does not exist
362      */
363     public Document retrievePURLMetadata(String purl, String targetURL) {
364         HttpURLConnection conn = null;
365         try {
366             URL url = new URL(purlServerBaseURL + PURL_PATH + purl);
367             conn = (HttpURLConnection) url.openConnection();
368             int response = conn.getResponseCode();
369 
370             if (response == 200) {
371 
372                 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
373                 DocumentBuilder db = dbf.newDocumentBuilder();
374 
375                 return db.parse(conn.getInputStream());
376                 /* <purl status="1">
377                  *   <id>/test/rosdok/ppn750527188</id> 
378                  *   <type>302</type>
379                  *   <maintainers>
380                  *     <uid>rosdok</uid>
381                  *     <uid>test</uid>
382                  *   </maintainers>
383                  *   <target>
384                  *     <url>http://localhost:8080/rosdok/resolve/id/rosdok_document_0000000259</url>
385                  *   </target> 
386                  * </purl>
387                  */
388             }
389         } catch (Exception e) {
390             LOGGER.error(e);
391         } finally {
392             if (conn != null) {
393                 conn.disconnect();
394             }
395         }
396         return null;
397     }
398 
399     /**
400      * check if a PURL exists
401      *
402      * @param purl      - the purl
403      * @return true, if the given PURL is known
404      */
405     public boolean existsPURL(String purl) {
406         HttpURLConnection conn = null;
407         try {
408             URL url = new URL(purlServerBaseURL + PURL_PATH + purl);
409             conn = (HttpURLConnection) url.openConnection();
410             int response = conn.getResponseCode();
411             return response == 200;
412         } catch (Exception e) {
413             LOGGER.error(e);
414         } finally {
415             if (conn != null) {
416                 conn.disconnect();
417             }
418         }
419         return false;
420     }
421 }