1
|
package eu.dnetlib.parthenos.catalogue;
|
2
|
|
3
|
import java.io.BufferedOutputStream;
|
4
|
import java.io.ByteArrayOutputStream;
|
5
|
import java.io.IOException;
|
6
|
import java.net.URISyntaxException;
|
7
|
import java.util.Iterator;
|
8
|
|
9
|
import com.fasterxml.jackson.core.JsonEncoding;
|
10
|
import com.fasterxml.jackson.core.JsonFactory;
|
11
|
import com.fasterxml.jackson.core.JsonGenerator;
|
12
|
import com.google.common.base.Joiner;
|
13
|
import eu.dnetlib.parthenos.jrr.ParthenosRegistryResource;
|
14
|
import eu.dnetlib.parthenos.publisher.ParthenosPublisherException;
|
15
|
import eu.dnetlib.parthenos.rdf.ResourceReader;
|
16
|
import org.apache.commons.lang3.StringUtils;
|
17
|
import org.apache.commons.logging.Log;
|
18
|
import org.apache.commons.logging.LogFactory;
|
19
|
import org.apache.jena.rdf.model.Resource;
|
20
|
import org.springframework.beans.factory.annotation.Autowired;
|
21
|
import org.springframework.stereotype.Component;
|
22
|
|
23
|
import static eu.dnetlib.parthenos.CRM.*;
|
24
|
|
25
|
/**
|
26
|
* Created by Alessia Bardi on 21/11/2017.
|
27
|
*
|
28
|
* @author Alessia Bardi
|
29
|
*/
|
30
|
@Component
|
31
|
public class CatalogueRegistrator {
|
32
|
|
33
|
private static final Log log = LogFactory.getLog(CatalogueRegistrator.class);
|
34
|
private final String PARTHENOS_BASE_URL = "http://parthenos.d4science.org";
|
35
|
|
36
|
@Autowired
|
37
|
private ResourceReader resourceReader;
|
38
|
|
39
|
@Autowired
|
40
|
private CatalogueAPIClient catalogueAPIClient;
|
41
|
|
42
|
private String groupTemplate = "{"
|
43
|
+ " \"name\":\"%s\","
|
44
|
+ " \"id\":\"%s\","
|
45
|
+ " \"title\": \"%s\""
|
46
|
//+ " \"description\": \"%s\""
|
47
|
+ "}";
|
48
|
|
49
|
|
50
|
|
51
|
public String register(final Resource resource, final Resource type) throws IOException, ParthenosPublisherException, URISyntaxException {
|
52
|
String resURI = resource.getURI();
|
53
|
log.debug(String.format("Catalogue --> Processing resource : %s with type: %s", resURI, type.getLocalName()));
|
54
|
//For the catalogue: Must be purely lowercase alphanumeric (ascii) characters and these symbols: -_
|
55
|
String resCatName = resURI.substring(resURI.lastIndexOf("/") + 1).toLowerCase();
|
56
|
ParthenosRegistryResource prr = catalogueAPIClient.getRegistered(resCatName);
|
57
|
if(prr != null){
|
58
|
//TODO: what to do if already registered?
|
59
|
return prr.getUuid();
|
60
|
}
|
61
|
else {
|
62
|
//resource not yet registered
|
63
|
ensureGroups(resource);
|
64
|
String json;
|
65
|
switch (type.getLocalName()) {
|
66
|
case "E7_Activity":
|
67
|
json = getJsonForActivity(resource, resCatName);
|
68
|
break;
|
69
|
case "E39_Actor":
|
70
|
json = getJsonForActor(resource, resCatName);
|
71
|
break;
|
72
|
case "E70_Thing":
|
73
|
json = getJsonForThing(resource, resCatName);
|
74
|
break;
|
75
|
//TODO: handle more types, see JRRPublisher
|
76
|
//CRM.E55_Type
|
77
|
//CRM.E29_Design_or_Procedure
|
78
|
default:
|
79
|
throw new IllegalArgumentException(String.format("Type " + type.getLocalName() + " not supported"));
|
80
|
}
|
81
|
String uuid = catalogueAPIClient.doRegister(json, resCatName);
|
82
|
log.debug(String.format("%s registered on the catalogue with uuid: %s", resURI, uuid));
|
83
|
return uuid;
|
84
|
}
|
85
|
|
86
|
}
|
87
|
|
88
|
protected boolean purge(final String resCatName) throws URISyntaxException, IOException {
|
89
|
return catalogueAPIClient.purgeItem(resCatName);
|
90
|
}
|
91
|
|
92
|
/**
|
93
|
* Ensure that providers referred in the Resource are available as "groups" in the registry.
|
94
|
* @param res Resource
|
95
|
*/
|
96
|
protected void ensureGroups(final Resource res) throws ParthenosPublisherException, IOException, URISyntaxException {
|
97
|
log.debug("Ensuring groups exist");
|
98
|
Iterator<String> providerNames = resourceReader.getProviderNames(res);
|
99
|
while(providerNames.hasNext()){
|
100
|
String name = providerNames.next();
|
101
|
if(StringUtils.isNotBlank(name)){
|
102
|
// the name of the group, a string between 2 and 100 characters long, containing only lowercase alphanumeric characters, - and _
|
103
|
String groupName = name.replaceAll(" ","_").toLowerCase();
|
104
|
if(groupName.length() > 100){
|
105
|
groupName = groupName.substring(0,99);
|
106
|
}
|
107
|
if(!catalogueAPIClient.groupExist(groupName)){
|
108
|
String groupJson = String.format(groupTemplate, groupName, groupName, name);
|
109
|
catalogueAPIClient.registerGroup(groupJson, groupName);
|
110
|
}
|
111
|
}
|
112
|
}
|
113
|
}
|
114
|
|
115
|
protected String getJsonForActivity(final Resource res, final String resNameForCatalogue) throws IOException {
|
116
|
JsonFactory jsonFactory = new JsonFactory();
|
117
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
118
|
BufferedOutputStream bos = new BufferedOutputStream(out);
|
119
|
JsonGenerator jg = jsonFactory.createGenerator(bos, JsonEncoding.UTF8);
|
120
|
jg.writeStartObject();
|
121
|
writeCommonFields(jg, res, resNameForCatalogue);
|
122
|
//TODO: from which linked E39_Actor?
|
123
|
//for project we can take it from the maintainer team: is this correct also for other Things (like Services and Software?)
|
124
|
jg.writeStringField("maintainer", Joiner.on(", ").join(resourceReader.getMaintainerUrls(res)));
|
125
|
//TODO: it should be better to identify email contacts rather than generic contact labels of maintainer
|
126
|
//jg.writeStringField("maintainer_email", Joiner.on(", ").join(resourceReader.getMaintainerContacts(res)));
|
127
|
|
128
|
jg.writeArrayFieldStart("extras");
|
129
|
addExtra(jg, "system:type", E7_Activity.getLocalName());
|
130
|
//specific class
|
131
|
addExtra(jg, "instance of", resourceReader.findSpecificType(res, E7_Activity).getLocalName());
|
132
|
if (res.getURI().startsWith(PARTHENOS_BASE_URL))
|
133
|
addExtra(jg, "Parthenos URL", res.getURI());
|
134
|
//for Services
|
135
|
addExtra(jg, "competence", resourceReader.getCompetence(res));
|
136
|
addExtra(jg, "provided by", Joiner.on(", ").join(resourceReader.getProviderNames(res)));
|
137
|
addExtra(jg, "declared begin/end of operation", Joiner.on(", ").join(resourceReader.getDeclarativeTimes(res)));
|
138
|
//TODO: where to get it?
|
139
|
addExtra(jg, "last confirmation", "");
|
140
|
//TODO: where to get it?
|
141
|
addExtra(jg, "date of registration", "");
|
142
|
addExtra(jg, "availability", resourceReader.getAvailability(res));
|
143
|
addExtra(jg, "condition of use", resourceReader.getConditionOfUse(res));
|
144
|
addExtra(jg, "contact points", Joiner.on(", ").join(resourceReader.getProviderContactPoints(res)));
|
145
|
addExtra(jg, "activity type", Joiner.on(", ").join(resourceReader.getActivityTypes(res)));
|
146
|
// can't add multiple fields with same name, hence skipping
|
147
|
addExtra(jg, "hosts", Joiner.on(", ").join(resourceReader.getHostedStuff(res)));
|
148
|
addExtra(jg, "online access point", Joiner.on(", ").join(resourceReader.getAccessPoints(res)));
|
149
|
//TODO: where to get it?
|
150
|
addExtra(jg, "authorization", "");
|
151
|
addExtra(jg, "protocol", Joiner.on(", ").join(resourceReader.getProtocols(res)));
|
152
|
addExtra(jg,"curates", Joiner.on(", ").join(resourceReader.getCuratedObjects(res)));
|
153
|
//TODO: where to get it?
|
154
|
addExtra(jg, "runs on request", "");
|
155
|
addExtra(jg, "delivers on request", Joiner.on(", ").join(resourceReader.getDeliversOnRequest(res)));
|
156
|
addExtra(jg, "uses curation plan", Joiner.on(", ").join(resourceReader.getCurationPlans(res)));
|
157
|
|
158
|
//for Projects
|
159
|
addExtra(jg, "offers", Joiner.on(", ").join(resourceReader.getOfferedServiceUrls(res)));
|
160
|
addExtra(jg, "started", Joiner.on(", ").join(resourceReader.getStartTimes(res)));
|
161
|
|
162
|
jg.writeEndArray(); //end extras
|
163
|
|
164
|
jg.writeEndObject();
|
165
|
jg.close();
|
166
|
return out.toString("UTF-8");
|
167
|
}
|
168
|
|
169
|
protected String getJsonForActor(final Resource res, final String resNameForCatalogue) throws IOException {
|
170
|
JsonFactory jsonFactory = new JsonFactory();
|
171
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
172
|
BufferedOutputStream bos = new BufferedOutputStream(out);
|
173
|
JsonGenerator jg = jsonFactory.createGenerator(bos, JsonEncoding.UTF8);
|
174
|
jg.writeStartObject();
|
175
|
writeCommonFields(jg, res, resNameForCatalogue);
|
176
|
|
177
|
jg.writeArrayFieldStart("extras");
|
178
|
addExtra(jg, "system:type", E39_Actor.getLocalName());
|
179
|
//specific class
|
180
|
addExtra(jg, "instance of", resourceReader.findSpecificType(res, E39_Actor).getLocalName());
|
181
|
if (res.getURI().startsWith(PARTHENOS_BASE_URL))
|
182
|
addExtra(jg, "Parthenos URL", res.getURI());
|
183
|
addExtra(jg, "has member", Joiner.on(", ").join(resourceReader.getMemberUrls(res)));
|
184
|
addExtra(jg, "is member of", Joiner.on(", ").join(resourceReader.isMemberOf(res)));
|
185
|
addExtra(jg, "has contact point", Joiner.on(", ").join(resourceReader.getResourceDirectContactPoints(res)));
|
186
|
addExtra(jg, "provides", Joiner.on(", ").join(resourceReader.getProvidedServiceUrls(res)));
|
187
|
jg.writeEndArray();
|
188
|
|
189
|
jg.writeEndObject();
|
190
|
jg.close();
|
191
|
return out.toString("UTF-8");
|
192
|
}
|
193
|
|
194
|
protected String getJsonForThing(final Resource res, final String resNameForCatalogue) throws IOException {
|
195
|
JsonFactory jsonFactory = new JsonFactory();
|
196
|
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
197
|
BufferedOutputStream bos = new BufferedOutputStream(out);
|
198
|
JsonGenerator jg = jsonFactory.createGenerator(bos, JsonEncoding.UTF8);
|
199
|
jg.writeStartObject();
|
200
|
writeCommonFields(jg, res, resNameForCatalogue);
|
201
|
|
202
|
jg.writeArrayFieldStart("extras");
|
203
|
addExtra(jg, "system:type", E70_Thing.getLocalName());
|
204
|
//specific class
|
205
|
addExtra(jg, "instance of", resourceReader.findSpecificType(res, E70_Thing).getLocalName());
|
206
|
if (res.getURI().startsWith(PARTHENOS_BASE_URL)) {
|
207
|
addExtra(jg, "Parthenos URL", res.getURI());
|
208
|
}
|
209
|
//TODO: things include digital objects, software, datasets, schema. Guess we should know what to add here.
|
210
|
addExtra(jg, "is part of", Joiner.on(", ").join(resourceReader.getIsPartOfUrls(res)));
|
211
|
addExtra(jg, "has part", Joiner.on(", ").join(resourceReader.getHasPartUrls(res)));
|
212
|
addExtra(jg, "curated by", Joiner.on(", ").join(resourceReader.getCuratorUrls(res)));
|
213
|
addExtra(jg, "curation plan", Joiner.on(", ").join(resourceReader.getCurationPlans(res)));
|
214
|
addExtra(jg, "hosted by", Joiner.on(", ").join(resourceReader.getHostedBys(res)));
|
215
|
//TODO where to get the encoding types? Should we through the Creation event?
|
216
|
//addExtra(jg, "encoding type", Joiner.on(", ").join());
|
217
|
//TODO where to get the schema/formats? Should we through the Creation event?
|
218
|
//addExtra(jg, "encoding type", Joiner.on(", ").join());
|
219
|
//TODO where to get the creator? Should we through the Creation event?
|
220
|
//addExtra(jg, "creator", Joiner.on(", ").join());
|
221
|
addExtra(jg, "subject", Joiner.on(", ").join(resourceReader.getSubjects(res)));
|
222
|
addExtra(jg, "temporal coverage", Joiner.on(", ").join(resourceReader.getTemporalCoverages(res)));
|
223
|
addExtra(jg, "spatial coverage", Joiner.on(", ").join(resourceReader.getSpatialCoverages(res)));
|
224
|
|
225
|
|
226
|
jg.writeEndArray();
|
227
|
|
228
|
jg.writeEndObject();
|
229
|
jg.close();
|
230
|
return out.toString("UTF-8");
|
231
|
}
|
232
|
|
233
|
protected void addExtra(final JsonGenerator jg, final String key, final String value) throws IOException {
|
234
|
jg.writeStartObject();
|
235
|
jg.writeStringField("key", key);
|
236
|
jg.writeStringField("value", value);
|
237
|
jg.writeEndObject();
|
238
|
}
|
239
|
|
240
|
protected void writeCommonFields(final JsonGenerator jg, final Resource res, final String resNameForCatalogue) throws IOException {
|
241
|
//end of URI
|
242
|
jg.writeStringField("name", resNameForCatalogue);
|
243
|
//default license
|
244
|
jg.writeStringField("license_id", "notspecified");
|
245
|
String title = resourceReader.getTitle(res);
|
246
|
if (StringUtils.isBlank(title))
|
247
|
title = resNameForCatalogue;
|
248
|
jg.writeStringField("title", title);
|
249
|
//description
|
250
|
jg.writeStringField("notes", resourceReader.getDescription(res));
|
251
|
//the names of all superclasses of the entity
|
252
|
jg.writeArrayFieldStart("tags");
|
253
|
Iterator<String> classNames = resourceReader.getRDFClassNames(res);
|
254
|
while (classNames.hasNext()) {
|
255
|
jg.writeStartObject();
|
256
|
jg.writeStringField("name", classNames.next());
|
257
|
jg.writeEndObject();
|
258
|
}
|
259
|
jg.writeEndArray();
|
260
|
//RI from which the entity has been collected, the source from which the RI collected the entity (if available).
|
261
|
//TODO: other Actors to add as catalogue group?
|
262
|
Iterator<String> providers = resourceReader.getProviderNames(res);
|
263
|
jg.writeArrayFieldStart("groups");
|
264
|
while(providers.hasNext()){
|
265
|
String provider = providers.next();
|
266
|
jg.writeStartObject();
|
267
|
jg.writeStringField("name", provider);
|
268
|
jg.writeEndObject();
|
269
|
}
|
270
|
jg.writeEndArray();
|
271
|
}
|
272
|
|
273
|
|
274
|
public ResourceReader getResourceReader() {
|
275
|
return resourceReader;
|
276
|
}
|
277
|
|
278
|
public void setResourceReader(final ResourceReader resourceReader) {
|
279
|
this.resourceReader = resourceReader;
|
280
|
}
|
281
|
|
282
|
public CatalogueAPIClient getCatalogueAPIClient() {
|
283
|
return catalogueAPIClient;
|
284
|
}
|
285
|
|
286
|
public void setCatalogueAPIClient(final CatalogueAPIClient catalogueAPIClient) {
|
287
|
this.catalogueAPIClient = catalogueAPIClient;
|
288
|
}
|
289
|
}
|