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