]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
6bc5112c25d831f0eb1c7f57ae465b361a4e7c43
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.common.vocabulary;
2
3 import java.util.HashMap;
4 import java.util.List;
5 import java.util.Map;
6 import java.util.regex.Matcher;
7 import java.util.regex.Pattern;
8
9 import javax.ws.rs.core.Response;
10
11 import org.collectionspace.services.client.AuthorityClient;
12 import org.collectionspace.services.client.PoxPayloadIn;
13 import org.collectionspace.services.common.ServiceMain;
14 import org.collectionspace.services.common.api.RefNameUtils;
15 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
16 import org.collectionspace.services.common.api.Tools;
17 import org.collectionspace.services.common.context.MultipartServiceContextImpl;
18 import org.collectionspace.services.common.context.ServiceContext;
19 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
20 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
21 import org.collectionspace.services.common.vocabulary.nuxeo.AuthorityIdentifierUtils;
22 import org.collectionspace.services.config.service.ServiceBindingType;
23 import org.collectionspace.services.config.tenant.RemoteClientConfig;
24 import org.collectionspace.services.config.tenant.RemoteClientConfigurations;
25 import org.collectionspace.services.config.tenant.TenantBindingType;
26 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
27 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
28 import org.dom4j.Document;
29 import org.dom4j.DocumentHelper;
30 import org.dom4j.Node;
31 import org.dom4j.XPath;
32 import org.collectionspace.services.common.document.DocumentException;
33 import org.collectionspace.services.common.document.DocumentNotFoundException;
34 import org.nuxeo.ecm.core.api.DocumentModel;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 @SuppressWarnings("rawtypes")
39 public class AuthorityServiceUtils {
40         private static final Logger logger = LoggerFactory.getLogger(AuthorityServiceUtils.class);
41         //
42         // Used to keep track if an authority item's is deprecated
43         public static final String DEFAULT_REMOTECLIENT_CONFIG_NAME = "default";
44         public static final String IS_DEPRECATED_PROPERTY = "IS_DEPRECATED_PROPERTY";
45         public static final Boolean DEPRECATED = true;
46         public static final Boolean NOT_DEPRECATED = !DEPRECATED;
47
48         // Used to keep track if an authority item's rev number should be updated
49         public static final String SHOULD_UPDATE_REV_PROPERTY = "SHOULD_UPDATE_REV_PROPERTY";
50         public static final boolean UPDATE_REV = true;
51         public static final boolean DONT_UPDATE_REV = !UPDATE_REV;
52
53         // Used to keep track if an authority item is a locally proposed member of a SAS authority
54         public static final String IS_PROPOSED_PROPERTY = "IS_PROPOSED";
55         public static final Boolean PROPOSED = true;
56         public static final Boolean NOT_PROPOSED = !PROPOSED;
57         public static final Boolean SAS_ITEM = true;
58         public static final Boolean NOT_SAS_ITEM = !SAS_ITEM;
59
60         public static final Boolean NO_CHANGE = null;
61
62         // Matches the domain name part of a refname. For example, "core.collectionspace.org" of
63         // urn:cspace:core.collectionspace.org:personauthorities:name(person):item:name(BigBird1461101206103)'Big Bird'.
64         public static final     Pattern REFNAME_DOMAIN_PATTERN = Pattern.compile("(urn:cspace:)(([a-z]{1,}\\.?)*)");
65
66         /*
67          * Try to find a named remote client configuration in the current tenant bindings.  If the value of the incoming param 'remoteClientConfigName' is
68          * blank or null, we'll try to find a name in the authority service's bindings.  If we can't find a name there, we'll try using the default name.
69          *
70          * If the incoming param 'remoteClientConfigName' is not null, we'll look through all the named remote client configurations in the tenant's binding
71          * to find the configuration.  If we can't find the named configuration, we'll throw an exception.
72          *
73          * If there are no remote client configurations in the tenant's bindings, we'll throw an exception.
74          */
75         public static final RemoteClientConfig getRemoteClientConfig(ServiceContext ctx, String remoteClientConfigName) throws Exception {
76                 RemoteClientConfig result = null;
77
78                 TenantBindingType tenantBinding = ServiceMain.getInstance().getTenantBindingConfigReader().getTenantBinding(ctx.getTenantId());
79                 RemoteClientConfigurations remoteClientConfigurations = tenantBinding.getRemoteClientConfigurations();
80                 if (remoteClientConfigurations != null) {
81                         if (Tools.isEmpty(remoteClientConfigName) == true) {
82                                 // Since the authority instance didn't specify a remote client config name, let's see if the authority type's service bindings specifies one
83                                 ServiceBindingType serviceBindingType =
84                                                 ServiceMain.getInstance().getTenantBindingConfigReader().getServiceBinding(ctx.getTenantId(), ctx.getServiceName());
85                                 remoteClientConfigName = serviceBindingType.getRemoteClientConfigName();
86                         }
87                         //
88                         // If we still don't have a remote client config name, let's use the default value.
89                         //
90                         if (Tools.isEmpty(remoteClientConfigName) == true) {
91                                 remoteClientConfigName = DEFAULT_REMOTECLIENT_CONFIG_NAME;
92                         }
93
94                         List<RemoteClientConfig> remoteClientConfigList = remoteClientConfigurations.getRemoteClientConfig();
95                         for (RemoteClientConfig config : remoteClientConfigList) {
96                                 if (config.getName().equalsIgnoreCase(remoteClientConfigName)) {
97                                         result = config;
98                                         break;
99                                 }
100                         }
101                 } else {
102                         String errMsg = String.format("No remote client configurations could be found in the tenant bindings for tenant named '%s'.",
103                                         ctx.getTenantName());
104                         logger.error(errMsg);
105                         throw new Exception(errMsg);
106                 }
107
108                 if (result == null) {
109                         String errMsg = String.format("Could not find a remote client configuration named '%s' in the tenant bindings for tenant named '%s'",
110                                         remoteClientConfigName, ctx.getTenantName());
111                         logger.error(errMsg);
112                         throw new Exception(errMsg);
113                 }
114
115                 return result;
116         }
117
118         /**
119          * Make a request to the SAS Server for an authority payload.
120          *
121          * @param ctx
122          * @param specifier
123          * @param responseType
124          * @return
125          * @throws Exception
126          */
127         public static PoxPayloadIn requestPayloadInFromRemoteServer(ServiceContext ctx, String remoteClientConfigName, Specifier specifier, Class responseType) throws Exception {
128                 PoxPayloadIn result = null;
129
130                 RemoteClientConfig remoteClientConfig = getRemoteClientConfig(ctx, remoteClientConfigName);
131                 AuthorityClient client = (AuthorityClient) ctx.getClient(remoteClientConfig);
132
133                 Response res = client.read(specifier.getURNValue());
134                 try {
135                         int statusCode = res.getStatus();
136                         if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
137                                 result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response!
138                         } else {
139                                 String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'.  Server returned status code %d",
140                                                 specifier.getURNValue(), remoteClientConfig.getUrl(), statusCode);
141                                 if (logger.isDebugEnabled()) {
142                                         logger.debug(errMsg);
143                                 }
144                                 throw new DocumentException(statusCode, errMsg);
145                         }
146                 } finally {
147                         res.close();
148                 }
149
150                 return result;
151         }
152
153         //
154         // Makes a call to the remote SAS server for a authority item payload
155         //
156         public static PoxPayloadIn requestPayloadInFromRemoteServer(
157                         AuthorityItemSpecifier specifier,
158                         String remoteClientConfigName,
159                         String serviceName,
160                         Class responseType,
161                         boolean syncHierarchicalRelationships) throws Exception {
162                 PoxPayloadIn result = null;
163
164                 ServiceContext authorityCtx = new MultipartServiceContextImpl(serviceName);
165                 RemoteClientConfig remoteClientConfig = getRemoteClientConfig(authorityCtx, remoteClientConfigName);
166                 AuthorityClient client = (AuthorityClient) authorityCtx.getClient(remoteClientConfig);
167                 Response res = client.readItem(specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(),
168                                 AuthorityClient.INCLUDE_DELETED_ITEMS, syncHierarchicalRelationships);
169
170                 try {
171                         int statusCode = res.getStatus();
172                         if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
173                                 result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response.
174                         } else {
175                                 String errMsg = String.format("Could not retrieve authority item information for '%s:%s' on remote server '%s'.  Server returned status code %d",
176                                                 specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(), remoteClientConfig.getUrl(), statusCode);
177                                 if (logger.isDebugEnabled()) {
178                                         logger.debug(errMsg);
179                                 }
180                                 throw new DocumentException(statusCode, errMsg);
181                         }
182                 } finally {
183                         res.close();
184                 }
185
186                 return result;
187         }
188
189         public static boolean setAuthorityItemDeprecated(ServiceContext ctx,
190                         DocumentModel docModel, String authorityItemCommonSchemaName, Boolean flag) throws Exception {
191                 boolean result = false;
192
193                 docModel.setProperty(authorityItemCommonSchemaName, AuthorityItemJAXBSchema.DEPRECATED,
194                                 new Boolean(flag));
195                 CoreSessionInterface repoSession = (CoreSessionInterface) ctx.getCurrentRepositorySession();
196                 repoSession.saveDocument(docModel);
197                 result = true;
198
199                 return result;
200         }
201
202         /*
203          * The domain name part of refnames on a remote SAS may not match that of local refnames.
204          * Update all the payload's refnames with the local domain name.
205          */
206         public static PoxPayloadIn localizeRefNameDomains(ServiceContext ctx, PoxPayloadIn payload) throws org.dom4j.DocumentException {
207                 String localDomain = ctx.getTenantName();
208                 Matcher matcher = REFNAME_DOMAIN_PATTERN.matcher(payload.getXmlPayload());
209                 StringBuffer localizedXmlBuffer = new StringBuffer();
210
211                 while (matcher.find() == true) {
212                         String remoteDomain = matcher.group(2);
213
214                         if (logger.isDebugEnabled()) {
215                                 logger.debug("Replacing " + remoteDomain + " with " + localDomain);
216                         }
217
218                         matcher.appendReplacement(localizedXmlBuffer, matcher.group(1) + localDomain);
219                 }
220
221                 matcher.appendTail(localizedXmlBuffer);
222
223                 if (logger.isTraceEnabled()) {
224                         logger.trace(String.format("Updated payload:\n%s", localizedXmlBuffer));
225                 }
226
227                 return new PoxPayloadIn(localizedXmlBuffer.toString());
228         }
229
230         /**
231          * Localizes the relations list in an authority item payload from a remote server during SAS sync.
232          *
233          * Relations to items that do not exist in the local authority are removed. If the related item
234          * doesn't exist locally because it is new on the remote and hasn't been synced yet, the relations
235          * will be created when the new item is synced, because the new item's relations list will include
236          * them.
237          *
238          * The following elements are removed from each relation: uri, csid, subjectCsid, objectCsid,
239          * object/csid, object/uri, subject/csid, subject/uri. These apply only to the remote. By removing
240          * them, the relation will be created locally if necessary.
241          *
242          * @param ctx
243          * @param authorityResource
244          * @param parentCsid
245          * @param itemSpecifier
246          * @param payload
247          * @return
248          * @throws Exception
249          */
250         public static PoxPayloadIn localizeRelations(ServiceContext ctx, AuthorityResource authorityResource, String parentCsid, Specifier itemSpecifier, PoxPayloadIn payload) throws Exception {
251                 // TODO: Relations to items that don't exist need to be removed, because a create/update will fail
252                 // if the subject/object of any supplied relation can't be found. Consider changing the create/update
253                 // code to ignore any relations to items that don't exist, but still save the record and any other
254                 // relations. This will speed up sync when many items have relations, since the checks to see if
255                 // all related items exist locally can be skipped.
256
257                 String itemShortId = itemSpecifier.value;
258                 Document document = payload.getDOMDocument();
259
260                 Map<String, String> namespaceUris = new HashMap<String, String>();
261                 namespaceUris.put("rel", "http://collectionspace.org/services/relation");
262
263                 XPath xPath = DocumentHelper.createXPath("//rel:relations-common-list/relation-list-item");
264                 xPath.setNamespaceURIs(namespaceUris);
265
266                 List<Node> listItemNodes = xPath.selectNodes(document);
267
268                 if (logger.isDebugEnabled()) {
269                         logger.debug(String.format("Found %d relation list items", listItemNodes.size()));
270                 }
271
272                 for (Node listItemNode : listItemNodes) {
273                         String objectRefName = listItemNode.selectSingleNode("object/refName").getText();
274                         AuthorityTermInfo objectTermInfo = RefNameUtils.parseAuthorityTermInfo(objectRefName);
275                         String objectShortId = objectTermInfo.name;
276
277                         if (
278                                 !objectShortId.equals(itemShortId)
279                                 && !checkItemExists(ctx, authorityResource, parentCsid, Specifier.createShortIdURNValue(objectShortId))
280                         ) {
281                                 if (logger.isDebugEnabled()) {
282                                         logger.debug(String.format("Omitting remote relation: object with short id %s does does not exist locally", objectShortId));
283                                 }
284
285                                 listItemNode.detach();
286                                 continue;
287                         }
288
289                         String subjectRefName = listItemNode.selectSingleNode("subject/refName").getText();
290                         AuthorityTermInfo subjectTermInfo = RefNameUtils.parseAuthorityTermInfo(subjectRefName);
291                         String subjectShortId = subjectTermInfo.name;
292
293                         if (
294                                 !subjectShortId.equals(itemShortId)
295                                 && !checkItemExists(ctx, authorityResource, parentCsid, Specifier.createShortIdURNValue(subjectShortId))
296                         ) {
297                                 if (logger.isDebugEnabled()) {
298                                         logger.debug(String.format("Omitting remote relation: subject with short id %s does does not exist locally", subjectShortId));
299                                 }
300
301                                 listItemNode.detach();
302                                 continue;
303                         }
304
305                         listItemNode.selectSingleNode("csid").detach();
306                         listItemNode.selectSingleNode("objectCsid").detach();
307                         listItemNode.selectSingleNode("subjectCsid").detach();
308                         listItemNode.selectSingleNode("uri").detach();
309                         listItemNode.selectSingleNode("object/csid").detach();
310                         listItemNode.selectSingleNode("object/uri").detach();
311                         listItemNode.selectSingleNode("subject/csid").detach();
312                         listItemNode.selectSingleNode("subject/uri").detach();
313                 }
314
315                 String xml = document.asXML();
316
317                 if (logger.isTraceEnabled()) {
318                         logger.trace("Prepared remote relations:\n" + xml);
319                 }
320
321                 return new PoxPayloadIn(xml);
322         }
323
324         /**
325          * Check if an item with a given short ID exists in a parent.
326          *
327          * @param ctx
328          * @param authorityResource
329          * @param parentCsid
330          * @param itemSpecifier
331          * @return true if the item exists, false otherwise.
332          * @throws Exception
333          */
334         public static boolean checkItemExists(ServiceContext ctx, AuthorityResource authorityResource, String parentCsid, String itemSpecifier) throws Exception {
335                 String itemCsid = null;
336
337                 try {
338                         itemCsid = authorityResource.lookupItemCSID(ctx, itemSpecifier, parentCsid, "checkItemExists()", "CHECK_ITEM_EXISTS");
339                 } catch (DocumentNotFoundException e) {
340                         itemCsid = null;
341                 }
342
343                 return (itemCsid != null);
344         }
345
346         /**
347          * Mark the authority item as deprecated.
348          *
349          * @param ctx
350          * @param itemInfo
351          * @throws Exception
352          */
353         public static boolean markAuthorityItemAsDeprecated(ServiceContext ctx, String authorityItemCommonSchemaName, AuthorityItemSpecifier authorityItemSpecifier) throws Exception {
354                 boolean result = false;
355
356                 try {
357                         DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(),
358                                         authorityItemCommonSchemaName, authorityItemSpecifier);
359                         result = setAuthorityItemDeprecated(ctx, docModel, authorityItemCommonSchemaName, AuthorityServiceUtils.DEPRECATED);
360                 } catch (Exception e) {
361                         logger.warn(String.format("Could not mark item '%s' as deprecated.", authorityItemSpecifier.getItemSpecifier().getURNValue()), e);
362                         throw e;
363                 }
364
365                 return result;
366         }
367 }