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