001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.content.indexing.solr; 017 018import java.io.IOException; 019import java.util.ArrayList; 020import java.util.Date; 021import java.util.List; 022 023import org.apache.avalon.framework.activity.Initializable; 024import org.apache.avalon.framework.component.Component; 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.avalon.framework.service.Serviceable; 028import org.apache.solr.client.solrj.SolrClient; 029import org.apache.solr.client.solrj.SolrServerException; 030import org.apache.solr.client.solrj.response.UpdateResponse; 031import org.apache.solr.client.solrj.util.ClientUtils; 032import org.apache.solr.common.SolrInputDocument; 033 034import org.ametys.cms.search.solr.SolrClientProvider; 035import org.ametys.plugins.repository.AmetysObject; 036import org.ametys.plugins.workflow.repository.WorkflowAwareAmetysObject; 037import org.ametys.plugins.workflow.support.WorkflowProvider; 038import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 039import org.ametys.runtime.config.Config; 040import org.ametys.runtime.plugin.component.AbstractLogEnabled; 041 042import com.opensymphony.workflow.spi.Step; 043 044/** 045 * Component indexing the whole workflow of {@link AmetysObject}s. 046 */ 047public class SolrWorkflowIndexer extends AbstractLogEnabled implements Component, Serviceable, SolrFieldNames, Initializable 048{ 049 /** The component role. */ 050 public static final String ROLE = SolrWorkflowIndexer.class.getName(); 051 052 /** The Solr client provider */ 053 protected SolrClientProvider _solrClientProvider; 054 055 /** The workflow provider. */ 056 protected WorkflowProvider _workflowProvider; 057 058 private boolean _enabled; 059 060 @Override 061 public void service(ServiceManager serviceManager) throws ServiceException 062 { 063 _solrClientProvider = (SolrClientProvider) serviceManager.lookup(SolrClientProvider.ROLE); 064 _workflowProvider = (WorkflowProvider) serviceManager.lookup(WorkflowProvider.ROLE); 065 } 066 067 public void initialize() throws Exception 068 { 069 _enabled = Config.getInstance().getValue("cms.solr.index.workflow"); 070 } 071 072 /** 073 * Index the workflow history of an Ametys object. 074 * @param object The ametys object. 075 * @param workspaceName The workspace where to work in 076 * @throws SolrServerException If a solr error occurs. 077 * @throws IOException If an I/O error occurs while indexing. 078 */ 079 public void indexAmetysObjectWorkflow(WorkflowAwareAmetysObject object, String workspaceName) throws SolrServerException, IOException 080 { 081 if (_enabled) 082 { 083 SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName, true); 084 indexAmetysObjectWorkflow(object, workspaceName, solrClient); 085 } 086 } 087 088 /** 089 * Index the workflow history of an Ametys object. 090 * @param object The ametys object. 091 * @param workspaceName The workspace where to work in 092 * @param solrClient The solr client to use 093 * @throws SolrServerException If a solr error occurs. 094 * @throws IOException If an I/O error occurs while indexing. 095 */ 096 public void indexAmetysObjectWorkflow(WorkflowAwareAmetysObject object, String workspaceName, SolrClient solrClient) throws SolrServerException, IOException 097 { 098 if (_enabled) 099 { 100 long time_0 = System.currentTimeMillis(); 101 getLogger().debug("Indexing the workflow of {}", object); 102 103 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(object); 104 105 long workflowId = object.getWorkflowId(); 106 List<Step> currentSteps = workflow.getCurrentSteps(workflowId); 107 List<Step> historySteps = workflow.getHistorySteps(workflowId); 108 109 String objectId = object.getId(); 110 111 List<SolrInputDocument> documents = new ArrayList<>(); 112 113 List<String> historyStepIds = indexSteps(historySteps, objectId, documents); 114 List<String> currentStepIds = indexSteps(currentSteps, objectId, documents); 115 116 indexWorkflowEntry(workflow, workflowId, objectId, historyStepIds, currentStepIds, documents); 117 118 String collection = _solrClientProvider.getCollectionName(workspaceName); 119 UpdateResponse solrResponse = solrClient.add(collection, documents); 120 int status = solrResponse.getStatus(); 121 122 if (status != 0) 123 { 124 throw new IOException("Workflow history indexation error (status code=" + status + ")"); 125 } 126 127 getLogger().debug("Successfully indexed workflow of {} in {} ms", object, System.currentTimeMillis() - time_0); 128 } 129 } 130 131 private void indexWorkflowEntry(AmetysObjectWorkflow workflow, long workflowId, String objectId, List<String> historyStepIds, List<String> currentStepIds, List<SolrInputDocument> documents) 132 { 133 SolrInputDocument entryDoc = new SolrInputDocument(); 134 135 String id = objectId + "#workflow"; 136 137 String name = workflow.getWorkflowName(workflowId); 138 int state = workflow.getEntryState(workflowId); 139 140 entryDoc.addField(ID, id); 141 entryDoc.addField(DOCUMENT_TYPE, TYPE_WF_ENTRY); 142 entryDoc.addField(WORKFLOW_NAME, name); 143 entryDoc.addField(WORKFLOW_ENTRY_STATE, state); 144 entryDoc.addField(WORKFLOW_HISTORY_STEPS_DV, historyStepIds); 145 entryDoc.addField(WORKFLOW_CURRENT_STEPS_DV, currentStepIds); 146 147 documents.add(entryDoc); 148 } 149 150 private List<String> indexSteps(List<Step> steps, String objectId, List<SolrInputDocument> documents) 151 { 152 List<String> ids = new ArrayList<>(); 153 154 for (Step step : steps) 155 { 156 String id = indexStep(step, objectId, documents); 157 ids.add(id); 158 } 159 160 return ids; 161 } 162 163 private String indexStep(Step step, String objectId, List<SolrInputDocument> documents) 164 { 165 SolrInputDocument entryDoc = new SolrInputDocument(); 166 167 String id = objectId + "#step" + step.getId(); 168 int stepId = step.getStepId(); 169 int actionId = step.getActionId(); 170 String owner = step.getOwner(); 171 String caller = step.getCaller(); 172 Date startDate = step.getStartDate(); 173 Date dueDate = step.getDueDate(); 174 Date finishDate = step.getFinishDate(); 175 String status = step.getStatus(); 176 177 entryDoc.addField(ID, id); 178 entryDoc.addField(DOCUMENT_TYPE, TYPE_WF_STEP); 179 180 entryDoc.addField(WORKFLOW_STEP_ID, stepId); 181 entryDoc.addField(WORKFLOW_STEP_ACTIONID, actionId); 182 if (owner != null) 183 { 184 entryDoc.addField(WORKFLOW_STEP_OWNER, owner); 185 } 186 if (caller != null) 187 { 188 entryDoc.addField(WORKFLOW_STEP_CALLER, caller); 189 } 190 if (startDate != null) 191 { 192 entryDoc.addField(WORKFLOW_STEP_STARTDATE, startDate); 193 } 194 if (dueDate != null) 195 { 196 entryDoc.addField(WORKFLOW_STEP_DUEDATE, dueDate); 197 } 198 if (finishDate != null) 199 { 200 entryDoc.addField(WORKFLOW_STEP_FINISHDATE, finishDate); 201 } 202 if (status != null) 203 { 204 entryDoc.addField(WORKFLOW_STEP_STATUS, status); 205 } 206 207 documents.add(entryDoc); 208 209 return id; 210 } 211 212 /** 213 * Unindex the workflow history of an Ametys object. 214 * @param objectId The Ametys object ID. 215 * @param workspaceName The workspace where to work in 216 * @param solrClient The solr client to use 217 * @throws SolrServerException If a solr error occurs. 218 * @throws IOException If an I/O error occurs while indexing. 219 */ 220 public void unindexAmetysObjectWorkflow(String objectId, String workspaceName, SolrClient solrClient) throws SolrServerException, IOException 221 { 222 if (_enabled) 223 { 224 // _documentType:(workflowEntry OR workflowStep) AND id:content\://xxx#* 225 StringBuilder query = new StringBuilder(); 226 query.append(DOCUMENT_TYPE).append(":(") 227 .append(TYPE_WF_ENTRY).append(" OR ").append(TYPE_WF_STEP) 228 .append(") AND id:").append(ClientUtils.escapeQueryChars(objectId + "#")).append("*"); 229 230 solrClient.deleteByQuery(_solrClientProvider.getCollectionName(workspaceName), query.toString()); 231 } 232 } 233 234}