Project

General

Profile

1
package eu.dnetlib.oai.core;
2

    
3
import java.util.ArrayList;
4
import java.util.Iterator;
5
import java.util.List;
6
import java.util.function.Function;
7

    
8
import javax.annotation.Resource;
9

    
10
import org.apache.commons.lang3.StringUtils;
11
import org.apache.commons.logging.Log;
12
import org.apache.commons.logging.LogFactory;
13

    
14
import eu.dnetlib.oai.BadResumptionTokenException;
15
import eu.dnetlib.oai.Cursor;
16
import eu.dnetlib.oai.NoRecordsMatchException;
17
import eu.dnetlib.oai.OAIError;
18
import eu.dnetlib.oai.PublisherStore;
19
import eu.dnetlib.oai.PublisherStoreDAO;
20
import eu.dnetlib.oai.conf.OAIConfigurationReader;
21
import eu.dnetlib.oai.info.ListDocumentsInfo;
22
import eu.dnetlib.oai.info.RecordInfo;
23
import eu.dnetlib.oai.info.ResumptionTokenImpl;
24
import eu.dnetlib.rmi.provision.MDFInfo;
25
import eu.dnetlib.rmi.provision.OaiPublisherException;
26
import eu.dnetlib.rmi.provision.OaiPublisherRuntimeException;
27

    
28
public class DNetOAICore extends AbstractOAICore {
29

    
30
	private static final Log log = LogFactory.getLog(DNetOAICore.class); // NOPMD by marko on 11/24/08 5:02 PM
31

    
32
	@Resource(name = "mongodbPublisherStoreDao")
33
	private PublisherStoreDAO<PublisherStore<Cursor>, Cursor> publisherStoreDAO;
34

    
35
	private String defaultDate = "2008-01-01T12:00:00Z";
36

    
37
	@Override
38
	protected RecordInfo getRecordById(final MDFInfo mdf, final String id) throws OaiPublisherException {
39
		final PublisherStore<Cursor> store = this.publisherStoreDAO.getStoreFor(mdf.getPrefix(), getCurrentDBName());
40
		if (store == null) { throw new OaiPublisherRuntimeException(
41
				"Missing store for metadata prefix " + mdf.getPrefix() + ". Please check OAI publisher configuration."); }
42
		RecordInfo record = null;
43
		if (StringUtils.isBlank(mdf.getTransformationRuleID())) {
44
			record = store.getRecord(id);
45
		} else {
46
			final Function<String, String> function = getLookupClient().getUnaryFunctionFromTDSRule(mdf.getTransformationRuleID());
47
			record = store.getRecord(id, function);
48
		}
49
		if (record != null) {
50
			record.setPrefix(mdf.getPrefix());
51
		}
52
		return record;
53
	}
54

    
55
	/**
56
	 * {@inheritDoc}
57
	 * <p>
58
	 * String, String)
59
	 */
60
	@Override
61
	protected ListDocumentsInfo getDocuments(final boolean onlyIdentifiers,
62
			final String set,
63
			final String metadataPrefix,
64
			final String from,
65
			final String until)
66
			throws OaiPublisherException {
67
		final MDFInfo mdf = obtainMDFInfo(metadataPrefix);
68
		final boolean hasDateRange = StringUtils.isNotBlank(from) || StringUtils.isNotBlank(until);
69
		final String query = this.generateQuery(mdf, set, from, until, hasDateRange);
70
		final int total = this.countTotal(hasDateRange, query, set, mdf);
71
		log.debug("Total number of records: " + total);
72
		final Cursor results = this.getCursor(query, onlyIdentifiers, mdf);
73
		final ListDocumentsInfo res = this.prepareListDocumentsInfo(results, mdf, query, set, 0, total, hasDateRange);
74
		log.debug("Delivering " + res.getDocs().size() + " in a page");
75
		return res;
76
	}
77

    
78
	@Override
79
	protected ListDocumentsInfo getDocuments(final boolean onlyIdentifiers, final String resumptionToken) throws OaiPublisherException {
80
		final ResumptionTokenImpl resToken = new ResumptionTokenImpl();
81
		resToken.deserialize(resumptionToken);
82

    
83
		log.debug(resToken.toString());
84

    
85
		final MDFInfo mdf = obtainMDFInfo(resToken.getMetadataPrefix());
86
		final String lastID = resToken.getLastObjIdentifier();
87
		final String query = resToken.getQuery();
88
		String newQuery = "";
89
		if (StringUtils.isNotBlank(query)) {
90
			newQuery = query + " AND ";
91
		}
92
		newQuery += " _id > \"" + lastID + "\"";
93
		log.debug("NEW QUERY BECAUSE of resumptionToken: " + newQuery);
94
		final int total = this.countTotal(resToken.hasDateRange(), query, resToken.getRequestedSet(), mdf);
95
		final Cursor results = this.getCursor(newQuery, onlyIdentifiers, mdf);
96
		final int oldCount = resToken.getnMaxElements();
97
		// if the number of records changed, then for sure we can invalidate the resumption token, unless we have a new total of -1 (date
98
		// range queries can't be counted for performance reasons)
99
		if ((total != -1) && (oldCount != total)) { throw new BadResumptionTokenException(resumptionToken); }
100

    
101
		final ListDocumentsInfo res =
102
				this.prepareListDocumentsInfo(results, mdf, query, resToken.getRequestedSet(), resToken.getnRead(), resToken.getnMaxElements(),
103
						resToken.hasDateRange());
104
		res.setCursor(resToken.getnRead());
105
		return res;
106
	}
107

    
108
	protected ListDocumentsInfo prepareListDocumentsInfo(final Cursor results,
109
			final MDFInfo mdf,
110
			final String query,
111
			final String requestedSet,
112
			final int read,
113
			final int totalNumber,
114
			final boolean hasDateRange) throws OaiPublisherException {
115
		final ListDocumentsInfo documentList = new ListDocumentsInfo();
116
		documentList.setnMaxElements(totalNumber);
117
		documentList.setMetadataPrefix(mdf.getPrefix());
118
		documentList.setCursor(0);
119
		if (documentList.getnMaxElements() == 0) { throw new NoRecordsMatchException(OAIError.noRecordsMatch.getMessage()); }
120

    
121
		final List<RecordInfo> theRecords = this.generateOAIRecords(mdf, requestedSet, results);
122
		documentList.setDocs(theRecords);
123

    
124
		if ((theRecords == null) || theRecords.isEmpty()) { throw new NoRecordsMatchException("noRecordsMatch: 'documents' is null or empty"); }
125

    
126
		if ((documentList.getnMaxElements() > (read + theRecords.size())) || (documentList.getnMaxElements() == -1)) {
127
			final String lastID = theRecords.get(theRecords.size() - 1).getInternalId();
128
			final ResumptionTokenImpl nextToken = new ResumptionTokenImpl();
129
			nextToken.setDateRange(hasDateRange);
130
			nextToken.setLastObjIdentifier(lastID);
131
			nextToken.setMetadataPrefix(mdf.getPrefix());
132
			nextToken.setnMaxElements(totalNumber);
133
			nextToken.setnRead(read + theRecords.size());
134
			nextToken.setQuery(query);
135
			nextToken.setRequestedSet(requestedSet);
136
			documentList.setResumptionToken(nextToken);
137
		}
138

    
139
		return documentList;
140
	}
141

    
142
	protected Cursor getCursor(final String query, final boolean onlyIdentifiers, final MDFInfo mdfInfo) {
143
		final PublisherStore<Cursor> store = this.publisherStoreDAO.getStore(mdfInfo.getSourceFormat(), mdfInfo.getSourceInterpretation(),
144
				mdfInfo.getSourceLayout(), getCurrentDBName());
145
		if (store == null) { throw new OaiPublisherRuntimeException(
146
				"Missing store for metadata prefix " + mdfInfo.getPrefix() + ". Please check OAI publisher configuration."); }
147
		Cursor results = null;
148
		if (StringUtils.isBlank(mdfInfo.getTransformationRuleID())) {
149
			results = store.getRecords(query, !onlyIdentifiers, this.pageSize);
150
		} else {
151
			final Function<String, String> function = getLookupClient().getUnaryFunctionFromTDSRule(mdfInfo.getTransformationRuleID());
152
			results = store.getRecords(query, function, !onlyIdentifiers, this.pageSize);
153
		}
154
		return results;
155
	}
156

    
157
	/**
158
	 * Generates the List of RecordInfo to be delivered.
159
	 *
160
	 * @param mdf
161
	 *            MDFInfo, the requested metadata format information.
162
	 * @param requestedSet
163
	 *            set specified in the request. It is blank if no set was requested.
164
	 * @param cursor
165
	 *            Cursor instance to use to get the records.
166
	 * @return List of RecordInfo instances
167
	 */
168
	protected List<RecordInfo> generateOAIRecords(final MDFInfo mdf, final String requestedSet, final Cursor cursor) {
169
		final List<RecordInfo> documents = new ArrayList<>();
170
		final Iterator<RecordInfo> cursorIterator = cursor.iterator();
171
		while (cursorIterator.hasNext()) {
172
			final RecordInfo current = cursorIterator.next();
173
			current.addSetspec(requestedSet);
174
			current.setPrefix(mdf.getPrefix());
175
			documents.add(current);
176
		}
177
		return documents;
178
	}
179

    
180
	protected String generateQuery(final MDFInfo mdf, final String set, final String from, final String until, final boolean hasDateRange) {
181
		final String datestampIndexName = OAIConfigurationReader.DATESTAMP_FIELD;
182

    
183
		String query = mdf.getBaseQuery();
184
		if (!StringUtils.isBlank(set)) {
185
			if (!StringUtils.isBlank(query)) {
186
				query += " AND ";
187
			}
188
			query += getSetCollection().getSetQuery(set, getCurrentDBName());
189
		}
190
		if (hasDateRange) {
191
			if (!StringUtils.isBlank(query)) {
192
				query += " AND ";
193
			}
194
			if ((from != null) && (until != null)) {
195
				query += datestampIndexName + " >= " + from + " AND " + datestampIndexName + " <= " + until;
196
			} else if (from != null) {
197
				query += datestampIndexName + " >= " + from;
198
			} else if (until != null) {
199
				query += datestampIndexName + " <= " + until;
200
			}
201
		}
202

    
203
		log.info("QUERY GENERATED: \n" + query);
204
		return query;
205
	}
206

    
207
	private int countTotal(final boolean hasDateRange, final String query, final String set, final MDFInfo mdFormat) {
208
		int total = 0;
209
		if (hasDateRange) {
210
			// Counting in the store by date ranges is too expensive and delays to much the response
211
			total = -1;
212
		} else {
213
			String theSet = set;
214
			if (StringUtils.isBlank(set)) {
215
				theSet = "ALL";
216
			}
217
			log.debug("SET::: " + theSet);
218
			total = getSetCollection().count(theSet, mdFormat.getPrefix(), getCurrentDBName());
219
		}
220
		return total;
221
	}
222

    
223
	public String getDefaultDate() {
224
		return this.defaultDate;
225
	}
226

    
227
	public void setDefaultDate(final String defaultDate) {
228
		this.defaultDate = defaultDate;
229
	}
230

    
231
	public PublisherStoreDAO<PublisherStore<Cursor>, Cursor> getPublisherStoreDAO() {
232
		return this.publisherStoreDAO;
233
	}
234

    
235
	public void setPublisherStoreDAO(final PublisherStoreDAO<PublisherStore<Cursor>, Cursor> publisherStoreDAO) {
236
		this.publisherStoreDAO = publisherStoreDAO;
237
	}
238

    
239
	public int getPageSize() {
240
		return this.pageSize;
241
	}
242

    
243
	public void setPageSize(final int pageSize) {
244
		this.pageSize = pageSize;
245
	}
246

    
247
}
(3-3/3)