/* * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache 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://www.apache.org/licenses/LICENSE-2.0 * * 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. * * Contributors: * bstefanescu, jcarsique */ package org.nuxeo.connect.update.task.standalone; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.commons.collections.MapUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.Environment; import org.nuxeo.common.utils.StringUtils; import org.nuxeo.connect.update.LocalPackage; import org.nuxeo.connect.update.PackageException; import org.nuxeo.connect.update.PackageState; import org.nuxeo.connect.update.PackageUpdateService; import org.nuxeo.connect.update.ValidationStatus; import org.nuxeo.connect.update.task.Task; import org.nuxeo.connect.update.task.update.UpdateManager; /** * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public abstract class AbstractTask implements Task { static final Log log = LogFactory.getLog(AbstractTask.class); public static final String PKG_ID = "package.id"; public static final String PKG_NAME = "package.name"; public static final String PKG_VERSION = "package.version"; public static final String PKG_ROOT = "package.root"; public static final String ENV_HOME = "env.home"; /** * @since 5.5 */ public static final String ENV_SERVER_HOME = "env.server.home"; /** * Set only on JBoss - the EAR root directory path */ public static final String ENV_EAR = "env.ear"; public static final String ENV_LIB = "env.lib"; public static final String ENV_SYSLIB = "env.syslib"; public static final String ENV_BUNDLES = "env.bundles"; public static final String ENV_CONFIG = "env.config"; /** * @since 5.5 */ public static final String ENV_TEMPLATES = "env.templates"; public static final String ENV_TIMESTAMP = "sys.timestamp"; /** * The host application name. * * @see Environment#getHostApplicationName() */ public static final String ENV_HOSTAPP_NAME = "env.hostapp.name"; /** * The host application version * * @see Environment#getHostApplicationVersion() */ public static final String ENV_HOSTAPP_VERSION = "env.hostapp.version"; protected boolean restart; protected LocalPackage pkg; protected String serverPathPrefix; protected UpdateManager updateMgr; protected boolean updateMgrLoaded = false; protected PackageUpdateService service; /** * A map of environment key/values that can be used in XML install files as variables. */ protected final Map<String, String> env; public AbstractTask(PackageUpdateService pus) { service = pus; env = new HashMap<>(); Environment nxenv = Environment.getDefault(); File serverHome = nxenv.getServerHome(); File nxHome = nxenv.getRuntimeHome(); File config = nxenv.getConfig(); serverPathPrefix = serverHome.getAbsolutePath(); if (!serverPathPrefix.endsWith(File.separator)) { serverPathPrefix = serverPathPrefix.concat(File.separator); } env.put(ENV_SERVER_HOME, serverHome.getAbsolutePath()); env.put(ENV_HOME, nxHome.getAbsolutePath()); env.put(ENV_CONFIG, config.getAbsolutePath()); env.put(ENV_HOSTAPP_NAME, nxenv.getHostApplicationName()); env.put(ENV_HOSTAPP_VERSION, nxenv.getHostApplicationVersion()); env.put(ENV_SYSLIB, new File(serverHome, "lib").getAbsolutePath()); if (nxenv.isJBoss()) { File ear = config.getParentFile(); env.put(ENV_EAR, ear.getAbsolutePath()); env.put(ENV_LIB, new File(ear, "lib").getAbsolutePath()); env.put(ENV_BUNDLES, new File(ear, "bundles").getAbsolutePath()); } else { env.put(ENV_LIB, new File(nxHome, "lib").getAbsolutePath()); env.put(ENV_BUNDLES, new File(nxHome, "bundles").getAbsolutePath()); } env.put(ENV_TEMPLATES, new File(serverHome, "templates").getAbsolutePath()); env.put(ENV_TIMESTAMP, new SimpleDateFormat("yyMMddHHmmss").format(new Date())); updateMgr = new UpdateManager(serverHome, service.getRegistry()); } public abstract boolean isInstallTask(); @Override @SuppressWarnings("hiding") public void initialize(LocalPackage pkg, boolean restart) throws PackageException { this.pkg = pkg; this.restart = restart; env.put(PKG_ID, pkg.getId()); env.put(PKG_NAME, pkg.getName()); env.put(PKG_VERSION, pkg.getVersion().toString()); env.put(PKG_ROOT, pkg.getData().getRoot().getAbsolutePath()); if (log.isDebugEnabled()) { final ByteArrayOutputStream out = new ByteArrayOutputStream(); final PrintStream outPrint = new PrintStream(out); MapUtils.debugPrint(outPrint, null, env); log.debug(out.toString()); } } /** * Get a file given its key in the environment map. If no key exists then null is returned. * * @param key */ public File getFile(String key) { String val = env.get(key); return val == null ? null : new File(val); } @Override public boolean isRestartRequired() { return restart; } @Override public LocalPackage getPackage() { return pkg; } protected Map<Object, Object> createContextMap(Map<String, String> params) { Map<Object, Object> map = new HashMap<>(System.getProperties()); map.putAll(env); if (params != null && !params.isEmpty()) { map.putAll(params); } return map; } protected String loadParametrizedFile(File file, Map<String, String> params) throws IOException { String content = FileUtils.readFileToString(file); // replace variables. return StringUtils.expandVars(content, createContextMap(params)); } protected void saveParams(Map<String, String> params) throws PackageException { if (params == null || params.isEmpty()) { return; } try { Properties props = new Properties(); props.putAll(params); File file = pkg.getData().getEntry(LocalPackage.INSTALL_PROPERTIES); FileOutputStream out = new FileOutputStream(file); try { props.store(out, "user install parameters"); } finally { out.close(); } } catch (IOException e) { throw new PackageException("Failed to save install parameters", e); } } @Override public synchronized void run(Map<String, String> params) throws PackageException { if (isInstallTask()) { LocalPackage oldpkg = service.getActivePackage(pkg.getName()); if (oldpkg != null) { if (oldpkg.getPackageState() == PackageState.INSTALLING) { throw new PackageException("Another package with the same name is installing: " + oldpkg.getName()); } else { // uninstall it. Task utask = oldpkg.getUninstallTask(); try { utask.run(new HashMap<String, String>()); } catch (PackageException e) { utask.rollback(); throw new PackageException("Failed to uninstall: " + oldpkg.getId() + ". Cannot continue installation of " + pkg.getId(), e); } } } } service.setPackageState(pkg, PackageState.INSTALLING); saveParams(params); doRun(params); taskDone(); if (updateMgrLoaded) { updateMgr.store(); } } public synchronized UpdateManager getUpdateManager() throws PackageException { if (!updateMgrLoaded) { updateMgr.load(); updateMgrLoaded = true; } return updateMgr; } protected abstract void rollbackDone() throws PackageException; protected abstract void taskDone() throws PackageException; @Override public void rollback() throws PackageException { try { doRollback(); } finally { rollbackDone(); } } @Override public void setRestartRequired(boolean isRestartRequired) { this.restart = isRestartRequired; } protected abstract void doRun(Map<String, String> params) throws PackageException; protected abstract void doRollback() throws PackageException; @Override public ValidationStatus validate() throws PackageException { ValidationStatus status = new ValidationStatus(); if (isInstallTask()) { validateInstall(status); } doValidate(status); return status; } public abstract void doValidate(ValidationStatus status) throws PackageException; protected LocalPackage validateInstall(ValidationStatus status) throws PackageException { LocalPackage oldpkg = service.getActivePackage(pkg.getName()); if (oldpkg != null) { if (oldpkg.getPackageState() == PackageState.INSTALLING) { status.addWarning("A package with the same name: " + oldpkg.getId() + " is being installing. Try again later."); } else { status.addWarning("The package " + oldpkg.getId() + " will be uninstalled!"); } return oldpkg; } return null; } @Override public String getRelativeFilePath(File file) { String path = file.getAbsolutePath(); if (path.startsWith(serverPathPrefix)) { return path.substring(serverPathPrefix.length()); } return path; } }