1 package org.collectionspace.services.common.vocabulary;
3 import java.util.ArrayList;
4 import java.util.HashMap;
7 import java.util.regex.Matcher;
8 import java.util.regex.Pattern;
10 import javax.ws.rs.core.Response;
12 import org.collectionspace.services.client.AuthorityClient;
13 import org.collectionspace.services.client.PoxPayloadIn;
14 import org.collectionspace.services.client.workflow.WorkflowClient;
15 import org.collectionspace.services.common.ServiceMain;
16 import org.collectionspace.services.common.api.RefNameUtils;
17 import org.collectionspace.services.common.api.RefNameUtils.AuthorityTermInfo;
18 import org.collectionspace.services.common.api.Tools;
19 import org.collectionspace.services.common.context.MultipartServiceContextImpl;
20 import org.collectionspace.services.common.context.ServiceContext;
21 import org.collectionspace.services.common.document.DocumentException;
22 import org.collectionspace.services.common.document.DocumentNotFoundException;
23 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthorityItemSpecifier;
24 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.Specifier;
25 import org.collectionspace.services.config.service.ServiceBindingType;
26 import org.collectionspace.services.config.tenant.RemoteClientConfig;
27 import org.collectionspace.services.config.tenant.RemoteClientConfigurations;
28 import org.collectionspace.services.config.tenant.TenantBindingType;
29 import org.collectionspace.services.nuxeo.client.java.CoreSessionInterface;
30 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
31 import org.dom4j.Document;
32 import org.dom4j.DocumentHelper;
33 import org.dom4j.Node;
34 import org.dom4j.XPath;
35 import org.nuxeo.ecm.core.api.DocumentModel;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
39 @SuppressWarnings("rawtypes")
40 public class AuthorityServiceUtils {
41 private static final Logger logger = LoggerFactory.getLogger(AuthorityServiceUtils.class);
43 // Used to keep track if an authority item's is deprecated
44 public static final String DEFAULT_REMOTECLIENT_CONFIG_NAME = "default";
45 public static final String IS_DEPRECATED_PROPERTY = "IS_DEPRECATED_PROPERTY";
46 public static final Boolean DEPRECATED = true;
47 public static final Boolean NOT_DEPRECATED = !DEPRECATED;
49 // Used to keep track if an authority item's rev number should be updated
50 public static final String SHOULD_UPDATE_REV_PROPERTY = "SHOULD_UPDATE_REV_PROPERTY";
51 public static final boolean UPDATE_REV = true;
52 public static final boolean DONT_UPDATE_REV = !UPDATE_REV;
54 public static final boolean ROLLBACK_ON_EXCEPTION = true;
55 public static final boolean DONT_ROLLBACK_ON_EXCEPTION = !ROLLBACK_ON_EXCEPTION;
57 // Used to keep track if an authority item is a locally proposed member of a SAS authority
58 public static final String IS_PROPOSED_PROPERTY = "IS_PROPOSED";
59 public static final Boolean PROPOSED = true;
60 public static final Boolean NOT_PROPOSED = !PROPOSED;
61 public static final Boolean SAS_ITEM = true;
62 public static final Boolean NOT_SAS_ITEM = !SAS_ITEM;
64 public static final Boolean NO_CHANGE = null;
66 // Matches the domain name part of a refname. For example, "core.collectionspace.org" of
67 // urn:cspace:core.collectionspace.org:personauthorities:name(person):item:name(BigBird1461101206103)'Big Bird'.
68 public static final Pattern REFNAME_DOMAIN_PATTERN = Pattern.compile("(urn:cspace:)(([a-z]{1,}\\.?)*)");
71 * Try to find a named remote client configuration in the current tenant bindings. If the value of the incoming param 'remoteClientConfigName' is
72 * 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.
74 * If the incoming param 'remoteClientConfigName' is not null, we'll look through all the named remote client configurations in the tenant's binding
75 * to find the configuration. If we can't find the named configuration, we'll throw an exception.
77 * If there are no remote client configurations in the tenant's bindings, we'll throw an exception.
79 public static final RemoteClientConfig getRemoteClientConfig(ServiceContext ctx, String remoteClientConfigName) throws Exception {
80 RemoteClientConfig result = null;
82 TenantBindingType tenantBinding = ServiceMain.getInstance().getTenantBindingConfigReader().getTenantBinding(ctx.getTenantId());
83 RemoteClientConfigurations remoteClientConfigurations = tenantBinding.getRemoteClientConfigurations();
84 if (remoteClientConfigurations != null) {
85 if (Tools.isEmpty(remoteClientConfigName) == true) {
86 // Since the authority instance didn't specify a remote client config name, let's see if the authority type's service bindings specifies one
87 ServiceBindingType serviceBindingType =
88 ServiceMain.getInstance().getTenantBindingConfigReader().getServiceBinding(ctx.getTenantId(), ctx.getServiceName());
89 remoteClientConfigName = serviceBindingType.getRemoteClientConfigName();
92 // If we still don't have a remote client config name, let's use the default value.
94 if (Tools.isEmpty(remoteClientConfigName) == true) {
95 remoteClientConfigName = DEFAULT_REMOTECLIENT_CONFIG_NAME;
98 List<RemoteClientConfig> remoteClientConfigList = remoteClientConfigurations.getRemoteClientConfig();
99 for (RemoteClientConfig config : remoteClientConfigList) {
100 if (config.getName().equalsIgnoreCase(remoteClientConfigName)) {
106 String errMsg = String.format("No remote client configurations could be found in the tenant bindings for tenant named '%s'.",
107 ctx.getTenantName());
108 logger.error(errMsg);
109 throw new Exception(errMsg);
112 if (result == null) {
113 String errMsg = String.format("Could not find a remote client configuration named '%s' in the tenant bindings for tenant named '%s'",
114 remoteClientConfigName, ctx.getTenantName());
115 logger.error(errMsg);
116 throw new Exception(errMsg);
123 * Make a request to the SAS Server for an authority payload.
127 * @param responseType
131 public static PoxPayloadIn requestPayloadInFromRemoteServer(ServiceContext ctx, String remoteClientConfigName, Specifier specifier, Class responseType) throws Exception {
132 PoxPayloadIn result = null;
134 RemoteClientConfig remoteClientConfig = getRemoteClientConfig(ctx, remoteClientConfigName);
135 AuthorityClient client = (AuthorityClient) ctx.getClient(remoteClientConfig);
137 Response res = client.read(specifier.getURNValue());
139 int statusCode = res.getStatus();
140 if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
141 result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response!
143 String errMsg = String.format("Could not retrieve authority information for '%s' on remote server '%s'. Server returned status code %d",
144 specifier.getURNValue(), remoteClientConfig.getUrl(), statusCode);
145 if (logger.isDebugEnabled()) {
146 logger.debug(errMsg);
148 throw new DocumentException(statusCode, errMsg);
158 // Makes a call to the remote SAS server for a authority item payload
160 public static PoxPayloadIn requestPayloadInFromRemoteServer(
161 AuthorityItemSpecifier specifier,
162 String remoteClientConfigName,
165 boolean syncHierarchicalRelationships) throws Exception {
166 PoxPayloadIn result = null;
168 ServiceContext authorityCtx = new MultipartServiceContextImpl(serviceName);
169 RemoteClientConfig remoteClientConfig = getRemoteClientConfig(authorityCtx, remoteClientConfigName);
170 AuthorityClient client = (AuthorityClient) authorityCtx.getClient(remoteClientConfig);
171 Response res = client.readItem(specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(),
172 AuthorityClient.INCLUDE_DELETED_ITEMS, syncHierarchicalRelationships);
175 int statusCode = res.getStatus();
176 if (statusCode == org.apache.commons.httpclient.HttpStatus.SC_OK) {
177 result = new PoxPayloadIn((String)res.readEntity(responseType)); // Get the entire response.
179 String errMsg = String.format("Could not retrieve authority item information for '%s:%s' on remote server '%s'. Server returned status code %d",
180 specifier.getParentSpecifier().getURNValue(), specifier.getItemSpecifier().getURNValue(), remoteClientConfig.getUrl(), statusCode);
181 if (logger.isDebugEnabled()) {
182 logger.debug(errMsg);
184 throw new DocumentException(statusCode, errMsg);
194 * The domain name part of refnames on a remote SAS may not match that of local refnames.
195 * Update all the payload's refnames with the local domain name.
197 public static PoxPayloadIn localizeRefNameDomains(ServiceContext ctx, PoxPayloadIn payload) throws org.dom4j.DocumentException {
198 String localDomain = ctx.getTenantName();
199 Matcher matcher = REFNAME_DOMAIN_PATTERN.matcher(payload.getXmlPayload());
200 StringBuffer localizedXmlBuffer = new StringBuffer();
202 while (matcher.find() == true) {
203 String remoteDomain = matcher.group(2);
205 if (logger.isDebugEnabled()) {
206 logger.debug("Replacing " + remoteDomain + " with " + localDomain);
209 matcher.appendReplacement(localizedXmlBuffer, matcher.group(1) + localDomain);
212 matcher.appendTail(localizedXmlBuffer);
214 if (logger.isTraceEnabled()) {
215 logger.trace(String.format("Updated payload:\n%s", localizedXmlBuffer));
218 return new PoxPayloadIn(localizedXmlBuffer.toString());
222 * Localizes the relations list in an authority item payload from a remote server during SAS sync.
224 * Relations to items that do not exist in the local authority are removed. If the related item
225 * doesn't exist locally because it is new on the remote and hasn't been synced yet, the relations
226 * will be created when the new item is synced, because the new item's relations list will include
229 * The following elements are removed from each relation: uri, csid, subjectCsid, objectCsid,
230 * object/csid, object/uri, subject/csid, subject/uri. These apply only to the remote. By removing
231 * them, the relation will be created locally if necessary.
234 * @param authorityResource
236 * @param itemSpecifier
241 public static PoxPayloadIn localizeRelations(ServiceContext ctx, AuthorityResource authorityResource, String parentCsid, Specifier itemSpecifier, PoxPayloadIn payload) throws Exception {
242 // TODO: Relations to items that don't exist need to be removed, because a create/update will fail
243 // if the subject/object of any supplied relation can't be found. Consider changing the create/update
244 // code to ignore any relations to items that don't exist, but still save the record and any other
245 // relations. This will speed up sync when many items have relations, since the checks to see if
246 // all related items exist locally can be skipped.
248 String itemShortId = itemSpecifier.value;
249 Document document = payload.getDOMDocument();
251 Map<String, String> namespaceUris = new HashMap<String, String>();
252 namespaceUris.put("rel", "http://collectionspace.org/services/relation");
254 XPath xPath = DocumentHelper.createXPath("//rel:relations-common-list/relation-list-item");
255 xPath.setNamespaceURIs(namespaceUris);
257 List<Node> listItemNodes = xPath.selectNodes(document);
259 if (logger.isDebugEnabled()) {
260 logger.debug(String.format("Found %d relation list items", listItemNodes.size()));
263 for (Node listItemNode : listItemNodes) {
264 String objectRefName = listItemNode.selectSingleNode("object/refName").getText();
265 AuthorityTermInfo objectTermInfo = RefNameUtils.parseAuthorityTermInfo(objectRefName);
266 String objectShortId = objectTermInfo.name;
269 !objectShortId.equals(itemShortId)
270 && !checkItemExists(ctx, authorityResource, parentCsid, Specifier.createShortIdURNValue(objectShortId))
272 if (logger.isDebugEnabled()) {
273 logger.debug(String.format("Omitting remote relation: object with short id %s does does not exist locally", objectShortId));
276 listItemNode.detach();
280 String subjectRefName = listItemNode.selectSingleNode("subject/refName").getText();
281 AuthorityTermInfo subjectTermInfo = RefNameUtils.parseAuthorityTermInfo(subjectRefName);
282 String subjectShortId = subjectTermInfo.name;
285 !subjectShortId.equals(itemShortId)
286 && !checkItemExists(ctx, authorityResource, parentCsid, Specifier.createShortIdURNValue(subjectShortId))
288 if (logger.isDebugEnabled()) {
289 logger.debug(String.format("Omitting remote relation: subject with short id %s does does not exist locally", subjectShortId));
292 listItemNode.detach();
296 listItemNode.selectSingleNode("csid").detach();
297 listItemNode.selectSingleNode("objectCsid").detach();
298 listItemNode.selectSingleNode("subjectCsid").detach();
299 listItemNode.selectSingleNode("uri").detach();
300 listItemNode.selectSingleNode("object/csid").detach();
301 listItemNode.selectSingleNode("object/uri").detach();
302 listItemNode.selectSingleNode("subject/csid").detach();
303 listItemNode.selectSingleNode("subject/uri").detach();
306 String xml = document.asXML();
308 if (logger.isTraceEnabled()) {
309 logger.trace("Prepared remote relations:\n" + xml);
312 return new PoxPayloadIn(xml);
316 * Check if an item with a given short ID exists in a parent.
319 * @param authorityResource
321 * @param itemSpecifier
322 * @return true if the item exists, false otherwise.
325 public static boolean checkItemExists(ServiceContext ctx, AuthorityResource authorityResource, String parentCsid, String itemSpecifier) throws Exception {
326 String itemCsid = null;
329 itemCsid = authorityResource.lookupItemCSID(ctx, itemSpecifier, parentCsid, "checkItemExists()", "CHECK_ITEM_EXISTS");
330 } catch (DocumentNotFoundException e) {
334 return (itemCsid != null);
338 * Mark the authority item as deprecated.
344 public static boolean setAuthorityItemDeprecated(ServiceContext ctx, AuthorityResource authorityResource, String authorityItemCommonSchemaName, AuthorityItemSpecifier authorityItemSpecifier) throws Exception {
345 DocumentModel docModel = NuxeoUtils.getDocFromSpecifier(ctx, (CoreSessionInterface)ctx.getCurrentRepositorySession(),
346 authorityItemCommonSchemaName, authorityItemSpecifier);
348 return setAuthorityItemDeprecated(ctx, authorityResource, authorityItemSpecifier.getParentSpecifier().value, authorityItemSpecifier.getItemSpecifier().getURNValue(), docModel);
351 public static boolean setAuthorityItemDeprecated(ServiceContext ctx, AuthorityResource authorityResource, String parentIdentifier, String itemIdentifier, DocumentModel docModel) throws Exception {
352 String currentWorkflowState = docModel.getCurrentLifeCycleState();
354 // Find the transitions needed to get from the current state to the replicated deprecated state.
355 List<String> transitions = AuthorityServiceUtils.getTransitionList(WorkflowClient.WORKFLOWSTATE_DEPRECATED, currentWorkflowState);
357 if (!transitions.isEmpty()) {
358 for (String transition : transitions) {
359 authorityResource.updateItemWorkflowWithTransition(ctx, parentIdentifier, itemIdentifier, transition, AuthorityServiceUtils.DONT_UPDATE_REV);
367 * We need to change the local item's state to one that maps to the replication server's workflow
368 * state. This might involve making multiple transitions.
371 * See table at https://collectionspace.atlassian.net/wiki/spaces/SDR/pages/665886940/Workflow+transitions+to+map+SAS+item+states+to+Local+item+states
372 * (was https://wiki.collectionspace.org/pages/viewpage.action?pageId=162496564)
375 public static List<String> getTransitionList(String sasWorkflowState, String localItemWorkflowState) throws DocumentException {
376 List<String> result = new ArrayList<String>();
378 // The first set of conditions maps a replication-server "project" state to a local client state of "replicated"
380 if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
381 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
382 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
383 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
384 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
385 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
386 // Do nothing. We're good with this state
387 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
388 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
389 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
390 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
391 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
392 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
393 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
395 // The second set of conditions maps a replication-server "deleted" state to a local client state of "deleted"
397 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
398 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
399 result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
400 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
401 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
402 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
403 result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
404 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
405 // Do nothing. We're good with this state
406 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
407 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
408 result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
409 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
410 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
412 // The third set of conditions maps a replication-server "replicated" state to a local state of "replicated"
414 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
415 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
416 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
417 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
418 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
419 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
420 // Do nothing. We're good with this state
421 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
422 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
423 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
424 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
425 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
426 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
427 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
429 // The fourth set of conditions maps a replicatation-server "deprecated" state to a local state of "replicated_deprecated"
431 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
432 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
433 result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
434 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
435 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
436 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
437 result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
438 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
439 result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
440 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
441 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
442 result.add(WorkflowClient.WORKFLOWTRANSITION_DEPRECATE);
443 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
445 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DEPRECATED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
446 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDELETE);
448 // The last set of conditions maps a replication-server "replicated_deleted" state to a local client state of "deleted"
450 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_PROJECT)) {
451 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
452 result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
453 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_DELETED)) {
454 result.add(WorkflowClient.WORKFLOWTRANSITION_REPLICATE);
455 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED)) {
456 result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
457 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED)) {
458 // Do nothing. We're good with this state
459 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED)) {
460 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
461 result.add(WorkflowClient.WORKFLOWTRANSITION_DELETE);
462 } else if (sasWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED) && localItemWorkflowState.equals(WorkflowClient.WORKFLOWSTATE_REPLICATED_DEPRECATED_DELETED)) {
463 result.add(WorkflowClient.WORKFLOWTRANSITION_UNDEPRECATE);
466 // If we get here, we've encountered a SAS workflow state that we don't recognize.
468 throw new DocumentException(String.format("Encountered an invalid workflow state of '%s' on a SAS authority item.", sasWorkflowState));