001/* 002 * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. 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 * 016 * Contributors: 017 * bstefanescu, jcarsique 018 */ 019package org.nuxeo.connect.update.task.standalone; 020 021import static java.nio.charset.StandardCharsets.UTF_8; 022 023import java.io.ByteArrayOutputStream; 024import java.io.File; 025import java.io.FileOutputStream; 026import java.io.IOException; 027import java.io.PrintStream; 028import java.text.SimpleDateFormat; 029import java.util.Date; 030import java.util.HashMap; 031import java.util.Map; 032import java.util.Properties; 033 034import org.apache.commons.collections.MapUtils; 035import org.apache.commons.io.FileUtils; 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038import org.nuxeo.common.Environment; 039import org.nuxeo.common.utils.StringUtils; 040import org.nuxeo.connect.update.LocalPackage; 041import org.nuxeo.connect.update.PackageException; 042import org.nuxeo.connect.update.PackageState; 043import org.nuxeo.connect.update.PackageUpdateService; 044import org.nuxeo.connect.update.ValidationStatus; 045import org.nuxeo.connect.update.task.Task; 046import org.nuxeo.connect.update.task.update.UpdateManager; 047 048/** 049 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 050 */ 051public abstract class AbstractTask implements Task { 052 053 static final Log log = LogFactory.getLog(AbstractTask.class); 054 055 public static final String PKG_ID = "package.id"; 056 057 public static final String PKG_NAME = "package.name"; 058 059 public static final String PKG_VERSION = "package.version"; 060 061 public static final String PKG_ROOT = "package.root"; 062 063 public static final String ENV_HOME = "env.home"; 064 065 /** 066 * @since 5.5 067 */ 068 public static final String ENV_SERVER_HOME = "env.server.home"; 069 070 /** 071 * Set only on JBoss - the EAR root directory path 072 */ 073 public static final String ENV_EAR = "env.ear"; 074 075 public static final String ENV_LIB = "env.lib"; 076 077 public static final String ENV_SYSLIB = "env.syslib"; 078 079 public static final String ENV_BUNDLES = "env.bundles"; 080 081 public static final String ENV_CONFIG = "env.config"; 082 083 /** 084 * @since 5.5 085 */ 086 public static final String ENV_TEMPLATES = "env.templates"; 087 088 public static final String ENV_TIMESTAMP = "sys.timestamp"; 089 090 /** 091 * The host application name. 092 * 093 * @see Environment#getHostApplicationName() 094 */ 095 public static final String ENV_HOSTAPP_NAME = "env.hostapp.name"; 096 097 /** 098 * The host application version 099 * 100 * @see Environment#getHostApplicationVersion() 101 */ 102 public static final String ENV_HOSTAPP_VERSION = "env.hostapp.version"; 103 104 protected boolean restart; 105 106 protected LocalPackage pkg; 107 108 protected String serverPathPrefix; 109 110 protected UpdateManager updateMgr; 111 112 protected boolean updateMgrLoaded = false; 113 114 protected PackageUpdateService service; 115 116 /** 117 * A map of environment key/values that can be used in XML install files as variables. 118 */ 119 protected final Map<String, String> env; 120 121 public AbstractTask(PackageUpdateService pus) { 122 service = pus; 123 env = new HashMap<>(); 124 Environment nxenv = Environment.getDefault(); 125 File serverHome = nxenv.getServerHome(); 126 File nxHome = nxenv.getRuntimeHome(); 127 File config = nxenv.getConfig(); 128 serverPathPrefix = serverHome.getAbsolutePath(); 129 if (!serverPathPrefix.endsWith(File.separator)) { 130 serverPathPrefix = serverPathPrefix.concat(File.separator); 131 } 132 env.put(ENV_SERVER_HOME, serverHome.getAbsolutePath()); 133 env.put(ENV_HOME, nxHome.getAbsolutePath()); 134 env.put(ENV_CONFIG, config.getAbsolutePath()); 135 env.put(ENV_HOSTAPP_NAME, nxenv.getHostApplicationName()); 136 env.put(ENV_HOSTAPP_VERSION, nxenv.getHostApplicationVersion()); 137 env.put(ENV_SYSLIB, new File(serverHome, "lib").getAbsolutePath()); 138 if (nxenv.isJBoss()) { 139 File ear = config.getParentFile(); 140 env.put(ENV_EAR, ear.getAbsolutePath()); 141 env.put(ENV_LIB, new File(ear, "lib").getAbsolutePath()); 142 env.put(ENV_BUNDLES, new File(ear, "bundles").getAbsolutePath()); 143 } else { 144 env.put(ENV_LIB, new File(nxHome, "lib").getAbsolutePath()); 145 env.put(ENV_BUNDLES, new File(nxHome, "bundles").getAbsolutePath()); 146 } 147 env.put(ENV_TEMPLATES, new File(serverHome, "templates").getAbsolutePath()); 148 env.put(ENV_TIMESTAMP, new SimpleDateFormat("yyMMddHHmmss").format(new Date())); 149 updateMgr = new UpdateManager(serverHome, service.getRegistry()); 150 } 151 152 public abstract boolean isInstallTask(); 153 154 @Override 155 @SuppressWarnings("hiding") 156 public void initialize(LocalPackage pkg, boolean restart) throws PackageException { 157 this.pkg = pkg; 158 this.restart = restart; 159 env.put(PKG_ID, pkg.getId()); 160 env.put(PKG_NAME, pkg.getName()); 161 env.put(PKG_VERSION, pkg.getVersion().toString()); 162 env.put(PKG_ROOT, pkg.getData().getRoot().getAbsolutePath()); 163 164 if (log.isDebugEnabled()) { 165 final ByteArrayOutputStream out = new ByteArrayOutputStream(); 166 final PrintStream outPrint = new PrintStream(out); 167 MapUtils.debugPrint(outPrint, null, env); 168 log.debug(out.toString()); 169 } 170 } 171 172 /** 173 * Get a file given its key in the environment map. If no key exists then null is returned. 174 */ 175 public File getFile(String key) { 176 String val = env.get(key); 177 return val == null ? null : new File(val); 178 } 179 180 @Override 181 public boolean isRestartRequired() { 182 return restart; 183 } 184 185 @Override 186 public LocalPackage getPackage() { 187 return pkg; 188 } 189 190 protected Map<Object, Object> createContextMap(Map<String, String> params) { 191 Map<Object, Object> map = new HashMap<>(System.getProperties()); 192 map.putAll(env); 193 if (params != null && !params.isEmpty()) { 194 map.putAll(params); 195 } 196 return map; 197 } 198 199 protected String loadParametrizedFile(File file, Map<String, String> params) throws IOException { 200 String content = FileUtils.readFileToString(file, UTF_8); 201 // replace variables. 202 return StringUtils.expandVars(content, createContextMap(params)); 203 } 204 205 protected void saveParams(Map<String, String> params) throws PackageException { 206 if (params == null || params.isEmpty()) { 207 return; 208 } 209 try { 210 Properties props = new Properties(); 211 props.putAll(params); 212 File file = pkg.getData().getEntry(LocalPackage.INSTALL_PROPERTIES); 213 try (FileOutputStream out = new FileOutputStream(file)) { 214 props.store(out, "user install parameters"); 215 } 216 } catch (IOException e) { 217 throw new PackageException("Failed to save install parameters", e); 218 } 219 } 220 221 @Override 222 public synchronized void run(Map<String, String> params) throws PackageException { 223 if (isInstallTask()) { 224 LocalPackage oldpkg = service.getActivePackage(pkg.getName()); 225 if (oldpkg != null) { 226 if (oldpkg.getPackageState() == PackageState.INSTALLING) { 227 throw new PackageException("Another package with the same name is installing: " + oldpkg.getName()); 228 } else { 229 // uninstall it. 230 Task utask = oldpkg.getUninstallTask(); 231 try { 232 utask.run(new HashMap<>()); 233 } catch (PackageException e) { 234 utask.rollback(); 235 throw new PackageException("Failed to uninstall: " + oldpkg.getId() 236 + ". Cannot continue installation of " + pkg.getId(), e); 237 } 238 } 239 } 240 } 241 service.setPackageState(pkg, PackageState.INSTALLING); 242 saveParams(params); 243 doRun(params); 244 taskDone(); 245 if (updateMgrLoaded) { 246 updateMgr.store(); 247 } 248 } 249 250 public synchronized UpdateManager getUpdateManager() throws PackageException { 251 if (!updateMgrLoaded) { 252 updateMgr.load(); 253 updateMgrLoaded = true; 254 } 255 return updateMgr; 256 } 257 258 protected abstract void rollbackDone() throws PackageException; 259 260 protected abstract void taskDone() throws PackageException; 261 262 @Override 263 public void rollback() throws PackageException { 264 try { 265 doRollback(); 266 } finally { 267 rollbackDone(); 268 } 269 } 270 271 @Override 272 public void setRestartRequired(boolean isRestartRequired) { 273 this.restart = isRestartRequired; 274 } 275 276 protected abstract void doRun(Map<String, String> params) throws PackageException; 277 278 protected abstract void doRollback() throws PackageException; 279 280 @Override 281 public ValidationStatus validate() throws PackageException { 282 ValidationStatus status = new ValidationStatus(); 283 if (isInstallTask()) { 284 validateInstall(status); 285 } 286 doValidate(status); 287 return status; 288 } 289 290 public abstract void doValidate(ValidationStatus status) throws PackageException; 291 292 protected LocalPackage validateInstall(ValidationStatus status) throws PackageException { 293 LocalPackage oldpkg = service.getActivePackage(pkg.getName()); 294 if (oldpkg != null) { 295 if (oldpkg.getPackageState() == PackageState.INSTALLING) { 296 status.addWarning("A package with the same name: " + oldpkg.getId() 297 + " is being installing. Try again later."); 298 } else { 299 status.addWarning("The package " + oldpkg.getId() + " will be uninstalled!"); 300 } 301 return oldpkg; 302 } 303 return null; 304 } 305 306 @Override 307 public String getRelativeFilePath(File file) { 308 String path = file.getAbsolutePath(); 309 if (path.startsWith(serverPathPrefix)) { 310 return path.substring(serverPathPrefix.length()); 311 } 312 return path; 313 } 314 315}