Project

General

Profile

1
package eu.dnetlib.oai;
2

    
3
import java.text.SimpleDateFormat;
4
import java.util.Date;
5
import java.util.HashMap;
6
import java.util.Iterator;
7
import java.util.Map;
8
import java.util.Map.Entry;
9
import java.util.TimeZone;
10

    
11
import javax.servlet.http.HttpServletRequest;
12
import javax.servlet.http.HttpServletResponse;
13

    
14
import org.apache.commons.lang3.StringUtils;
15
import org.apache.commons.logging.Log;
16
import org.apache.commons.logging.LogFactory;
17
import org.springframework.beans.factory.annotation.Autowired;
18
import org.springframework.stereotype.Controller;
19
import org.springframework.ui.ModelMap;
20
import org.springframework.web.bind.annotation.ModelAttribute;
21
import org.springframework.web.bind.annotation.RequestMapping;
22

    
23
import eu.dnetlib.oai.core.AbstractOAICore;
24
import eu.dnetlib.oai.info.ListRecordsInfo;
25
import eu.dnetlib.oai.info.RecordInfo;
26
import eu.dnetlib.rmi.provision.OaiPublisherException;
27

    
28
/**
29
 * OAI Servlet.
30
 *
31
 * @author michele
32
 */
33
@Controller
34
public final class OAIController {
35

    
36
	/**
37
	 * logger.
38
	 */
39
	private static final Log log = LogFactory.getLog(OAIController.class); // NOPMD by marko on 11/24/08 5:02 PM
40
	/**
41
	 * Default content type.
42
	 */
43
	private static final String DEFAULT_CONTENT_TYPE = "text/xml;charset=utf-8";
44
	/**
45
	 * OAI servlet core. Most of the logic is here
46
	 */
47
	@Autowired
48
	private AbstractOAICore core;
49
	@Autowired
50
	private OAIProperties oaiProperties;
51

    
52
	@RequestMapping("/oai/clearCaches.do")
53
	public void clearOaiCaches() throws OaiPublisherException {
54
		this.core.getMdFormatsCache().removeAll();
55
		this.core.setCurrentDBFromIS();
56
		this.core.getLookupClient().evictCaches();
57
	}
58

    
59
	@RequestMapping("/oai/oai.do")
60
	public String oai(final ModelMap map, final HttpServletRequest request, final HttpServletResponse response) throws Exception {
61
		response.setContentType(OAIController.DEFAULT_CONTENT_TYPE);
62
		String theVerb = "";
63
		try {
64
			final Map<String, String> params = cleanParameters(request.getParameterMap());
65
			if (params == null) { return oaiError(OAIError.badArgument, map); }
66
			theVerb = params.get("verb");
67
			final OAI_VERBS requestedVerb = OAI_VERBS.getVerb(theVerb);
68
			switch (requestedVerb) {
69
			case IDENTIFY:
70
				return oaiIdentify(params, map);
71
			case LIST_METADATA_FORMATS:
72
				return oaiListMetadataFormats(params, map);
73
			case LIST_SETS:
74
				return oaiListSets(params, map);
75
			case GET_RECORD:
76
				return oaiGetRecord(params, map);
77
			case LIST_IDENTIFIERS:
78
				return oaiListIdentifiersOrRecords(params, map);
79
			case LIST_RECORDS:
80
				return oaiListIdentifiersOrRecords(params, map);
81
			default:
82
				return oaiError(OAIError.badVerb, map);
83
			}
84
		} catch (final CannotDisseminateFormatException e) {
85
			log.debug("ERROR", e);
86
			return oaiError(OAIError.cannotDisseminateFormat, theVerb, map);
87
		} catch (final NoRecordsMatchException e) {
88
			log.debug("ERROR", e);
89
			return oaiError(OAIError.noRecordsMatch, theVerb, map);
90
		} catch (final BadResumptionTokenException e) {
91
			log.debug("ERROR", e);
92
			return oaiError(OAIError.badResumptionToken, theVerb, map);
93
		} catch (final Exception e) {
94
			log.error("ERROR", e);
95
			return oaiError(e, map);
96
		}
97
	}
98

    
99
	private Map<String, String> cleanParameters(final Map<?, ?> startParams) {
100
		final HashMap<String, String> params = new HashMap<String, String>();
101
		final Iterator<?> iter = startParams.entrySet().iterator();
102
		while (iter.hasNext()) {
103
			final Entry<?, ?> entry = (Entry<?, ?>) iter.next();
104
			final String key = entry.getKey().toString();
105
			final String[] arr = (String[]) entry.getValue();
106
			if (arr.length == 0) { return null; }
107
			final String value = arr[0];
108
			if (key.equals("verb")) {
109
				params.put("verb", value);
110
			} else if (key.equals("from")) {
111
				params.put("from", value);
112
			} else if (key.equals("until")) {
113
				params.put("until", value);
114
			} else if (key.equals("metadataPrefix")) {
115
				params.put("metadataPrefix", value);
116
			} else if (key.equals("identifier")) {
117
				params.put("identifier", value);
118
			} else if (key.equals("set")) {
119
				params.put("set", value);
120
			} else if (key.equals("resumptionToken")) {
121
				params.put("resumptionToken", value);
122
			} else {
123
				return null;
124
			}
125
		}
126
		return params;
127
	}
128

    
129
	private String oaiIdentify(final Map<String, String> params, final ModelMap map) throws Exception {
130
		String verb = null;
131
		if (params.containsKey("verb")) {
132
			verb = params.get("verb");
133
			params.remove("verb");
134
		}
135
		if (params.entrySet().size() > 0) { return oaiError(OAIError.badArgument, verb, map); }
136
		return "oai/OAI_Identify";
137
	}
138

    
139
	private String oaiGetRecord(final Map<String, String> params, final ModelMap map) throws Exception {
140
		String verb = null;
141
		String prefix = null;
142
		String identifier = null;
143
		if (params.containsKey("verb")) {
144
			verb = params.get("verb");
145
			params.remove("verb");
146
		}
147
		if (params.containsKey("metadataPrefix")) {
148
			prefix = params.get("metadataPrefix");
149
			params.remove("metadataPrefix");
150
		} else {
151
			return oaiError(OAIError.badArgument, verb, map);
152
		}
153
		if (params.containsKey("identifier")) {
154
			identifier = params.get("identifier");
155
			params.remove("identifier");
156
		} else {
157
			return oaiError(OAIError.badArgument, verb, map);
158
		}
159
		if (params.entrySet().size() > 0) { return oaiError(OAIError.badArgument, verb, map); }
160
		this.core.setCurrentDBFromIS();
161
		final RecordInfo record = this.core.getInfoRecord(identifier, prefix);
162
		if (record == null) { return oaiError(OAIError.idDoesNotExist, map); }
163
		map.addAttribute("record", record);
164

    
165
		return "oai/OAI_GetRecord";
166
	}
167

    
168
	private String oaiListIdentifiersOrRecords(final Map<String, String> params, final ModelMap map) throws Exception {
169
		OAI_VERBS verb = null;
170
		String metadataPrefix = null;
171
		String resumptionToken = null;
172
		String set = null;
173
		String from = null;
174
		String until = null;
175

    
176
		if (params.containsKey("verb")) {
177
			verb = OAI_VERBS.getVerb(params.get("verb"));
178
			params.remove("verb");
179
		}
180

    
181
		if (params.containsKey("resumptionToken")) {
182
			resumptionToken = params.get("resumptionToken");
183
			params.remove("resumptionToken");
184
			this.core.setCurrentDBFromIS();
185
		} else {
186
			this.core.setCurrentDBFromIS();
187
			if (params.containsKey("metadataPrefix")) {
188
				metadataPrefix = params.get("metadataPrefix");
189
				params.remove("metadataPrefix");
190
			} else {
191
				return oaiError(OAIError.badArgument, verb.toString(), map);
192
			}
193
			if (params.containsKey("from")) {
194
				from = params.get("from");
195
				params.remove("from");
196
			}
197
			if (params.containsKey("until")) {
198
				until = params.get("until");
199
				params.remove("until");
200
			}
201
			if (params.containsKey("set")) {
202
				set = params.get("set");
203
				params.remove("set");
204
			}
205
		}
206
		if (params.entrySet().size() > 0) { return oaiError(OAIError.badArgument, verb.toString(), map); }
207
		if (StringUtils.isNotBlank(set) && !this.core.existSet(set)) { return oaiError(OAIError.badArgument, verb.toString(), map); }
208

    
209
		boolean onlyIdentifiers = true;
210
		if (verb == OAI_VERBS.LIST_RECORDS) {
211
			onlyIdentifiers = false;
212
		}
213

    
214
		ListRecordsInfo infos;
215

    
216
		if (StringUtils.isBlank(resumptionToken)) {
217
			infos = this.core.listRecords(onlyIdentifiers, metadataPrefix, set, from, until);
218
		} else {
219
			infos = this.core.listRecords(onlyIdentifiers, resumptionToken);
220
		}
221

    
222
		map.addAttribute("info", infos);
223

    
224
		if (verb == OAI_VERBS.LIST_RECORDS) { return "oai/OAI_ListRecords"; }
225

    
226
		return "oai/OAI_ListIdentifiers";
227
	}
228

    
229
	private String oaiListSets(final Map<String, String> params, final ModelMap map) throws Exception {
230
		String verb = null;
231
		if (params.containsKey("verb")) {
232
			verb = params.get("verb");
233
			params.remove("verb");
234
		}
235
		if (params.entrySet().size() > 0) { return oaiError(OAIError.badArgument, verb, map); }
236
		this.core.setCurrentDBFromIS();
237
		map.addAttribute("sets", this.core.listSets());
238
		return "oai/OAI_ListSets";
239
	}
240

    
241
	private String oaiListMetadataFormats(final Map<String, String> params, final ModelMap map) throws Exception {
242
		String id = null;
243
		String verb = null;
244
		if (params.containsKey("verb")) {
245
			verb = params.get("verb");
246
			params.remove("verb");
247
		}
248
		if (params.containsKey("identifier")) {
249
			id = params.get("identifier");
250
			params.remove("identifier");
251
		}
252
		if (params.entrySet().size() > 0) { return oaiError(OAIError.badArgument, verb, map); }
253
		this.core.setCurrentDBFromIS();
254
		map.addAttribute("formats", this.core.listMetadataFormats());
255
		if (id != null) {
256
			map.addAttribute("identifier", id);
257
			return "oai/OAI_ListMetadataFormats_withid";
258
		}
259
		return "oai/OAI_ListMetadataFormats";
260
	}
261

    
262
	private String oaiError(final OAIError errCode, final String verb, final ModelMap map) throws Exception {
263
		if (StringUtils.isBlank(verb)) { return oaiError(errCode, map); }
264
		map.addAttribute("verb", verb);
265
		map.addAttribute("errcode", errCode.name());
266
		map.addAttribute("errmsg", errCode.getMessage());
267
		return "oai/OAI_Error";
268
	}
269

    
270
	private String oaiError(final OAIError errCode, final ModelMap map) throws Exception {
271
		map.addAttribute("errcode", errCode.name());
272
		map.addAttribute("errmsg", errCode.getMessage());
273
		return "oai/OAI_Error_noverb";
274
	}
275

    
276
	private String oaiError(final Exception e, final ModelMap map) throws Exception {
277
		map.addAttribute("errcode", "InternalException");
278
		map.addAttribute("errmsg", e.getMessage());
279
		return "oai/OAI_Error_noverb";
280
	}
281

    
282
	/*
283
	 * Default Attribute
284
	 */
285
	@ModelAttribute("url")
286
	public String url(final HttpServletRequest request) {
287
		final String forwardedUrl = request.getHeader(getForwardedUrlHeaderName());
288
		final String baseURL = getBaseUrl();
289
		if (StringUtils.isNotBlank(baseURL)) {
290
			return baseURL;
291
		} else if (StringUtils.isNotBlank(forwardedUrl)) {
292
			return forwardedUrl;
293
		} else {
294
			return request.getRequestURL() + "";
295
		}
296
	}
297

    
298
	/**
299
	 * Model attribute for the date of response.
300
	 * <p>
301
	 * According to OAI-PMH protocol, it must be in UTC with format YYYY-MM-DDThh:mm:ssZ, where Z is the time zone ('Z' for "Zulu Time",
302
	 * i.e. UTC).
303
	 * </p>
304
	 * <p>
305
	 * See http://www.openarchives.org/OAI/openarchivesprotocol.html#Dates
306
	 * </p>
307
	 */
308
	@ModelAttribute("date")
309
	public String date() {
310
		final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
311
		formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
312
		final String date = formatter.format(new Date());
313
		return date.replace("+0000", "Z");
314
	}
315

    
316
	@ModelAttribute("repoName")
317
	public String getRepoName() {
318
		return this.oaiProperties.getRepoName();
319
	}
320

    
321
	@ModelAttribute("email")
322
	public String getRepoEmail() {
323
		return this.oaiProperties.getRepoEmail();
324
	}
325

    
326
	@ModelAttribute("earliestDatestamp")
327
	public String getEarliestDatestamp() {
328
		return this.oaiProperties.getEarliestDatestamp();
329
	}
330

    
331
	public String getBaseUrl() {
332
		return this.oaiProperties.getBaseUrl();
333
	}
334

    
335
	public String getForwardedUrlHeaderName() {
336
		return this.oaiProperties.getForwardedUrlHeaderName();
337
	}
338

    
339
	@ModelAttribute("deletedRecord")
340
	public String getDeletedRecordSupport() {
341
		return this.oaiProperties.getDeletedRecordSupport();
342
	}
343

    
344
	@ModelAttribute("granularity")
345
	public String getDateGranularity() {
346
		return this.oaiProperties.getDateGranularity();
347
	}
348

    
349
	public enum OAI_VERBS {
350
		IDENTIFY, LIST_IDENTIFIERS, LIST_RECORDS, LIST_METADATA_FORMATS, LIST_SETS, GET_RECORD, UNSUPPORTED_VERB;
351

    
352
		public static OAI_VERBS getVerb(final String theVerb) {
353
			if (StringUtils.isBlank(theVerb)) { return UNSUPPORTED_VERB; }
354
			if (theVerb.equalsIgnoreCase("Identify")) { return IDENTIFY; }
355
			if (theVerb.equalsIgnoreCase("ListIdentifiers")) { return LIST_IDENTIFIERS; }
356
			if (theVerb.equalsIgnoreCase("ListRecords")) { return LIST_RECORDS; }
357
			if (theVerb.equalsIgnoreCase("ListMetadataFormats")) { return LIST_METADATA_FORMATS; }
358
			if (theVerb.equalsIgnoreCase("ListSets")) { return LIST_SETS; }
359
			if (theVerb.equalsIgnoreCase("GetRecord")) { return GET_RECORD; }
360
			if (theVerb.equalsIgnoreCase("listidentifiers")) { return LIST_IDENTIFIERS; }
361
			return UNSUPPORTED_VERB;
362

    
363
		}
364
	}
365

    
366
	public enum DELETED_SUPPORT {
367
		NO, TRANSIENT, PERSISTENT;
368

    
369
		@Override
370
		public String toString() {
371
			switch (this) {
372
			case TRANSIENT:
373
				return "transient";
374
			case PERSISTENT:
375
				return "persistent";
376
			default:
377
				return "no";
378
			}
379
		}
380

    
381
	}
382
}
(6-6/13)