/* Index ECM Engine - A system for managing the capture (when created
* or received), classification (cataloguing), storage, retrieval,
* revision, sharing, reuse and disposition of documents.
*
* Copyright (C) 2008 Regione Piemonte
* Copyright (C) 2008 Provincia di Torino
* Copyright (C) 2008 Comune di Torino
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package it.doqui.index.ecmengine.business.job.model;
import static it.doqui.index.ecmengine.util.EcmEngineConstants.*;
import it.doqui.index.ecmengine.util.EcmEngineConstants;
import it.doqui.index.ecmengine.business.job.JobBusinessInterface;
import it.doqui.index.ecmengine.business.personalization.multirepository.Repository;
import it.doqui.index.ecmengine.business.personalization.multirepository.RepositoryManager;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.RepoModelDefinition;
import org.alfresco.repo.dictionary.DictionaryModelType;
import org.alfresco.repo.dictionary.RepositoryLocation;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.Tenant;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.admin.RepoAdminService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class CustomModelActivationJob implements Job {
private static Log logger = LogFactory.getLog(ECMENGINE_JOB_MODEL_LOG_CATEGORY);
private static boolean running = false;
private RepoAdminService repoAdminService;
private SearchService searchService;
private NodeService nodeService ;
private TransactionService transactionService;
private NamespaceService namespaceService;
private TenantAdminService tenantAdminService;
private RepositoryLocation repoModelsLocation;
private DictionaryModelType dictionaryModelType;
private JobBusinessInterface jobManager;
public void execute(JobExecutionContext context) throws JobExecutionException {
logger.debug("[CustomModelActivationJob::execute] BEGIN");
// MB abilito il singleton del JOB per decongestionare ECMEngine in caso di troppe esecuzioni parallele
if (!running) {
synchronized (this) {
if (!running) {
running = true;
} else {
logger.debug("[CustomModelActivationJob::execute] job already running");
logger.debug("[CustomModelActivationJob::execute] END");
return;
}
}
} else {
logger.debug("[CustomModelActivationJob::execute] job already running");
logger.debug("[CustomModelActivationJob::execute] END");
return;
}
//final String originalRepoId = RepositoryManager.getCurrentRepository();
try {
repoAdminService = (RepoAdminService)context.getJobDetail().getJobDataMap().get(ECMENGINE_REPO_ADMIN_SERVICE_BEAN);
searchService = (SearchService)context.getJobDetail().getJobDataMap().get(ECMENGINE_SEARCH_SERVICE_BEAN);
nodeService = (NodeService)context.getJobDetail().getJobDataMap().get(ECMENGINE_NODE_SERVICE_BEAN);
transactionService = (TransactionService)context.getJobDetail().getJobDataMap().get(ECMENGINE_TRANSACTION_SERVICE_BEAN);
namespaceService = (NamespaceService)context.getJobDetail().getJobDataMap().get(ECMENGINE_NAMESPACE_SERVICE_BEAN);
tenantAdminService = (TenantAdminService)context.getJobDetail().getJobDataMap().get(ECMENGINE_TENANT_ADMIN_SERVICE_BEAN);
repoModelsLocation = (RepositoryLocation)context.getJobDetail().getJobDataMap().get(ECMENGINE_CUSTOM_MODEL_LOCATION_BEAN);
dictionaryModelType = (DictionaryModelType)context.getJobDetail().getJobDataMap().get(ECMENGINE_DICTIONARY_MODEL_TYPE_BEAN);
jobManager = (JobBusinessInterface)context.getJobDetail().getJobDataMap().get(ECMENGINE_JOB_MANAGER_BEAN);
for (Repository repository : RepositoryManager.getInstance().getRepositories()) {
RepositoryManager.setCurrentRepository(repository.getId());
if (logger.isDebugEnabled()) {
logger.debug("[CustomModelActivationJob::execute] current repository: "+RepositoryManager.getCurrentRepository());
}
AuthenticationUtil.setSystemUserAsCurrentUser();
// Se su quel repository non ho dei tenant in creazione o delete
if( !isTenantJobRunning() ) {
List<RepoModelDefinition> models = repoAdminService.getModels();
for (final RepoModelDefinition model : models) {
if (logger.isDebugEnabled()) {
logger.debug("[CustomModelActivationJob::execute] repository admin current model: "+model.toString());
}
// MB
// E' stato segnalato un problema di reploy dei singoli custom model
// Il caso e' quello di custom model a cascata, dipendenti l'uno dall'altro
// In quel caso, se non sono stati deployate le dipendenze c'e' un errore di
// org.alfresco.service.cmr.dictionary.DictionaryException: Failed to compile model acta-arc:model]
// In questo modo, con dei tentativi di deploy dei singoli custom model, dovremmo riuscire a fare un deploy
// incrementale
// Strano cmq perche' processModel e' in un try-catch
try {
final RetryingTransactionCallback<Object> processModelCallback = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Throwable
{
processModel(model);
return null;
}
};
transactionService.getRetryingTransactionHelper().doInTransaction(processModelCallback, false);
} catch(org.alfresco.service.cmr.dictionary.DictionaryException de) {
logger.error("[CustomModelActivationJob::execute] ERROR", de);
}
}
// Check all tenants
List<Tenant> tenantList = tenantAdminService.getAllTenants();
for (Tenant tenant : tenantList) {
// MB: evito che un errore di deploy su un Tenant mi invalidi il deploy del CustomModel su un altro tenant
try {
if (tenant.isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("[CustomModelActivationJob::execute] processing tenant '"+tenant.getTenantDomain()+"', repository: "+RepositoryManager.getCurrentRepository());
}
// MB; Su ogni tenant
RunAsWork<Object> tenantWork = new RunAsWork<Object>() {
public Object doWork() throws Exception {
// MB: Per ogni model del tenant
List<RepoModelDefinition> tenantModels = repoAdminService.getModels();
for (final RepoModelDefinition model : tenantModels) {
// MB: Processo i content model, in entry catch
final RetryingTransactionCallback<Object> processTenantModelCallback = new RetryingTransactionCallback<Object>() {
public Object execute() throws Throwable {
if (logger.isDebugEnabled()) {
logger.debug("[CustomModelActivationJob::execute] tenant current model: "+model.toString());
}
processModel(model);
return null;
}
};
// MB:
// E' stato segnalato un problema di reploy dei singoli custom model
// Il caso e' quello di custom model a cascata, dipendenti l'uno dall'altro
// In quel caso, se non sono stati deployate le dipendenze c'e' un errore di
// org.alfresco.service.cmr.dictionary.DictionaryException: Failed to compile model acta-arc:model]
// In questo modo, con dei tentativi di deploy dei singoli custom model, dovremmo riuscire a fare un deploy
// incrementale
try {
transactionService.getRetryingTransactionHelper().doInTransaction(processTenantModelCallback, false);
} catch(org.alfresco.service.cmr.dictionary.DictionaryException de) {
logger.error("[CustomModelActivationJob::execute] ERROR", de);
}
}
return null;
}
};
AuthenticationUtil.runAs(tenantWork, TenantService.ADMIN_BASENAME+TenantService.SEPARATOR+tenant.getTenantDomain());
}
} catch(org.alfresco.service.cmr.repository.InvalidStoreRefException isre) {
logger.error("[CustomModelActivationJob::execute] invalid store su tenant '"+tenant.getTenantDomain()+"', repository: "+RepositoryManager.getCurrentRepository());
//MB: Evitiamo di dare lo stack. E' un problema che si evidenzia in caso di TENANT Rotto
//logger.error("[CustomModelActivationJob::execute] ERROR", isre);
} catch(Exception e) {
logger.error("[CustomModelActivationJob::execute] Exception su tenant '"+tenant.getTenantDomain()+"', repository: "+RepositoryManager.getCurrentRepository());
//MB: L'errore lo passo solo in DEBUG. Altrimenti su tenant rotti abbiamo tonnellate di stack trace
logger.debug("[CustomModelActivationJob::execute] ERROR", e);
}
}
}
}
} catch(Exception e) {
logger.error("[CustomModelActivationJob::execute] ERROR", e);
throw new JobExecutionException(e);
} finally {
running = false;
//RepositoryManager.setCurrentRepository(originalRepoId);
logger.debug("[CustomModelActivationJob::execute] END ");
}
}
private void processModel(final RepoModelDefinition model) throws Exception {
logger.debug("[CustomModelActivationJob::processModel] BEGIN");
try {
if (logger.isDebugEnabled()) {
logger.debug("[CustomModelActivationJob::processModel] processing model: "+model.getRepoName());
logger.debug("[CustomModelActivationJob::processModel] current model: "+model.toString());
}
String modelFileName = model.getRepoName();
StoreRef storeRef = repoModelsLocation.getStoreRef();
// MB: Provo a prendere il root note. Nei Tenant in costruzione non e' ancora creato
NodeRef rootNode = null;
try {
rootNode = nodeService.getRootNode(storeRef);
} catch(Exception e) {
logger.error("[CustomModelActivationJob::processModel] root node non trovato " +storeRef);
}
// MB: controllo l'esistenza del root node
if( rootNode!=null ){
List<NodeRef> nodeRefs = searchService.selectNodes(rootNode, repoModelsLocation.getPath()+"//.[@cm:name='"+modelFileName+"' and subtypeOf('cm:dictionaryModel')]", null, namespaceService, false);
// Verifico quanti model vengono trovati
if (nodeRefs.size() == 0) {
logger.error("[CustomModelActivationJob::processModel] Could not find custom model " + modelFileName);
} else if (nodeRefs.size() > 1) {
// unexpected: should not find multiple nodes with same name
logger.error("[CustomModelActivationJob::processModel] Found multiple custom models " + modelFileName);
} else {
NodeRef modelNodeRef = nodeRefs.get(0);
boolean isActive = false;
Boolean value = (Boolean)nodeService.getProperty(modelNodeRef, ContentModel.PROP_MODEL_ACTIVE);
if (value != null)
{
isActive = value.booleanValue();
}
// Note: dictionaryModelType.onContentUpdate() generates a refresh event on
// DictionaryDAO in order to load models.
if (model.getModel() == null && isActive) {
logger.debug("[CustomModelActivationJob::processModel] model "+modelFileName+" is not active: activating...");
dictionaryModelType.onContentUpdate(modelNodeRef, true);
logger.debug("[CustomModelActivationJob::processModel] model "+modelFileName+" activated");
} else if (model.getModel() != null && !isActive) {
logger.debug("[CustomModelActivationJob::processModel] model "+modelFileName+" is active: deactivating...");
dictionaryModelType.onContentUpdate(modelNodeRef, true);
logger.debug("[CustomModelActivationJob::processModel] model "+modelFileName+" deactivated");
}
}
}
} catch(Exception e) {
logger.error("[CustomModelActivationJob::processModel] ERROR", e);
} finally {
logger.debug("[CustomModelActivationJob::processModel] END");
}
}
private boolean isTenantJobRunning(){
// Fermo l'IndexTransactionTracker se e' attivo il job di creazione dei tenant
boolean isTenantBootstrapRunning = false;
// Fermo l'IndexTransactionTracker se e' attivo il job di delete dei tenant
boolean isTenantDeleteRunning = false;
try {
try {
isTenantBootstrapRunning = jobManager.isExecuting(EcmEngineConstants.ECMENGINE_TENANT_ADMIN_JOB_REF);
} catch (Exception e) {
// Riportiamo semplicemente un warning
logger.warn("[CustomModelActivationJob::execute] Exception accessing Job DAO on repository (" +RepositoryManager.getCurrentRepository() +")", e);
}
if (isTenantBootstrapRunning) {
// ECM Engine sta eseguendo il bootstrap di un nuovo tenant. Sospendiamo momentaneamente l'index tracking.
logger.info("[CustomModelActivationJob::execute] Tenant bootstrap running on repository (" +RepositoryManager.getCurrentRepository() +") skipping CustomModelActivationJob.");
}
try {
isTenantDeleteRunning = jobManager.isExecuting(EcmEngineConstants.ECMENGINE_TENANT_DELETE_JOB_REF);
} catch (Exception e) {
// Riportiamo semplicemente un warning
logger.warn("[CustomModelActivationJob::execute] Exception accessing Job DAO on repository (" +RepositoryManager.getCurrentRepository() +")", e);
}
if (isTenantDeleteRunning) {
// ECM Engine sta eseguendo il bootstrap di un nuovo tenant. Sospendiamo momentaneamente l'index tracking.
logger.info("[CustomModelActivationJob::execute] Tenant delete running on repository (" +RepositoryManager.getCurrentRepository() +") skipping CustomModelActivationJob.");
}
} catch (Exception e) {
// Riportiamo semplicemente un warning
logger.warn("[CustomModelActivationJob::execute] Exception on repository (" +RepositoryManager.getCurrentRepository() +")", e);
}
return (isTenantBootstrapRunning || isTenantDeleteRunning);
}
}