View Javadoc

1   package org.fedoracommons.funapi.pmh;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.io.StringReader;
7   import java.io.StringWriter;
8   import java.io.UnsupportedEncodingException;
9   
10  import java.net.URL;
11  
12  import javax.xml.transform.Source;
13  import javax.xml.transform.Transformer;
14  import javax.xml.transform.TransformerException;
15  import javax.xml.transform.TransformerFactory;
16  import javax.xml.transform.dom.DOMSource;
17  import javax.xml.transform.stream.StreamResult;
18  import javax.xml.xpath.XPath;
19  import javax.xml.xpath.XPathConstants;
20  import javax.xml.xpath.XPathExpressionException;
21  import javax.xml.xpath.XPathFactory;
22  
23  import org.apache.commons.httpclient.HttpClient;
24  import org.apache.commons.httpclient.HttpMethod;
25  import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
26  import org.apache.commons.httpclient.UsernamePasswordCredentials;
27  import org.apache.commons.httpclient.auth.AuthScope;
28  import org.apache.commons.httpclient.methods.GetMethod;
29  
30  import org.w3c.dom.Node;
31  import org.w3c.dom.NodeList;
32  
33  import org.xml.sax.InputSource;
34  
35  import org.fedoracommons.funapi.FormatException;
36  import org.fedoracommons.funapi.IdentifierException;
37  import org.fedoracommons.funapi.ObjectResolver;
38  import org.fedoracommons.funapi.UnapiException;
39  import org.fedoracommons.funapi.UnapiFormat;
40  import org.fedoracommons.funapi.UnapiFormats;
41  import org.fedoracommons.funapi.UnapiObject;
42  import org.fedoracommons.funapi.utilities.NamespaceContextImpl;
43  
44  
45  import static org.apache.commons.httpclient.HttpStatus.SC_OK;
46  
47  /**
48   *
49   * @author Edwin Shin
50   * @since 0.1
51   * @version $Id: AbstractPmhResolver.java 33 2008-10-25 19:31:36Z pangloss $
52   */
53  public abstract class AbstractPmhResolver
54          implements ObjectResolver {
55      
56      private final static String FORMATS = "%s?verb=ListMetadataFormats";
57      private final static String RECORD = "%s?verb=GetRecord&metadataPrefix=%s&identifier=%s";
58      private NamespaceContextImpl nsCtx;
59      private HttpClient httpClient;
60      private HttpMethod httpMethod;
61      
62      public UnapiFormats getFormats() throws UnapiException {
63          String mdFormats = listMetadataFormats();
64          UnapiFormats formats = new UnapiFormats(null);
65          XPath xpath = getXPath();
66          NodeList nodelist = null;
67          try {
68              nodelist = (NodeList)xpath.evaluate("//oai:metadataFormat", 
69                                                  new InputSource(new StringReader(mdFormats)), 
70                                                  XPathConstants.NODESET);
71              for(int i = 0; i < nodelist.getLength(); i++) {
72                  Node node = nodelist.item(i);
73                  String format = xpath.evaluate("oai:metadataPrefix", node);
74                  String docs = xpath.evaluate("oai:schema", node);
75                  UnapiFormat uFormat = new UnapiFormat(format, "application/xml", docs);
76                  formats.addFormat(uFormat);
77              }
78          } catch (XPathExpressionException e) {
79              throw new UnapiException(e.getMessage(), e);
80          }
81          return formats;
82      }
83      
84      public UnapiFormats getFormats(String id) throws UnapiException {
85          UnapiFormats formats = getFormats();
86          formats.setId(id);
87          return formats;
88      }
89  
90      public UnapiObject getObject(String id, String format) throws UnapiException {
91          try {
92              String record = getRecord(id, format);         
93              XPath xpath = getXPath();
94  
95              Node pmh = (Node)xpath.evaluate("//oai:OAI-PMH", 
96                                              new InputSource(new StringReader(record)), 
97                                              XPathConstants.NODE);
98              
99              Node metadata = (Node)xpath.evaluate("//oai:metadata/*", 
100                                                  pmh, 
101                                                  XPathConstants.NODE);
102 
103             if (metadata == null) {
104                 String error = xpath.evaluate("//oai:error/@code", pmh);
105                 if (error.equalsIgnoreCase("idDoesNotExist")) {
106                     throw new IdentifierException(error);
107                 } else if (error.equalsIgnoreCase("cannotDisseminateFormat")) {
108                     throw new FormatException(error);
109                 } else {
110                     throw new UnapiException(error);
111                 }
112             }
113             
114             TransformerFactory xformFactory = TransformerFactory.newInstance();
115             Transformer transformer = xformFactory.newTransformer();  
116             
117             Source source = new DOMSource(metadata);
118             StringWriter sw = new StringWriter();            
119             transformer.transform(source, new StreamResult(sw));
120             InputStream in = new ByteArrayInputStream(sw.toString().getBytes("UTF-8"));
121             return new UnapiObject(in, "application/xml");
122         } catch (XPathExpressionException e) {
123             throw new UnapiException(e.getMessage(), e);
124         } catch (TransformerException e) {
125             throw new UnapiException(e.getMessage(), e);
126         } catch (UnsupportedEncodingException e) {
127             throw new UnapiException(e.getMessage(), e);
128         }
129     }
130     
131     private XPath getXPath() {
132         XPathFactory xpFactory = XPathFactory.newInstance();
133         XPath xpath = xpFactory.newXPath();
134         if (nsCtx == null) {
135             nsCtx = new NamespaceContextImpl();
136             nsCtx.addNamespace("oai", "http://www.openarchives.org/OAI/2.0/");
137             nsCtx.addNamespace("oai_dc", "http://www.openarchives.org/OAI/2.0/oai_dc/");
138             nsCtx.addNamespace("dc", "http://purl.org/dc/elements/1.1/");
139         }
140         xpath.setNamespaceContext(nsCtx);
141         return xpath;
142     }
143     
144     protected HttpClient getHttpClient() {
145         if (httpClient != null) {
146             return httpClient;
147         }
148         MultiThreadedHttpConnectionManager connectionManager = 
149             new MultiThreadedHttpConnectionManager();
150 
151         HttpClient client = new HttpClient(connectionManager);
152         client.getParams().setAuthenticationPreemptive(true);
153         if (getUsername() != null && getPassword() != null) {
154             client.getState().setCredentials(
155                  new AuthScope(getPmhBaseUrl().getHost(), 
156                                getPmhBaseUrl().getPort(), null),
157                  new UsernamePasswordCredentials(getUsername(), getPassword())
158                  );
159         }
160         return client;
161     }
162     
163     protected void setHttpClient(HttpClient httpClient) {
164         this.httpClient = httpClient;
165     }
166     
167     protected void setHttpMethod(HttpMethod httpMethod) {
168         this.httpMethod = httpMethod;
169     }
170     
171     private String listMetadataFormats() throws UnapiException {
172         String url = String.format(FORMATS, getPmhBaseUrl());
173         return getResponse(url);
174     }
175     
176     private String getRecord(String id, String format) throws UnapiException {
177         String url = String.format(RECORD, getPmhBaseUrl(), format, getPmhId(id));
178         return getResponse(url);
179     }
180     
181     private String getResponse(String url) throws UnapiException {
182         HttpMethod getMethod;
183         if (httpMethod == null) {
184             getMethod = new GetMethod(url);
185         } else {
186             getMethod = httpMethod;
187         }
188         try {
189             int status = getHttpClient().executeMethod(getMethod);
190             if (status != SC_OK) {
191                 throw new UnapiException(status, getMethod.getStatusText());
192             }
193             return getMethod.getResponseBodyAsString();
194         } catch (IOException e) {
195             throw new UnapiException(e.getMessage(), e);
196         } finally {
197             getMethod.releaseConnection();
198         }
199     }
200     
201     /**
202      * @param id an unAPI identifier
203      * @return the corresponding OAI-PMH identifier
204      */
205     protected abstract String getPmhId(String id);
206 
207     /**
208      * 
209      * @return The base URL of the OAI-PMH service, e.g. http://localhost:8080/oai/request.
210      */
211     protected abstract URL getPmhBaseUrl();
212     
213     /**
214      * @return The username, if any, required to access the OAI-PMH service.
215      */
216     protected abstract String getUsername();
217     
218     /**
219      * @return The password, if any, required to access the OAI-PMH service.
220      */
221     protected abstract String getPassword();
222 }