Project

General

Profile

1
package eu.dnetlib.openaire.common;
2

    
3
import java.io.IOException;
4
import java.nio.charset.Charset;
5
import java.util.List;
6
import java.util.Map;
7
import java.util.Queue;
8
import java.util.concurrent.LinkedBlockingQueue;
9
import java.util.function.Function;
10
import java.util.stream.Collectors;
11

    
12
import com.google.common.collect.Lists;
13
import com.google.common.escape.Escaper;
14
import com.google.common.xml.XmlEscapers;
15
import eu.dnetlib.OpenaireExporterConfig;
16
import eu.dnetlib.enabling.datasources.common.DsmException;
17
import eu.dnetlib.enabling.datasources.common.DsmForbiddenException;
18
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
19
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpService;
20
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryException;
21
import eu.dnetlib.enabling.is.registry.rmi.ISRegistryService;
22
import eu.dnetlib.openaire.dsm.dao.utils.IndexDsInfo;
23
import eu.dnetlib.openaire.context.Context;
24
import eu.dnetlib.openaire.context.ContextMappingUtils;
25
import eu.dnetlib.openaire.dsm.domain.ApiDetails;
26
import eu.dnetlib.openaire.dsm.domain.DatasourceDetails;
27
import org.apache.commons.io.IOUtils;
28
import org.apache.commons.lang3.StringUtils;
29
import org.apache.commons.logging.Log;
30
import org.apache.commons.logging.LogFactory;
31
import org.apache.http.HttpStatus;
32
import org.springframework.beans.factory.annotation.Autowired;
33
import org.springframework.cache.annotation.CacheEvict;
34
import org.springframework.cache.annotation.Cacheable;
35
import org.springframework.core.io.ClassPathResource;
36
import org.springframework.stereotype.Component;
37

    
38
import static eu.dnetlib.openaire.dsm.dao.utils.DsmMappingUtils.asRepositoryInterfce;
39
import static eu.dnetlib.openaire.dsm.dao.utils.DsmMappingUtils.asRepositoryProfile;
40
import static eu.dnetlib.openaire.common.Utils.escape;
41

    
42
/**
43
 * Created by claudio on 20/10/2016.
44
 */
45
@Component
46
public class ISClientImpl implements ISClient {
47

    
48
 	private static final Log log = LogFactory.getLog(ISClientImpl.class);
49

    
50
	@Autowired
51
	private OpenaireExporterConfig config;
52

    
53
	@Autowired
54
	private ISLookUpService isLookUpService;
55

    
56
	@Autowired
57
	private ISRegistryService isRegistryService;
58

    
59
	@Autowired
60
	private OperationManager operationManager;
61

    
62
	@Override
63
	@Cacheable("indexdsinfo-cache")
64
	public IndexDsInfo calculateCurrentIndexDsInfo() throws DsmException {
65
		log.warn("calculateCurrentIndexDsInfo(): not using cache");
66
		final String[] arr;
67
		try {
68
			arr = _isLookUp(_getQuery(config.getFindIndexDsInfo())).split("@@@");
69
			return new IndexDsInfo(
70
				_isLookUp(_getQuery(config.getFindSolrIndexUrl())),
71
				arr[0].trim(), arr[1].trim(), arr[2].trim());
72
		} catch (IOException | ISLookUpException e) {
73
			throw new DsmException("unable fetch index DS information from IS");
74
		}
75
	}
76

    
77
	@Override
78
	@Cacheable("objectstoreid-cache")
79
	public String getObjectStoreId(final String dsId, final Queue<Throwable> errors) throws IOException {
80
		log.warn(String.format("getObjectStoreId(%s): not using cache", dsId));
81
		final String xqueryTemplate = _getQuery(config.getFindObjectStore());
82
		try {
83
			return _isLookUp(String.format(xqueryTemplate, dsId));
84
		} catch (ISLookUpException e) {
85
			errors.add(new DsmException("unable to find objectStore for datasource " + dsId));
86
			return "";
87
		}
88
	}
89

    
90
	@Override
91
	@Cacheable("context-cache")
92
	public Map<String, Context> getFunderContextMap() throws IOException {
93
		return _processContext(
94
				new LinkedBlockingQueue<>(),
95
				_getQuery(config.getFindFunderContexts()));
96
	}
97

    
98
	@Override
99
	@Cacheable("context-cache")
100
	public Map<String, Context> getCommunityContextMap() throws IOException {
101
		return _processContext(
102
				new LinkedBlockingQueue<>(),
103
				_getQuery(config.getFindCommunityContexts()));
104
	}
105

    
106
	@Override
107
	@CacheEvict(value = "context-cache", allEntries = true)
108
	public void updateContextParam(final String id, final String name, final String value) {
109
		final Queue<Throwable> errors = Lists.newLinkedList();
110
		final Escaper esc = XmlEscapers.xmlContentEscaper();
111
		_quickSeachProfile(String.format(
112
				"update value collection('/db/DRIVER/ContextDSResources/ContextDSResourceType')" +
113
				"/RESOURCE_PROFILE/BODY/CONFIGURATION/context[./@id = '%s']/param[./@name = '%s']/text() with '%s'", id, name, escape(esc, value)),
114
				errors);
115

    
116
		if (!errors.isEmpty()) {
117
			errors.forEach(e -> log.error(e));
118
		}
119
	}
120

    
121
	@Override
122
	@CacheEvict(value = "context-cache", allEntries = true)
123
	public void updateContextAttribute(final String id, final String name, final String value) {
124
		final Queue<Throwable> errors = Lists.newLinkedList();
125
		final Escaper esc = XmlEscapers.xmlAttributeEscaper();
126
		_quickSeachProfile(String.format(
127
				"update value collection('/db/DRIVER/ContextDSResources/ContextDSResourceType')" +
128
						"/RESOURCE_PROFILE/BODY/CONFIGURATION/context[./@id = '%s']/@%s with '%s'", id, name, escape(esc, value)),
129
				errors);
130

    
131
		if (!errors.isEmpty()) {
132
			errors.forEach(e -> log.error(e));
133
		}
134
	}
135

    
136
	@Override
137
	@CacheEvict(value = "context-cache", allEntries = true)
138
	public void addConcept(final String id, final String categoryId, final String data) {
139
		final Queue<Throwable> errors = Lists.newLinkedList();
140
		_quickSeachProfile(String.format(
141
				"update insert %s into collection('/db/DRIVER/ContextDSResources/ContextDSResourceType')" +
142
				"/RESOURCE_PROFILE/BODY/CONFIGURATION/context[./@id = '%s']/category[./@id = '%s']", data, id, categoryId), errors);
143

    
144
		if (!errors.isEmpty()) {
145
			errors.forEach(e -> log.error(e));
146
		}
147
	}
148

    
149
	@Override
150
	@CacheEvict(value = "context-cache", allEntries = true)
151
	public void removeConcept(final String id, final String categoryId, final String conceptId) {
152
		final Queue<Throwable> errors = Lists.newLinkedList();
153
		_quickSeachProfile(String.format(
154
				"for $concept in collection('/db/DRIVER/ContextDSResources/ContextDSResourceType')" +
155
						"/RESOURCE_PROFILE/BODY/CONFIGURATION/context[./@id = '%s']" +
156
						"/category[./@id = '%s']/concept[./@id = '%s']" +
157
						"return update delete $concept", id, categoryId, conceptId),
158
				errors);
159
		if (!errors.isEmpty()) {
160
			errors.forEach(e -> log.error(e.getMessage()));
161
		}
162
	}
163

    
164
	@Override
165
	public void updateDatasourceFields(final String dsId, final Map<String, String> changes) {
166
		final Queue<Throwable> errors = Lists.newLinkedList();
167
		operationManager.addOperation(() -> {
168
			Thread.currentThread().setName("update-ds:" + dsId);
169
			changes.forEach((xpath, value) -> {
170
				try {
171
					_isLookUp(String.format(
172
							"for $x in collection('/db/DRIVER/RepositoryServiceResources/RepositoryServiceResourceType')\n" +
173
							"where $x/RESOURCE_PROFILE/BODY/CONFIGURATION/DATASOURCE_ORIGINAL_ID[@provenance='OPENAIRE']/text() = '%s'\n" +
174
							"return update value $x%s with '%s'", dsId, xpath, value));
175
				} catch (ISLookUpException e) {
176
					errors.add(e);
177
				}
178
			});
179
		});
180

    
181
		if (!errors.isEmpty()) {
182
			errors.forEach(e -> log.error(e.getMessage()));
183
		}
184
	}
185

    
186
	@Override
187
	public void updateAPIField(final String dsId, final String apiId, final Map<String, String> changes) {
188
		final Queue<Throwable> errors = Lists.newLinkedList();
189
		operationManager.addOperation(() -> {
190
			Thread.currentThread().setName("update-api:" + dsId);
191
			changes.forEach((xpath, value) -> {
192
				try {
193
					_isLookUp(String.format(
194
							"let $x:=/RESOURCE_PROFILE/BODY/CONFIGURATION/DATASOURCE_ORIGINAL_ID[@provenance='OPENAIRE' and ./text() = '%s']\n" +
195
							"return update value $x/..//INTERFACE[./@id = '%s']%s with '%s'",
196
							dsId, apiId, xpath, value));
197
				} catch (ISLookUpException e) {
198
					errors.add(e);
199
				}
200
			});
201
		});
202

    
203
		if (!errors.isEmpty()) {
204
			errors.forEach(e -> log.error(e.getMessage()));
205
		}
206
	}
207

    
208
	@Override
209
	public void registerDS(final DatasourceDetails d) {
210
		final Queue<Throwable> errors = Lists.newLinkedList();
211
		operationManager.addOperation(() -> {
212
			Thread.currentThread().setName("save-ds:" + d.getId());
213
			try {
214
				final String id = isRegistryService.registerProfile(asRepositoryProfile(d));
215
				log.debug(String.format("registered DS profile %s", id));
216
			} catch (ISRegistryException e) {
217
				errors.add(e);
218
			}
219
		});
220
		if (!errors.isEmpty()) {
221
			errors.forEach(e -> log.error(e.getMessage()));
222
		}
223
	}
224

    
225
	@Override
226
	public void registerAPI(final ApiDetails api) {
227
		final Queue<Throwable> errors = Lists.newLinkedList();
228
		operationManager.addOperation(() -> {
229
			Thread.currentThread().setName("save-api:" + api.getId());
230
			try {
231
				final String dsId = api.getDatasource();
232
				final String iface = asRepositoryInterfce(api);
233
				_isLookUp(String.format(
234
						"let $x:=/RESOURCE_PROFILE/BODY/CONFIGURATION/DATASOURCE_ORIGINAL_ID[@provenance='OPENAIRE' and ./text() = '%s']\n" +
235
						"return update insert %s into $x/../INTERFACES", dsId, iface));
236

    
237
				log.debug(String.format("registered API %s", api.getId()));
238
			} catch (ISLookUpException e) {
239
				errors.add(e);
240
			}
241
		});
242
		if (!errors.isEmpty()) {
243
			errors.forEach(e -> log.error(e.getMessage()));
244
		}
245
	}
246

    
247
	@Override
248
	public void removeAPI(final String apiId) throws DsmForbiddenException {
249
		final Queue<Throwable> errors = Lists.newLinkedList();
250

    
251
		try {
252
			final List<String> metaWorkflows = _quickSeachProfile(String.format(
253
					"distinct-values(for $x in collection('/db/DRIVER/MetaWorkflowDSResources/MetaWorkflowDSResourceType')\n" +
254
					"where $x/RESOURCE_PROFILE/BODY/DATAPROVIDER[./@interface = '%s']\n" +
255
					"return $x/RESOURCE_PROFILE/BODY/DATAPROVIDER/@id/string())", apiId),
256
					errors);
257
			if (!metaWorkflows.isEmpty()) {
258
				throw new DsmForbiddenException(
259
						HttpStatus.SC_FORBIDDEN,
260
						String.format("cannot remove api '%s', it has workflows associated", apiId));
261
			}
262
			_isLookUp(String.format(
263
					"update delete /RESOURCE_PROFILE/BODY/CONFIGURATION/INTERFACES/INTERFACE[./@id = '%s']", apiId));
264

    
265
			log.debug(String.format("deleted API %s", apiId));
266
		} catch (ISLookUpException e) {
267
			errors.add(e);
268
		}
269

    
270
		if (!errors.isEmpty()) {
271
			errors.forEach(e -> log.error(e.getMessage()));
272
		}
273
	}
274

    
275
	/// HELPERS
276

    
277
	private Map<String, Context> _processContext(final Queue<Throwable> errors, final String xquery) throws IOException {
278
		try {
279
			return getContextProfiles(errors, xquery).stream()
280
					.filter(StringUtils::isNotBlank)
281
					.map(s -> ContextMappingUtils.parseContext(s, errors))
282
					.collect(Collectors.toMap(
283
							Context::getId,
284
							Function.identity()));
285
		} finally {
286
			if (!errors.isEmpty()) {
287
				log.error(errors);
288
				errors.forEach(e -> e.printStackTrace());
289
			}
290
		}
291
	}
292

    
293
	private List<String> getContextProfiles(final Queue<Throwable> errors, final String xquery) throws IOException {
294
		log.warn("getContextProfiles(): not using cache");
295
		return _quickSeachProfile(xquery, errors);
296
	}
297

    
298
	private String _getQuery(final ClassPathResource resource) throws IOException {
299
		return IOUtils.toString(resource.getInputStream(), Charset.defaultCharset());
300
	}
301

    
302
	private String _isLookUp(final String xquery) throws ISLookUpException {
303
		log.debug(String.format("running xquery:\n%s", xquery));
304
		final String res = isLookUpService.getResourceProfileByQuery(xquery);
305
		//log.debug(String.format("query result: %s", res));
306
		return res;
307
	}
308

    
309
	private List<String> _quickSeachProfile(final String xquery, final Queue<Throwable> errors) {
310
		final List<String> res = Lists.newArrayList();
311
		try {
312
			log.debug(String.format("running xquery:\n%s", xquery));
313
			final List<String> list = isLookUpService.quickSearchProfile(xquery);
314
			if (list != null) {
315
				res.addAll(list);
316
			}
317
			log.debug(String.format("query result size: %s", res.size()));
318
		} catch (Throwable e) {
319
			errors.add(e);
320
			return Lists.newArrayList();
321
		} finally {
322
			return res;
323
		}
324
	}
325

    
326
	@CacheEvict(cacheNames = { "context-cache", "indexdsinfo-cache", "objectstoreid-cache" }, allEntries = true)
327
	public void dropCache() {
328
		log.info("dropped dsManager IS cache");
329
	}
330

    
331
}
(6-6/9)