source: VirtualCollectionRegistry/trunk/VirtualCollectionRegistry/src/main/java/eu/clarin/cmdi/virtualcollectionregistry/pid/GWDGPersistentIdentifierProvider.java @ 5594

Last change on this file since 5594 was 5594, checked in by Twan Goosen, 10 years ago

Added REST service information to the help page. Moved the documentation into a separate 'doc' directory in the project.
Refs #614

  • Property svn:eol-style set to native
File size: 12.2 KB
Line 
1package eu.clarin.cmdi.virtualcollectionregistry.pid;
2
3import eu.clarin.cmdi.virtualcollectionregistry.ServletUtils;
4import eu.clarin.cmdi.virtualcollectionregistry.VirtualCollectionRegistryException;
5import eu.clarin.cmdi.virtualcollectionregistry.model.VirtualCollection;
6
7import java.net.URI;
8import java.net.URISyntaxException;
9import java.util.ArrayList;
10import java.util.Collections;
11import java.util.HashMap;
12import java.util.List;
13import java.util.Map;
14
15import javax.servlet.ServletContext;
16import javax.xml.stream.XMLInputFactory;
17import javax.xml.stream.XMLStreamConstants;
18import javax.xml.stream.XMLStreamReader;
19import org.apache.http.HttpEntity;
20import org.apache.http.HttpResponse;
21import org.apache.http.NameValuePair;
22import org.apache.http.StatusLine;
23import org.apache.http.auth.AuthScope;
24import org.apache.http.auth.UsernamePasswordCredentials;
25import org.apache.http.client.entity.UrlEncodedFormEntity;
26import org.apache.http.client.methods.HttpPost;
27import org.apache.http.impl.client.DefaultHttpClient;
28import org.apache.http.message.BasicNameValuePair;
29import org.apache.http.params.CoreProtocolPNames;
30import org.apache.http.protocol.BasicHttpContext;
31import org.apache.http.protocol.HttpContext;
32import org.apache.http.util.EntityUtils;
33
34import org.slf4j.Logger;
35import org.slf4j.LoggerFactory;
36import org.springframework.beans.factory.annotation.Autowired;
37import org.springframework.context.annotation.Profile;
38import org.springframework.stereotype.Service;
39
40@Service
41@Profile("vcr.pid.gwdg")
42public class GWDGPersistentIdentifierProvider implements
43        PersistentIdentifierProvider {
44
45    public static final String BASE_URI = "eu.clarin.cmdi.virtualcollectionregistry.base_uri";
46
47    private static enum Attribute {
48
49        PID, URL, CREATOR, EXPDATE;
50
51        public static Attribute fromString(String s) {
52            if (s.equalsIgnoreCase("pid")) {
53                return PID;
54            } else if (s.equalsIgnoreCase("url")) {
55                return URL;
56            } else if (s.equalsIgnoreCase("creator")) {
57                return CREATOR;
58            } else if (s.equalsIgnoreCase("expdate")) {
59                return EXPDATE;
60            }
61            return null;
62        }
63
64        @Override
65        public String toString() {
66            switch (this) {
67                case PID:
68                    return "pid";
69                case URL:
70                    return "url";
71                case CREATOR:
72                    return "creator";
73                case EXPDATE:
74                    return "expdate";
75                default:
76                    throw new InternalError();
77            }
78        }
79    } // private enum Attribute
80
81    public static final String USERNAME = "pid_provider.username";
82    public static final String PASSWORD = "pid_provider.password";
83    private static final String SERVICE_URI_BASE
84            = "http://handle.gwdg.de:8080/pidservice/";
85    private static final String USER_AGENT
86            = "CLARIN-VirtualCollectionRegisty/1.0";
87    private static final Logger logger
88            = LoggerFactory.getLogger(GWDGPersistentIdentifierProvider.class);
89    private String base_uri = null;
90    private String username = null;
91    private String password = null;
92    private XMLInputFactory factory;
93
94    @Autowired
95    public GWDGPersistentIdentifierProvider(ServletContext servletContext) throws VirtualCollectionRegistryException {
96        this(ServletUtils.createParameterMap(servletContext));
97    }
98
99    public GWDGPersistentIdentifierProvider(Map<String, String> config)
100            throws VirtualCollectionRegistryException {
101        super();
102        try {
103            String base_uri = getConfigParameter(config, BASE_URI);
104            if (!base_uri.endsWith("/")) {
105                base_uri = base_uri + "/";
106            }
107            URI uri = new URI(base_uri);
108            this.base_uri = uri.toString();
109        } catch (URISyntaxException e) {
110            throw new VirtualCollectionRegistryException("configuration "
111                    + "parameter \"" + BASE_URI + "\" is invalid", e);
112        }
113        this.username = getConfigParameter(config, USERNAME);
114        this.password = getConfigParameter(config, PASSWORD);
115
116        this.factory = XMLInputFactory.newInstance();
117        factory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
118        factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES,
119                Boolean.TRUE);
120    }
121
122    @Override
123    public PersistentIdentifier createIdentifier(VirtualCollection vc)
124            throws VirtualCollectionRegistryException {
125        if (vc == null) {
126            throw new NullPointerException("vc == null");
127        }
128        logger.debug("creating handle for virtual collection \"{}\"",
129                vc.getId());
130        try {
131            String target = makeCollectionURI(vc);
132            URI serviceURI = URI.create(SERVICE_URI_BASE + "write/create");
133
134            List<NameValuePair> params = new ArrayList<NameValuePair>();
135            params.add(new BasicNameValuePair("url", target));
136            Map<Attribute, String> props = invokeWebService(serviceURI, params);
137            String pid = props.get(Attribute.PID);
138            if (pid == null) {
139                throw new VirtualCollectionRegistryException(
140                        "no handle returned");
141            }
142            logger.info("created handle \"{}\" for virtual collection \"{}\"",
143                    pid, vc.getId());
144            return new PersistentIdentifier(vc, PersistentIdentifier.Type.HANDLE, pid);
145        } catch (VirtualCollectionRegistryException e) {
146            throw new RuntimeException("failed to create handle", e);
147        }
148    }
149
150    @Override
151    public void updateIdentifier(String pid, URI target)
152            throws VirtualCollectionRegistryException {
153        if (pid == null) {
154            throw new NullPointerException("pid == null");
155        }
156        if (pid.isEmpty()) {
157            throw new IllegalArgumentException("pid is empty");
158        }
159        if (target == null) {
160            throw new NullPointerException("target == null");
161        }
162        List<NameValuePair> params = new ArrayList<NameValuePair>();
163        params.add(new BasicNameValuePair("pid", pid));
164        params.add(new BasicNameValuePair("url", target.toString()));
165        URI serviceURI = URI.create(SERVICE_URI_BASE + "write/modify");
166        invokeWebService(serviceURI, params);
167        logger.info("updated handle \"{}\"", pid);
168    }
169
170    @Override
171    public void deleteIdentifier(String pid)
172            throws VirtualCollectionRegistryException {
173        if (pid == null) {
174            throw new NullPointerException("pid == null");
175        }
176        if (pid.isEmpty()) {
177            throw new IllegalArgumentException("pid is empty");
178        }
179        /*
180         * actually one cannot delete a handle, but we can set an expired date
181         * to mark it invalid
182         */
183        List<NameValuePair> params = new ArrayList<NameValuePair>();
184        params.add(new BasicNameValuePair("pid", pid));
185        params.add(new BasicNameValuePair("expdate", "1970-01-01"));
186        URI serviceURI = URI.create(SERVICE_URI_BASE + "write/modify");
187        invokeWebService(serviceURI, params);
188        logger.info("deleted/expired handle \"{}\"", pid);
189    }
190
191    private String makeCollectionURI(VirtualCollection vc) {
192        return base_uri + "service/clarin-virtualcollection/" + vc.getId();
193    }
194
195    private Map<Attribute, String> invokeWebService(URI serviceTargetURI,
196            List<NameValuePair> formparams)
197            throws VirtualCollectionRegistryException {
198        // force xml encoding
199        formparams.add(new BasicNameValuePair("encoding", "xml"));
200
201        DefaultHttpClient client = null;
202        try {
203            client = new DefaultHttpClient();
204            int port = serviceTargetURI.getPort() != -1 ? serviceTargetURI
205                    .getPort() : AuthScope.ANY_PORT;
206            client.getCredentialsProvider().setCredentials(
207                    new AuthScope(serviceTargetURI.getHost(), port),
208                    new UsernamePasswordCredentials(username, password));
209            // disable expect continue, GWDG does not like very well
210            client.getParams().setParameter(
211                    CoreProtocolPNames.USE_EXPECT_CONTINUE, Boolean.FALSE);
212            // set a proper user agent
213            client.getParams().setParameter(CoreProtocolPNames.USER_AGENT,
214                    USER_AGENT);
215            HttpPost request = new HttpPost(serviceTargetURI);
216            request.addHeader("Accept", "text/xml, application/xml");
217            request.setEntity(new UrlEncodedFormEntity(formparams, "UTF-8"));
218            HttpContext ctx = new BasicHttpContext();
219
220            logger.debug("invoking GWDG service at {}", serviceTargetURI);
221            HttpResponse response = client.execute(request, ctx);
222            StatusLine status = response.getStatusLine();
223            HttpEntity entity = response.getEntity();
224            Map<Attribute, String> props = Collections.emptyMap();
225
226            logger.debug("GWDG Service status: {}", status.toString());
227            if ((status.getStatusCode() >= 200)
228                    && (status.getStatusCode() <= 299) && (entity != null)) {
229                String encoding = EntityUtils.getContentCharSet(entity);
230                if (encoding == null) {
231                    encoding = "UTF-8";
232                }
233
234                XMLStreamReader reader = factory.createXMLStreamReader(entity
235                        .getContent(), encoding);
236                props = new HashMap<Attribute, String>();
237                while (reader.hasNext()) {
238                    reader.next();
239
240                    int type = reader.getEventType();
241                    if (type != XMLStreamConstants.START_ELEMENT) {
242                        continue;
243                    }
244                    Attribute attribute = Attribute.fromString(reader
245                            .getLocalName());
246                    if (attribute != null) {
247                        if (!reader.hasNext()) {
248                            throw new VirtualCollectionRegistryException(
249                                    "unexpected end of data stream");
250                        }
251                        reader.next();
252                        if (reader.getEventType() != XMLStreamConstants.CHARACTERS) {
253                            throw new VirtualCollectionRegistryException(
254                                    "unexpected element type: "
255                                    + reader.getEventType());
256                        }
257                        String value = reader.getText();
258                        if (value == null) {
259                            throw new VirtualCollectionRegistryException(
260                                    "element \"" + attribute + "\" was empty");
261                        }
262                        value = value.trim();
263                        if (!value.isEmpty()) {
264                            props.put(attribute, value);
265                        }
266                    }
267                }
268
269            } else {
270                logger.debug("GWDG Handle service failed: {}", status);
271                request.abort();
272                throw new VirtualCollectionRegistryException(
273                        "error invoking GWDG handle service");
274            }
275            return props;
276        } catch (VirtualCollectionRegistryException e) {
277            throw e;
278        } catch (Exception e) {
279            logger.debug("GWDG Handle service failed", e);
280            throw new VirtualCollectionRegistryException(
281                    "error invoking GWDG handle service", e);
282        } finally {
283            if (client != null) {
284                client.getConnectionManager().shutdown();
285            }
286        }
287    }
288
289    private static String getConfigParameter(Map<String, String> config,
290            String parameter) throws VirtualCollectionRegistryException {
291        String value = config.get(parameter);
292        if (value == null) {
293            throw new VirtualCollectionRegistryException("configuration "
294                    + "parameter \"" + parameter + "\" is not set");
295        }
296        value = value.trim();
297        if (value.isEmpty()) {
298            throw new VirtualCollectionRegistryException("configuration "
299                    + "parameter \"" + parameter + "\" is invalid");
300        }
301        return value;
302    }
303
304} // class GWDGPersistentIdentifierProvider
Note: See TracBrowser for help on using the repository browser.