<artifactId>org.collectionspace.services.systeminfo.service</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.collectionspace.services</groupId>
+ <artifactId>org.collectionspace.services.index.service</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.collectionspace.services</groupId>
<artifactId>org.collectionspace.services.media.service</artifactId>
import org.collectionspace.services.media.MediaResource;
import org.collectionspace.services.group.GroupResource;
import org.collectionspace.services.intake.IntakeResource;
+import org.collectionspace.services.index.IndexResource;
import org.collectionspace.services.loanin.LoaninResource;
import org.collectionspace.services.loanout.LoanoutResource;
import org.collectionspace.services.uoc.UocResource;
singletons.add(new ImportsResource());
singletons.add(new StructuredDateResource());
singletons.add(new SystemInfoResource());
+ singletons.add(new IndexResource());
addResourceToMapAndSingletons(new VocabularyResource());
addResourceToMapAndSingletons(new PersonAuthorityResource());
public static final String SERVICE_COMMONPART_NAME = SERVICE_NAME + PART_LABEL_SEPARATOR + PART_COMMON_LABEL;
public static final String SERVICE_AUTHZ_SUFFIX = "/*/" + SERVICE_PATH_COMPONENT + "/";
public static final String INDEX_ID_PARAM = "indexid";
-
+ public static final String FULLTEXT_ID = "fulltext";
+ public static final String ELASTICSEARCH_ID = "elasticsearch";
+ public static final String DEFAULT_REINDEX_QUERY = "SELECT ecm:uuid, ecm:primaryType FROM Document"
+ + " WHERE ecm:isProxy = 0"
+ + " ORDER BY ecm:uuid";
+
public IndexClient() throws Exception {
super();
}
<tenant:serviceBindings id="Index" merge:matcher="id" name="Index" type="utility"
version="1.0">
<service:repositoryDomain xmlns:service="http://collectionspace.org/services/config/service">default-domain</service:repositoryDomain>
- <service:documentHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.common.index.service.nuxeo.WorkflowDocumentModelHandler
+ <service:documentHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.index.nuxeo.IndexDocumentModelHandler
</service:documentHandler>
- <service:validatorHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.index.nuxeo.WorkflowValidatorHandler
+ <service:validatorHandler xmlns:service="http://collectionspace.org/services/config/service">org.collectionspace.services.index.nuxeo.IndexValidatorHandler
</service:validatorHandler>
+ <service:properties xmlns:service="http://collectionspace.org/services/config/service">
+ <types:item xmlns:types="http://collectionspace.org/services/config/types">
+ <types:key>fulltextquery</types:key>
+ <types:value>SELECT ecm:uuid, ecm:primaryType FROM Document WHERE ecm:isProxy = 0 AND ecm:currentLifeCycleState <> 'deleted' ORDER BY ecm:uuid</types:value>
+ </types:item>
+ </service:properties>
<service:object xmlns:service="http://collectionspace.org/services/config/service" name="Index"
version="1.0">
<service:part id="0" control_group="Managed" versionable="true" auditable="false" label="index-system"
}
//======================= REINDEX ====================================================
- @GET
+ @POST
@Path("{csid}/index/{indexid}")
public Response reindex(
@Context Request request,
}
//======================= REINDEX ====================================================
- @GET
+ @POST
@Path("index/{indexid}")
public Response reindex(
@Context Request request,
import java.lang.IndexOutOfBoundsException;
+import org.collectionspace.services.client.index.IndexClient;
import org.collectionspace.services.common.api.Tools;
import org.collectionspace.services.common.document.DocumentUtils;
import org.slf4j.Logger;
public static final boolean QUALIFIED_PROP_NAMES = true;
public static final boolean UNQUALIFIED_PROP_NAMES = false;
public static final String OUTPUT_MIME_PROP = "outputMIME";
+ public static final String FULLTEXT_QUERY = IndexClient.FULLTEXT_ID;
+ public static final String ELASTICSEARCH_QUERY = IndexClient.ELASTICSEARCH_ID;
public static final String AUTH_REF_PROP = "authRef";
public static final String TERM_REF_PROP = "termRef";
public static final String OBJ_NUMBER_PROP = "objectNumberProperty";
import org.collectionspace.services.client.CollectionSpaceClient;
import org.collectionspace.services.client.index.IndexClient;
import org.collectionspace.services.client.workflow.WorkflowClient;
+import org.collectionspace.services.common.api.Tools;
import org.collectionspace.services.config.service.ServiceBindingType;
import org.collectionspace.authentication.AuthN;
while (strTok.hasMoreTokens() == true) {
pathSegment = strTok.nextToken();
if (pathSegment.equals("*") ||
- pathSegment.equals("index") || pathSegment.equals(CollectionSpaceClient.SERVICE_DESCRIPTION_PATH)) { // Strip off subresource paths since they inherit their parent's permissions
+ pathSegment.equals(IndexClient.SERVICE_PATH_COMPONENT) || pathSegment.equals(CollectionSpaceClient.SERVICE_DESCRIPTION_PATH)) { // Strip off subresource paths since they inherit their parent's permissions
//
// leave the loop if we hit a wildcard character or the "index" subresource
//
}
result = result.concat(pathSegment);
}
+ //
+ // Special case for the "index" services since "index" is also a subresource for some of the other services.
+ //
+ if (Tools.isEmpty(result) && pathSegment.equals(IndexClient.SERVICE_PATH_COMPONENT)) {
+ result = IndexClient.SERVICE_PATH_COMPONENT;
+ }
return result;
}
import org.collectionspace.services.client.PoxPayloadIn;
import org.collectionspace.services.client.PoxPayloadOut;
import org.collectionspace.services.client.Profiler;
+import org.collectionspace.services.client.index.IndexClient;
import org.collectionspace.services.client.workflow.WorkflowClient;
import org.collectionspace.services.common.context.ServiceContext;
import org.collectionspace.services.common.query.QueryContext;
import org.nuxeo.ecm.core.api.DocumentRef;
import org.nuxeo.ecm.core.api.IdRef;
import org.nuxeo.ecm.core.api.PathRef;
+import org.nuxeo.runtime.api.Framework;
import org.nuxeo.runtime.transaction.TransactionRuntimeException;
import org.nuxeo.ecm.core.opencmis.bindings.NuxeoCmisServiceFactory;
import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoCmisService;
+import org.nuxeo.elasticsearch.api.ElasticSearchAdmin;
+import org.nuxeo.elasticsearch.api.ElasticSearchIndexing;
+import org.nuxeo.elasticsearch.api.ElasticSearchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public boolean reindex(DocumentHandler handler, String csid, String indexid) throws DocumentNotFoundException, DocumentException
{
boolean result = true;
+
+ switch (indexid) {
+ case IndexClient.FULLTEXT_ID:
+ result = reindexFulltext(handler, csid, indexid);
+ break;
+ case IndexClient.ELASTICSEARCH_ID:
+ result = reindexElasticsearch(handler, csid, indexid);
+ break;
+ default:
+ throw new NuxeoDocumentException(String.format("Unknown index '%s'. Reindex request failed.",
+ indexid));
+ }
+
+ return result;
+ }
+
+ /**
+ * Reindex Nuxeo's fulltext index.
+ *
+ * @param handler
+ * @param csid
+ * @param indexid
+ * @return
+ * @throws NuxeoDocumentException
+ * @throws TransactionException
+ */
+ private boolean reindexFulltext(DocumentHandler handler, String csid, String indexid) throws NuxeoDocumentException, TransactionException {
+ boolean result = true;
CoreSessionInterface repoSession = null;
ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = handler.getServiceContext();
repoSession = getRepositorySession(ctx);
CSReindexFulltextRoot indexer = new CSReindexFulltextRoot(repoSession);
indexer.reindexFulltext(0, 0, queryString);
- //
- // Set repository session to handle the document
- //
} catch (Throwable e) {
rollbackTransaction(repoSession);
if (logger.isDebugEnabled()) {
return result;
}
+
+ /**
+ * Reindex Nuxeo's Elasticsearch index.
+ *
+ * @param handler
+ * @param csid
+ * @param indexid
+ * @return
+ * @throws NuxeoDocumentException
+ * @throws TransactionException
+ */
+ private boolean reindexElasticsearch(DocumentHandler handler, String csid, String indexid) throws NuxeoDocumentException, TransactionException {
+ boolean result = false;
+ CoreSessionInterface repoSession = null;
+ ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = handler.getServiceContext();
+
+ try {
+ ElasticSearchIndexing esi = Framework.getService(ElasticSearchIndexing.class);
+ ElasticSearchAdmin esa = Framework.getService(ElasticSearchAdmin.class);
+
+ String queryString = handler.getDocumentsToIndexQuery(indexid, csid);
+ esa.initIndexes(true);
+ esa.refresh();
+ repoSession = getRepositorySession(ctx);
+ esi.runReindexingWorker(repoSession.getRepositoryName(), queryString);
+ result = true;
+ } catch (Throwable e) {
+ rollbackTransaction(repoSession);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Caught exception trying to reindex Nuxeo repository ", e);
+ }
+ throw new NuxeoDocumentException(e);
+ } finally {
+ if (repoSession != null) {
+ releaseRepositorySession(ctx, repoSession);
+ }
+ }
+
+ return result;
+ }
@Override
public boolean synchronize(ServiceContext ctx, Object specifier, DocumentHandler handler)
--- /dev/null
+/**
+ *
+ */
+package org.collectionspace.services;
+
+/**
+ * @author remillet
+ *
+ */
+public interface IndexJAXBSchema {
+ final static String INDEX_NAME = "indexName";
+}
--- /dev/null
+package org.collectionspace.services;
+
+public interface IndexListItemJAXBSchema {
+ final static String INDEX_NAME = "indexName";
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+
+<!--
+ Index schema (XSD)
+
+ Entity : Index
+ Part : Common
+ Used for: JAXB binding between XML and Java objects
+
+ $LastChangedRevision$
+ $LastChangedDate$
+-->
+
+<xs:schema
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
+ jaxb:version="1.0" elementFormDefault="unqualified"
+ xmlns:ns="http://collectionspace.org/services/index"
+ xmlns="http://collectionspace.org/services/index"
+ targetNamespace="http://collectionspace.org/services/index"
+ version="0.1"
+>
+
+<!--
+ Avoid XmlRootElement nightmare:
+ See http://weblogs.java.net/blog/kohsuke/archive/2006/03/why_does_jaxb_p.html
+-->
+<!-- See http://wiki.collectionspace.org/display/collectionspace/Index+Schema -->
+
+ <!-- index -->
+ <xs:element name="index_common">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="indexName" type="xs:string" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
+
<packaging>pom</packaging>
<modules>
+ <module>jaxb</module>
<module>3rdparty</module>
<module>client</module>
<module>service</module>
*/
package org.collectionspace.services.index;
+import org.collectionspace.services.client.PoxPayloadIn;
+import org.collectionspace.services.client.PoxPayloadOut;
import org.collectionspace.services.client.index.IndexClient;
import org.collectionspace.services.common.CSWebApplicationException;
import org.collectionspace.services.common.NuxeoBasedResource;
import org.collectionspace.services.common.ResourceMap;
import org.collectionspace.services.common.ServiceMessages;
+import org.collectionspace.services.common.UriInfoWrapper;
+import org.collectionspace.services.common.context.RemoteServiceContext;
+import org.collectionspace.services.common.document.DocumentHandler;
import org.collectionspace.services.jaxb.AbstractCommonList;
import javax.ws.rs.Consumes;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
return response;
}
+ @POST
+ @Path("{indexid}")
+ public Response reindex(
+ @Context Request request,
+ @Context UriInfo uriInfo,
+ @PathParam("indexid") String indexid) {
+ uriInfo = new UriInfoWrapper(uriInfo);
+ Response result = Response.noContent().build();
+ boolean success = false;
+ String docType = null;
+
+ try {
+ RemoteServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = (RemoteServiceContext<PoxPayloadIn, PoxPayloadOut>) createServiceContext(uriInfo);
+ docType = ctx.getTenantQualifiedDoctype(); // this will used in the error message if an error occurs
+ DocumentHandler handler = createDocumentHandler(ctx);
+ success = getRepositoryClient(ctx).reindex(handler, indexid);
+ } catch (Exception e) {
+ throw bigReThrow(e, ServiceMessages.REINDEX_FAILED);
+ }
+
+ if (success == false) {
+ Response response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
+ ServiceMessages.REINDEX_FAILED + ServiceMessages.resourceNotReindexedMsg(docType)).type("text/plain").build();
+ throw new CSWebApplicationException(response);
+ }
+
+ return result;
+ }
/* (non-Javadoc)
* @see org.collectionspace.services.common.ResourceBase#getList(javax.ws.rs.core.UriInfo, java.lang.String)
--- /dev/null
+/**
+ * This document is a part of the source code and related artifacts
+ * for CollectionSpace, an open source collections management system
+ * for museums and related institutions:
+
+ * http://www.collectionspace.org
+ * http://wiki.collectionspace.org
+
+ * Copyright 2009 University of California at Berkeley
+
+ * Licensed under the Educational Community License (ECL), Version 2.0.
+ * You may not use this file except in compliance with this License.
+
+ * You may obtain a copy of the ECL 2.0 License at
+
+ * https://source.collectionspace.org/collection-space/LICENSE.txt
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.collectionspace.services.index.nuxeo;
+
+import java.util.List;
+
+import org.collectionspace.services.client.index.IndexClient;
+import org.collectionspace.services.common.ServiceMain;
+import org.collectionspace.services.common.api.Tools;
+import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
+import org.collectionspace.services.common.context.ServiceBindingUtils;
+import org.collectionspace.services.common.context.ServiceContext;
+import org.collectionspace.services.common.document.DocumentException;
+import org.collectionspace.services.config.service.ServiceBindingType;
+import org.collectionspace.services.config.types.PropertyItemType;
+import org.collectionspace.services.index.IndexCommon;
+import org.collectionspace.services.nuxeo.client.java.NuxeoDocumentModelHandler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * IntakeDocumentModelHandler
+ *
+ * $LastChangedRevision: $
+ * $LastChangedDate: $
+ */
+public class IndexDocumentModelHandler
+ extends NuxeoDocumentModelHandler<IndexCommon> {
+ private final Logger logger = LoggerFactory.getLogger(IndexDocumentModelHandler.class);
+
+
+ @Override
+ public String getDocumentsToIndexQuery(String indexId, String csid) throws DocumentException, Exception {
+ String result = null;
+
+ switch (indexId) {
+ case IndexClient.FULLTEXT_ID:
+ result = getReindexQuery(indexId, csid);
+ break;
+ case IndexClient.ELASTICSEARCH_ID:
+ result = getReindexQuery(indexId, csid);
+
+ break;
+ }
+
+ if (Tools.isEmpty(result) == true) {
+ String msg = String.format("There is no reindex query in the Index service bindings for index '%s', so we'll use this default query: '%s'",
+ indexId, IndexClient.DEFAULT_REINDEX_QUERY);
+ logger.warn(msg);
+ result = IndexClient.DEFAULT_REINDEX_QUERY;
+ }
+
+ return result;
+ }
+
+ /**
+ * Reads the Index service bindings to get the query that will be used to find all documents needing
+ * reindexing.
+ *
+ * @param indexId
+ * @param csid
+ * @return
+ * @throws DocumentException
+ * @throws Exception
+ *
+ * TODO: Use the incoming CSID param to qualify the returned query.
+ */
+ private String getReindexQuery(String indexId, String csid) throws DocumentException, Exception {
+ String result = null;
+
+ //
+ // Read in the NXQL query to use when performing a full
+ //
+ TenantBindingConfigReaderImpl tReader =
+ ServiceMain.getInstance().getTenantBindingConfigReader();
+ ServiceContext ctx = this.getServiceContext();
+
+ ServiceBindingType reportServiceBinding = tReader.getServiceBinding(ctx.getTenantId(), ctx.getServiceName());
+ List<PropertyItemType> queryTypeList = ServiceBindingUtils.getPropertyValueList(reportServiceBinding, indexId);
+
+ if (queryTypeList != null && queryTypeList.isEmpty() == false) {
+ PropertyItemType propertyItemType = queryTypeList.get(0);
+ if (propertyItemType != null) {
+ result = propertyItemType.getValue();
+ }
+ }
+
+ return result;
+ }
+
+}
+
private final Logger logger = LoggerFactory.getLogger(IndexValidatorHandler.class);
/** Error Messages **/
- private static final String VALIDATION_ERROR = "The intake record payload was invalid. See log file for more details.";
+ private static final String VALIDATION_ERROR = "The index record payload was invalid. See log file for more details.";
@Override
@Override
protected void handleCreate() throws InvalidDocumentException {
try {
- IndexCommon intakesCommon = (IndexCommon)getCommonPart();
- assert(intakesCommon != null);
+ IndexCommon indexCommon = (IndexCommon)getCommonPart();
+ assert(indexCommon != null);
} catch (AssertionError e) {
if (logger.isErrorEnabled() == true) {
logger.error(e.getMessage(), e);