001/* 002 * Copyright 2020 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.core.migration.action.impl; 017 018import java.io.IOException; 019import java.io.InputStream; 020import java.net.MalformedURLException; 021import java.util.Map; 022 023import javax.script.ScriptException; 024 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.commons.io.IOUtils; 031import org.apache.commons.lang3.StringUtils; 032import org.apache.excalibur.source.Source; 033import org.apache.excalibur.source.SourceNotFoundException; 034import org.apache.excalibur.source.SourceResolver; 035import org.slf4j.LoggerFactory; 036 037import org.ametys.core.migration.MigrationException; 038import org.ametys.core.migration.action.Action; 039import org.ametys.core.migration.action.data.ActionData; 040import org.ametys.core.migration.action.data.impl.ScriptActionData; 041import org.ametys.core.migration.version.Version; 042import org.ametys.plugins.core.ui.script.ScriptHandler; 043import org.ametys.runtime.plugin.component.AbstractLogEnabled; 044 045/** 046 * SQL action : A script will be executed 047 */ 048public class ScriptAction extends AbstractLogEnabled implements Action, Serviceable 049{ 050 /** Source resolver */ 051 protected SourceResolver _sourceResolver; 052 053 /** Script Handler */ 054 protected ScriptHandler _scriptHandler; 055 056 public void service(ServiceManager manager) throws ServiceException 057 { 058 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 059 _scriptHandler = (ScriptHandler) manager.lookup(ScriptHandler.ROLE); 060 } 061 062 public void doAction(ActionData actionData) throws MigrationException 063 { 064 getLogger().debug("Start initialization for : {}", actionData.toString()); 065 066 if (!(actionData instanceof ScriptActionData)) 067 { 068 throw new MigrationException("Script Upgrade can only be created for a Script upgrade, this is not the case for upgrade : " + actionData.toString()); 069 } 070 071 ScriptActionData scriptActionData = (ScriptActionData) actionData; 072 try 073 { 074 String script = getScript(scriptActionData); 075 076 Version version = actionData.getVersion(); 077 Map<String, Object> variables = Map.of( 078 "version", version, 079 "logger", LoggerFactory.getLogger(getLogger().getName() + "." + version.getComponentId()) 080 ); 081 082 // Execute the script in the admin workspace 083 Map<String, Object> scriptResult = _scriptHandler.executeScript(script, variables, "admin"); 084 if (!StringUtils.isAllBlank((String) scriptResult.get("error"), (String) scriptResult.get("message"), (String) scriptResult.get("stacktrace"))) 085 { 086 throw new MigrationException("Error while executing script to upgrade component '" + actionData.getVersion().getComponentId() + "' to version '" + actionData.getVersionNumber() + "'. Error : '" + scriptResult.get("error") + "', message : '" + scriptResult.get("message") + "', stacktrace : '" + scriptResult.get("stacktrace") + "'"); 087 } 088 } 089 catch (ScriptException e) 090 { 091 throw new MigrationException("Error while executing script to upgrade : '" + actionData.toString() + "'", e); 092 } 093 094 getLogger().debug("End upgrade for : {}", actionData.toString()); 095 } 096 097 /** 098 * Get the content of the script from the actionData 099 * @param scriptActionData actionData containing info about the upgrade 100 * @return The script as a String (either from the config or from a file to parse) 101 * @throws MigrationException Something went wrong 102 */ 103 protected String getScript(ScriptActionData scriptActionData) throws MigrationException 104 { 105 String script; 106 if (StringUtils.isNotBlank(scriptActionData.getScript())) 107 { 108 script = scriptActionData.getScript(); 109 } 110 else 111 { 112 String uri = "plugin:" + scriptActionData.getPlugin() + "://" + scriptActionData.getFile(); 113 Source source = null; 114 try 115 { 116 source = _sourceResolver.resolveURI(uri); 117 118 if (!source.exists()) 119 { 120 throw new SourceNotFoundException("URI '" + uri + "' does not exist."); 121 } 122 try (InputStream is = source.getInputStream()) 123 { 124 script = IOUtils.toString(is, "UTF-8"); 125 } 126 } 127 catch (MalformedURLException e) 128 { 129 throw new MigrationException("Impossible to find the script for the upgrade : " + scriptActionData.toString(), e); 130 } 131 catch (IOException e) 132 { 133 throw new MigrationException("Impossible to run the upgrade : " + scriptActionData.toString(), e); 134 } 135 finally 136 { 137 if (source != null) 138 { 139 _sourceResolver.release(source); 140 } 141 } 142 } 143 return "function main() { \n " + script + " \n }"; 144 } 145 146 public ActionData generateActionData(String id, Version version, String comment, String from, String type, String pluginName, Configuration configuration) throws MigrationException, ConfigurationException 147 { 148 return new ScriptActionData(id, version, comment, from, type, pluginName, configuration); 149 } 150}