/** * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * * The Apereo Foundation licenses this file to you under the Educational * Community License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of the License * at: * * http://opensource.org/licenses/ecl2.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.opencastproject.workflow.impl; import static org.opencastproject.util.ReadinessIndicator.ARTIFACT; import org.opencastproject.util.ReadinessIndicator; import org.opencastproject.workflow.api.WorkflowDefinition; import org.opencastproject.workflow.api.WorkflowParser; import org.apache.commons.io.IOUtils; import org.apache.felix.fileinstall.ArtifactInstaller; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.InputStream; import java.util.ArrayList; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; /** * Loads, unloads, and reloads {@link WorkflowDefinition}s from "*workflow.xml" files in any of fileinstall's watch * directories. */ public class WorkflowDefinitionScanner implements ArtifactInstaller { private static final Logger logger = LoggerFactory.getLogger(WorkflowDefinitionScanner.class); /** An internal collection of workflows that we have installed */ protected Map<String, WorkflowDefinition> installedWorkflows = new HashMap<String, WorkflowDefinition>(); /** An internal collection of artifact id, bind the workflow definition files and their id */ protected Map<File, String> artifactIds = new HashMap<File, String>(); /** List of artifact parsed with error */ protected List<File> artifactsWithError = new ArrayList<File>(); /** OSGi bundle context */ private BundleContext bundleCtx = null; /** Tag to define if the the workflows definition have already been loaded */ private boolean isWFSinitiliazed = false; /** The current workflow definition being installed */ private WorkflowDefinition currentWFD = null; /** * OSGi callback on component activation. private boolean initialized = true; * * /** OSGi callback on component activation. * * @param ctx * the bundle context */ void activate(BundleContext ctx) { this.bundleCtx = ctx; } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactInstaller#install(java.io.File) */ public void install(File artifact) throws Exception { WorkflowDefinition def = currentWFD; // If the current workflow definition is null, it means this is a first install and not an update... if (def == null) { // ... so we have to load the definition first def = parseWorkflowDefinitionFile(artifact); if (def == null) { logger.warn("Unable to install workflow from '{}'", artifact.getName()); artifactsWithError.add(artifact); return; } } logger.debug("Installing workflow from file '{}'", artifact.getName()); artifactsWithError.remove(artifact); artifactIds.put(artifact, def.getId()); putWorkflowDefinition(def.getId(), def); // Determine the number of available profiles String[] filesInDirectory = artifact.getParentFile().list(new FilenameFilter() { public boolean accept(File arg0, String name) { return name.endsWith(".xml"); } }); logger.info("Worfkflow definition '{}' from file '{}' installed", def.getId(), artifact.getName()); // Once all profiles have been loaded, announce readiness if ((filesInDirectory.length - artifactsWithError.size()) == artifactIds.size() && !isWFSinitiliazed) { logger.info("{} Workflow definitions loaded, activating Workflow service", filesInDirectory.length - artifactsWithError.size()); Dictionary<String, String> properties = new Hashtable<String, String>(); properties.put(ARTIFACT, "workflowdefinition"); logger.debug("Indicating readiness of workflow definitions"); bundleCtx.registerService(ReadinessIndicator.class.getName(), new ReadinessIndicator(), properties); isWFSinitiliazed = true; } } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactInstaller#uninstall(java.io.File) */ public void uninstall(File artifact) throws Exception { // Since the artifact is gone, we can't open it to read its ID. So we look in the local map. String id = artifactIds.remove(artifact); if (id != null) { WorkflowDefinition def = removeWorkflowDefinition(id); logger.info("Uninstalling workflow definition '{}' from file '{}'", def.getId(), artifact.getName()); } } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactInstaller#update(java.io.File) */ public void update(File artifact) throws Exception { currentWFD = parseWorkflowDefinitionFile(artifact); if (currentWFD != null) { uninstall(artifact); install(artifact); currentWFD = null; } } /** * Parse the given workflow definition file and return the related workflow definition * * @param artifact * The workflow definition file to parse * @return the workflow definition if the given contained a valid one, or null if the file can not be parsed. */ public WorkflowDefinition parseWorkflowDefinitionFile(File artifact) { InputStream stream = null; try { stream = new FileInputStream(artifact); WorkflowDefinition def = WorkflowParser.parseWorkflowDefinition(stream); if (def.getOperations().size() == 0) logger.warn("Workflow '{}' has no operations", def.getId()); return def; } catch (Exception e) { logger.warn("Unable to parse workflow from file '{}', {}", artifact.getName(), e.getMessage()); return null; } finally { IOUtils.closeQuietly(stream); } } /** * Gets the workflow definitions with the given id. * * @param id * @return the workflow definition if exist or null */ public WorkflowDefinition getWorkflowDefinition(String id) { return installedWorkflows.get(id); } /** * Get the list of installed workflow definitions. * * @return the collection of installed workflow definitions id */ public Map<String, WorkflowDefinition> getWorkflowDefinitions() { return installedWorkflows; } /** * Add the given workflow definition to the installed workflow definition id. * * @param id * the id of the workflow definition to add * @param wfd * the workflow definition id */ public void putWorkflowDefinition(String id, WorkflowDefinition wfd) { installedWorkflows.put(id, wfd); } /** * Remove the workflow definition with the given id from the installed definition list. * * @param id * the workflow definition id * @return the removed workflow definition */ public WorkflowDefinition removeWorkflowDefinition(String id) { return installedWorkflows.remove(id); } /** * {@inheritDoc} * * @see org.apache.felix.fileinstall.ArtifactListener#canHandle(java.io.File) */ public boolean canHandle(File artifact) { return "workflows".equals(artifact.getParentFile().getName()) && artifact.getName().endsWith(".xml"); } }