All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bonitasoft.engine.api.impl.resolver.DependencyResolver Maven / Gradle / Ivy

/**
 * Copyright (C) 2013 BonitaSoft S.A.
 * BonitaSoft, 32 rue Gustave Eiffel - 38000 Grenoble
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation
 * version 2.1 of the License.
 * This library 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 Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 **/
package org.bonitasoft.engine.api.impl.resolver;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.io.FileUtils;
import org.bonitasoft.engine.api.ProcessAPI;
import org.bonitasoft.engine.api.impl.transaction.dependency.AddSDependency;
import org.bonitasoft.engine.bpm.bar.BusinessArchive;
import org.bonitasoft.engine.bpm.process.ConfigurationState;
import org.bonitasoft.engine.bpm.process.ProcessDeployException;
import org.bonitasoft.engine.commons.exceptions.SBonitaException;
import org.bonitasoft.engine.commons.transaction.TransactionExecutor;
import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;
import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;
import org.bonitasoft.engine.core.process.definition.model.SProcessDefinitionDeployInfo;
import org.bonitasoft.engine.dependency.DependencyService;
import org.bonitasoft.engine.dependency.SDependencyCreationException;
import org.bonitasoft.engine.dependency.SDependencyException;
import org.bonitasoft.engine.dependency.model.SDependency;
import org.bonitasoft.engine.dependency.model.builder.DependencyBuilderAccessor;
import org.bonitasoft.engine.exception.BonitaException;
import org.bonitasoft.engine.exception.BonitaHomeNotSetException;
import org.bonitasoft.engine.exception.BonitaRuntimeException;
import org.bonitasoft.engine.home.BonitaHomeServer;
import org.bonitasoft.engine.log.technical.TechnicalLogSeverity;
import org.bonitasoft.engine.log.technical.TechnicalLoggerService;
import org.bonitasoft.engine.persistence.QueryOptions;
import org.bonitasoft.engine.recorder.model.EntityUpdateDescriptor;
import org.bonitasoft.engine.service.TenantServiceAccessor;
import org.bonitasoft.engine.transaction.STransactionException;

/**
 * Handles the resolution of Process Dependencies. A process can have a list of ProcessDependencyResolvers which validates different aspects of the
 * process to validate (or "resolve")
 * 
 * @author Emmanuel Duchastenier
 * @author Matthieu Chaffotte
 */
public class DependencyResolver {

    private final List dependencyResolvers;

    public DependencyResolver(final List dependencyResolvers) {
        this.dependencyResolvers = dependencyResolvers;
    }

    public boolean resolveDependencies(final ProcessAPI processAPI, final BusinessArchive businessArchive, final TenantServiceAccessor tenantAccessor,
            final SProcessDefinition sDefinition) throws ProcessDeployException {
        final List resolvers = getResolvers();
        boolean resolved = true;
        for (final ProcessDependencyResolver resolver : resolvers) {
            try {
                resolved &= resolver.resolve(processAPI, tenantAccessor, businessArchive, sDefinition);
            } catch (final BonitaException e) {
                // not logged, we will check later why the process is not resolved
                resolved = false;
            }
        }
        return resolved;
    }

    /*
     * Done in a separated transaction
     * We try here to check if now the process is resolved so it must not be done in the same transaction that did the modification
     * this does not throw exception, it only log because it can be retried after.
     */
    public void resolveDependencies(final long processDefinitionId, final TenantServiceAccessor tenantAccessor) throws SBonitaException {
        final List resolvers = getResolvers();
        resolveDependencies(processDefinitionId, tenantAccessor, resolvers.toArray(new ProcessDependencyResolver[resolvers.size()]));
    }

    public void resolveDependencies(final long processDefinitionId, final TenantServiceAccessor tenantAccessor, final ProcessDependencyResolver... resolvers) {
        final TransactionExecutor transactionExecutor = tenantAccessor.getTransactionExecutor();
        final TechnicalLoggerService loggerService = tenantAccessor.getTechnicalLoggerService();
        final ProcessDefinitionService processDefinitionService = tenantAccessor.getProcessDefinitionService();
        final DependencyService dependencyService = tenantAccessor.getDependencyService();
        final DependencyBuilderAccessor dependencyBuilderAccessor = tenantAccessor.getDependencyBuilderAccessor();
        try {
            final boolean txOpened = transactionExecutor.openTransaction();
            try {
                boolean resolved = true;
                for (final ProcessDependencyResolver dependencyResolver : resolvers) {
                    final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);
                    resolved &= dependencyResolver.checkResolution(tenantAccessor, processDefinition).isEmpty();
                }
                changeResolutionStatus(processDefinitionId, tenantAccessor, processDefinitionService, dependencyService, dependencyBuilderAccessor, resolved);
            } catch (final SBonitaException e) {
                transactionExecutor.setTransactionRollback();
                loggerService.log(DependencyResolver.class, TechnicalLogSeverity.DEBUG, e);
                loggerService.log(DependencyResolver.class, TechnicalLogSeverity.WARNING, "Unable to resolve dependencies after they were modified because of "
                        + e.getMessage() + ". Please retry it manually");
            } catch (final BonitaHomeNotSetException e) {
                throw new BonitaRuntimeException("Bonita home not set", e);
            } finally {
                transactionExecutor.completeTransaction(txOpened);
            }
        } catch (final STransactionException e) {
            loggerService.log(DependencyResolver.class, TechnicalLogSeverity.DEBUG, e);
            loggerService.log(DependencyResolver.class, TechnicalLogSeverity.WARNING,
                    "Unable to resolve dependencies after they were modified. Please retry it manually");
        }
    }

    private void changeResolutionStatus(final long processDefinitionId, final TenantServiceAccessor tenantAccessor,
            final ProcessDefinitionService processDefinitionService, final DependencyService dependencyService,
            final DependencyBuilderAccessor dependencyBuilderAccessor, final boolean resolved) throws SBonitaException, BonitaHomeNotSetException {
        final SProcessDefinitionDeployInfo processDefinitionDeployInfo = processDefinitionService.getProcessDeploymentInfo(processDefinitionId);
        if (resolved) {
            if (ConfigurationState.UNRESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) {
                resolveAndCreateDependencies(
                        new File(new File(BonitaHomeServer.getInstance().getProcessesFolder(tenantAccessor.getTenantId())), String.valueOf(processDefinitionId)),
                        processDefinitionService, dependencyService, dependencyBuilderAccessor, processDefinitionId);
            }
        } else {
            if (ConfigurationState.RESOLVED.name().equals(processDefinitionDeployInfo.getConfigurationState())) {
                final EntityUpdateDescriptor updateDescriptor = tenantAccessor.getBPMDefinitionBuilders().getProcessDefinitionDeployInfoUpdateBuilder()
                        .updateConfigurationState(ConfigurationState.UNRESOLVED).done();
                processDefinitionService.updateProcessDefinitionDeployInfo(processDefinitionId, updateDescriptor);
            }
        }
    }

    /**
     * create dependencies based on the bonita home (the process folder)
     * 
     * @param processFolder
     * @param processDefinitionService
     * @param dependencyService
     * @param dependencyBuilderAccessor
     * @param processDefinitionId
     * @throws SBonitaException
     */
    public void resolveAndCreateDependencies(final File processFolder, final ProcessDefinitionService processDefinitionService,
            final DependencyService dependencyService, final DependencyBuilderAccessor dependencyBuilderAccessor, final long processDefinitionId)
            throws SBonitaException {
        final Map resources = new HashMap();

        final File file = new File(processFolder, "classpath");
        if (file.exists() && file.isDirectory()) {
            final File[] listFiles = file.listFiles();
            for (final File jarFile : listFiles) {
                final String name = jarFile.getName();
                try {
                    final byte[] jarContent = FileUtils.readFileToByteArray(jarFile);
                    resources.put(getDependencyName(processDefinitionId, name), jarContent);
                } catch (final IOException e) {
                    throw new SDependencyCreationException(e);
                }
            }
        }
        addDependencies(resources, dependencyService, dependencyBuilderAccessor, processDefinitionId);
        processDefinitionService.resolveProcess(processDefinitionId);
    }

    private String getDependencyName(final long processDefinitionId, final String name) {
        return processDefinitionId + "_" + name;
    }

    private void addDependencies(final Map resources, final DependencyService dependencyService,
            final DependencyBuilderAccessor dependencyBuilderAccessor, final long processDefinitionId) throws SBonitaException {
        final List dependencyIds = getDependencyMappingsOfProcess(dependencyService, processDefinitionId);
        final List dependencies = getDependenciesOfProcess(dependencyService, dependencyIds);

        final Iterator> iterator = resources.entrySet().iterator();
        while (iterator.hasNext()) {
            final Map.Entry entry = iterator.next();
            if (!dependencies.contains(entry.getKey())) {
                addDependency(entry.getKey(), entry.getValue(), dependencyService, dependencyBuilderAccessor, processDefinitionId);
            }
        }

    }

    private List getDependenciesOfProcess(final DependencyService dependencyService, final List dependencyIds) throws SBonitaException {
        if (dependencyIds.isEmpty()) {
            return Collections.emptyList();
        }
        final List dependencies = dependencyService.getDependencies(dependencyIds);
        final ArrayList dependencyNames = new ArrayList(dependencies.size());
        for (final SDependency sDependency : dependencies) {
            dependencyNames.add(sDependency.getName());
        }
        return dependencyNames;
    }

    private List getDependencyMappingsOfProcess(final DependencyService dependencyService, final long processDefinitionId) throws SDependencyException {
        final List dependencyIds = new ArrayList();
        QueryOptions queryOptions = QueryOptions.defaultQueryOptions();
        List currentPage;
        do {
            currentPage = dependencyService.getDependencyIds(processDefinitionId, "process", queryOptions);
            dependencyIds.addAll(currentPage);
            queryOptions = QueryOptions.getNextPage(queryOptions);
        } while (currentPage.size() == QueryOptions.DEFAULT_NUMBER_OF_RESULTS);
        return dependencyIds;
    }

    /**
     * create dependencies based on the business archive
     * 
     * @param businessArchive
     * @param processDefinitionService
     * @param dependencyService
     * @param dependencyBuilderAccessor
     * @param sDefinition
     * @throws SBonitaException
     */
    public void resolveAndCreateDependencies(final BusinessArchive businessArchive, final ProcessDefinitionService processDefinitionService,
            final DependencyService dependencyService, final DependencyBuilderAccessor dependencyBuilderAccessor, final SProcessDefinition sDefinition)
            throws SBonitaException {
        final Long processDefinitionId = sDefinition.getId();
        if (businessArchive != null) {
            final Map resources = businessArchive.getResources("^classpath/.*$");

            // remove the classpath/ on path of dependencies
            final Map resourcesWithRealName = new HashMap(resources.size());
            for (final Entry resource : resources.entrySet()) {
                final String name = resource.getKey().substring(10);
                final byte[] jarContent = resource.getValue();
                resourcesWithRealName.put(getDependencyName(processDefinitionId, name), jarContent);
            }
            addDependencies(resourcesWithRealName, dependencyService, dependencyBuilderAccessor, sDefinition.getId());
        }
        processDefinitionService.resolveProcess(processDefinitionId);
    }

    private void addDependency(final String name, final byte[] jarContent, final DependencyService dependencyService,
            final DependencyBuilderAccessor dependencyBuilderAccessor, final long processdefinitionId) throws SDependencyException {
        final AddSDependency addSDependency = new AddSDependency(dependencyService, dependencyBuilderAccessor, name, jarContent, processdefinitionId, "process");
        addSDependency.execute();
    }

    public List getResolvers() {
        return dependencyResolvers;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy