001/*
002 *  Copyright 2021 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.plugins.workflow.definition;
017
018import java.io.File;
019import java.io.IOException;
020import java.io.InputStream;
021import java.util.Optional;
022
023import org.apache.avalon.framework.component.Component;
024import org.apache.avalon.framework.configuration.Configurable;
025import org.apache.avalon.framework.configuration.Configuration;
026import org.apache.avalon.framework.configuration.ConfigurationException;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.excalibur.source.Source;
031import org.apache.excalibur.source.SourceResolver;
032
033import org.ametys.plugins.workflow.support.I18nHelper;
034import org.ametys.plugins.workflow.support.WorkflowHelper;
035import org.ametys.runtime.i18n.I18nizableText;
036import org.ametys.runtime.plugin.component.AbstractLogEnabled;
037import org.ametys.runtime.plugin.component.PluginAware;
038
039import com.opensymphony.workflow.loader.WorkflowDescriptor;
040import com.opensymphony.workflow.loader.WorkflowLoader;
041
042/**
043 * Object to describe a workflow.
044 */
045public class WorkflowDefinition extends AbstractLogEnabled implements Component, Serviceable, Configurable, PluginAware
046{
047    /** The source resolver */
048    protected SourceResolver _srcResolver;
049    
050    /** The workflow helper */
051    protected WorkflowHelper _workflowHelper;
052    
053    private String _configuredFile;
054    private I18nizableText _label;
055    private WorkflowDescriptor _descriptor;
056    private String _id;
057    private String _pluginName;
058    
059    public void service(ServiceManager manager) throws ServiceException
060    {
061        _srcResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
062        _workflowHelper = (WorkflowHelper) manager.lookup(WorkflowHelper.ROLE);
063    }
064    
065    public void configure(Configuration configuration) throws ConfigurationException
066    {
067        _configuredFile = configuration.getChild("file").getValue(null);
068        
069        String defaultCatalog = getDefaultCatalog();
070        I18nizableText defaultValue = new I18nizableText(defaultCatalog, I18nHelper.buildI18nWorkflowKey(_id));
071        _label = I18nizableText.parseI18nizableText(configuration.getChild("label"), defaultCatalog, defaultValue);
072        // Lazy loaded
073        _descriptor = null;
074    }
075    
076    /**
077     * Get the default catalog for the workflow name.
078     * @return the default catalog
079     */
080    public String getDefaultCatalog()
081    {
082        return "plugin." + _pluginName;
083    }
084    
085    public void setPluginInfo(String pluginName, String featureName, String id)
086    {
087        _id = id;
088        _pluginName = pluginName;
089    }
090    
091    /**
092     * Get the workflow label.
093     * @return The workflow label, default is plugin.[pluginName]:WORKFLOW_[workflowNameUpperCased]
094     */
095    public I18nizableText getLabel()
096    {
097        return _label;
098    }
099    
100    /**
101     * Get the location of the workflow file.
102     * @return the location of the workflow file
103     */
104    public String getLocation()
105    {
106        return _getFilePathFromParam(_id)                                   // Search in WEB-INF/param/workflows/[workflowId].xml
107                .or(() -> Optional.ofNullable(_configuredFile))             // Then in the path defined by the "file" child of the configuration if defined
108                .orElseGet(() -> _getFilePathFromPlugin(_pluginName, _id)); // Then in [plugin]/workflows/[workflowId].xml
109    }
110    
111    /**
112     * Get or load the workflow descriptor.
113     * @param validate if the descriptor has to be valid
114     * @return the workflow descriptor
115     * @throws Exception if an error occurs
116     */
117    public WorkflowDescriptor getDescriptor(boolean validate) throws Exception
118    {
119        if (_descriptor == null)
120        {
121            try (InputStream is = getDefinition())
122            {
123                _descriptor = WorkflowLoader.load(is, validate);
124            }
125            
126            _descriptor.setName(_id);
127        }
128        
129        return _descriptor;
130    }
131    
132    /**
133     * Get the workflow definition
134     * @return the workflow definition
135     * @throws IOException if an error occurred
136     */
137    protected InputStream getDefinition() throws IOException
138    {
139        String location = getLocation();
140        getLogger().debug("Loading '{}' for the workflow '{}'.", location, _id);
141        
142        Source source = null;
143        
144        try
145        {
146            source = _srcResolver.resolveURI(location);
147            return source.getInputStream();
148        }
149        finally
150        {
151            _srcResolver.release(source);
152        }
153        
154    }
155    
156    /**
157     * Method to reset the workflow.
158     */
159    public void reset()
160    {
161        _descriptor = null;
162    }
163    
164    private Optional<String> _getFilePathFromParam(String workflowId)
165    {
166        return Optional.of(workflowId)
167                .map(id -> id + ".xml")
168                .map(filename -> new File(_workflowHelper.getParamWorkflowDir(), filename))
169                .filter(File::exists)
170                .filter(File::isFile)
171                // Transform it to URI with context://
172                .map(file -> "context://WEB-INF/param/workflows/" + file.getName());
173    }
174    
175    private String _getFilePathFromPlugin(String pluginName, String workflowId)
176    {
177        return "plugin:" + pluginName + "://workflows/" + workflowId + ".xml";
178    }
179}