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.util.Optional; 020 021import org.apache.avalon.framework.configuration.Configuration; 022import org.apache.avalon.framework.configuration.ConfigurationException; 023import org.apache.avalon.framework.configuration.DefaultConfiguration; 024import org.apache.avalon.framework.configuration.MutableConfiguration; 025import org.apache.avalon.framework.context.ContextException; 026import org.apache.cocoon.Constants; 027import org.apache.cocoon.environment.Context; 028import org.apache.commons.lang3.StringUtils; 029 030import org.ametys.runtime.plugin.component.AbstractThreadSafeComponentExtensionPoint; 031 032/** 033 * The {@link WorkflowDefinition} extension point to list all available workflows. 034 */ 035public class WorkflowDefinitionExtensionPoint extends AbstractThreadSafeComponentExtensionPoint<WorkflowDefinition> 036{ 037 /** The Avalon role */ 038 public static final String ROLE = WorkflowDefinitionExtensionPoint.class.getName(); 039 040 /** The Cocoon context */ 041 protected Context _cocoonContext; 042 043 @Override 044 public void contextualize(org.apache.avalon.framework.context.Context context) throws ContextException 045 { 046 super.contextualize(context); 047 _cocoonContext = (Context) _context.get(Constants.CONTEXT_ENVIRONMENT_CONTEXT); 048 } 049 050 @Override 051 public void initializeExtensions() throws Exception 052 { 053 /* Add custom workflow found in WEB-INF/param/workflows and ignore those which are already defined in the kernel */ 054 055 // List workflow files 056 File[] workflowFiles = _getParamWorkflowDir().listFiles((file, name) -> name.endsWith(".xml")); 057 058 if (workflowFiles != null) 059 { 060 for (File workflowFile : workflowFiles) 061 { 062 String workflowId = _getWorkflowIdFromFilename(workflowFile.getName()); 063 064 // If the workflow definition is not already declared, add it to known extensions 065 if (!hasExtension(workflowId)) 066 { 067 DefaultConfiguration extensionConf = new DefaultConfiguration("extension"); 068 extensionConf.setAttribute("class", WorkflowDefinition.class.getName()); 069 070 // Set null to the configuration because the configuration is get into the method 071 addExtension(workflowId, "unknown", "unknown", extensionConf); 072 } 073 } 074 } 075 076 super.initializeExtensions(); 077 } 078 079 @Override 080 public void addExtension(String id, String pluginName, String featureName, Configuration configuration) throws ConfigurationException 081 { 082 DefaultConfiguration extensionConf = new DefaultConfiguration(configuration, true); 083 084 MutableConfiguration workflowFileConf = extensionConf.getMutableChild("file", true); 085 086 // Search in WEB-INF/param/workflows/[workflowId].xml 087 String filepath = _getFilePathFromParam(id) 088 // Then in the path defined by the "file" child of the configuration if defined 089 .or(() -> _getFilePathFromConf(workflowFileConf)) 090 // Then in [plugin]/workflows/[workflowId].xml 091 .orElseGet(() -> _getFilePathFromPlugin(pluginName, id)); 092 093 workflowFileConf.setValue(filepath); 094 095 super.addExtension(id, pluginName, featureName, extensionConf); 096 } 097 098 private Optional<String> _getFilePathFromParam(String workflowId) 099 { 100 return Optional.of(workflowId) 101 .map(id -> id + ".xml") 102 .map(filename -> new File(_getParamWorkflowDir(), filename)) 103 .filter(File::exists) 104 .filter(File::isFile) 105 // Transform it to URI with context:// 106 .map(file -> "context://WEB-INF/param/workflows/" + file.getName()); 107 } 108 109 private Optional<String> _getFilePathFromConf(Configuration workflowFileConf) 110 { 111 return Optional.of(workflowFileConf) 112 .map(conf -> workflowFileConf.getValue(null)) 113 .filter(StringUtils::isNotBlank); 114 } 115 116 private String _getFilePathFromPlugin(String pluginName, String workflowId) 117 { 118 return "plugin:" + pluginName + "://workflows/" + workflowId + ".xml"; 119 } 120 121 /** 122 * Get the param workflows directory. 123 * @return the workflows directory in WEB-INF/param. 124 */ 125 protected File _getParamWorkflowDir() 126 { 127 return new File(_cocoonContext.getRealPath("/WEB-INF/param/workflows")); 128 } 129 130 /** 131 * Get the workflow ID from the workflow filename. 132 * @param filename The workflow filename 133 * @return the workflow ID 134 */ 135 protected String _getWorkflowIdFromFilename(String filename) 136 { 137 return filename.substring(0, filename.lastIndexOf('.')); 138 } 139}