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:[email protected]">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 * @param key 176 */ 177 public File getFile(String key) { 178 String val = env.get(key); 179 return val == null ? null : new File(val); 180 } 181 182 @Override 183 public boolean isRestartRequired() { 184 return restart; 185 } 186 187 @Override 188 public LocalPackage getPackage() { 189 return pkg; 190 } 191 192 protected Map<Object, Object> createContextMap(Map<String, String> params) { 193 Map<Object, Object> map = new HashMap<>(System.getProperties()); 194 map.putAll(env); 195 if (params != null && !params.isEmpty()) { 196 map.putAll(params); 197 } 198 return map; 199 } 200 201 protected String loadParametrizedFile(File file, Map<String, String> params) throws IOException { 202 String content = FileUtils.readFileToString(file, UTF_8); 203 // replace variables. 204 return StringUtils.expandVars(content, createContextMap(params)); 205 } 206 207 protected void saveParams(Map<String, String> params) throws PackageException { 208 if (params == null || params.isEmpty()) { 209 return; 210 } 211 try { 212 Properties props = new Properties(); 213 props.putAll(params); 214 File file = pkg.getData().getEntry(LocalPackage.INSTALL_PROPERTIES); 215 try (FileOutputStream out = new FileOutputStream(file)) { 216 props.store(out, "user install parameters"); 217 } 218 } catch (IOException e) { 219 throw new PackageException("Failed to save install parameters", e); 220 } 221 } 222 223 @Override 224 public synchronized void run(Map<String, String> params) throws PackageException { 225 if (isInstallTask()) { 226 LocalPackage oldpkg = service.getActivePackage(pkg.getName()); 227 if (oldpkg != null) { 228 if (oldpkg.getPackageState() == PackageState.INSTALLING) { 229 throw new PackageException("Another package with the same name is installing: " + oldpkg.getName()); 230 } else { 231 // uninstall it. 232 Task utask = oldpkg.getUninstallTask(); 233 try { 234 utask.run(new HashMap<>()); 235 } catch (PackageException e) { 236 utask.rollback(); 237 throw new PackageException("Failed to uninstall: " + oldpkg.getId() 238 + ". Cannot continue installation of " + pkg.getId(), e); 239 } 240 } 241 } 242 } 243 service.setPackageState(pkg, PackageState.INSTALLING); 244 saveParams(params); 245 doRun(params); 246 taskDone(); 247 if (updateMgrLoaded) { 248 updateMgr.store(); 249 } 250 } 251 252 public synchronized UpdateManager getUpdateManager() throws PackageException { 253 if (!updateMgrLoaded) { 254 updateMgr.load(); 255 updateMgrLoaded = true; 256 } 257 return updateMgr; 258 } 259 260 protected abstract void rollbackDone() throws PackageException; 261 262 protected abstract void taskDone() throws PackageException; 263 264 @Override 265 public void rollback() throws PackageException { 266 try { 267 doRollback(); 268 } finally { 269 rollbackDone(); 270 } 271 } 272 273 @Override 274 public void setRestartRequired(boolean isRestartRequired) { 275 this.restart = isRestartRequired; 276 } 277 278 protected abstract void doRun(Map<String, String> params) throws PackageException; 279 280 protected abstract void doRollback() throws PackageException; 281 282 @Override 283 public ValidationStatus validate() throws PackageException { 284 ValidationStatus status = new ValidationStatus(); 285 if (isInstallTask()) { 286 validateInstall(status); 287 } 288 doValidate(status); 289 return status; 290 } 291 292 public abstract void doValidate(ValidationStatus status) throws PackageException; 293 294 protected LocalPackage validateInstall(ValidationStatus status) throws PackageException { 295 LocalPackage oldpkg = service.getActivePackage(pkg.getName()); 296 if (oldpkg != null) { 297 if (oldpkg.getPackageState() == PackageState.INSTALLING) { 298 status.addWarning("A package with the same name: " + oldpkg.getId() 299 + " is being installing. Try again later."); 300 } else { 301 status.addWarning("The package " + oldpkg.getId() + " will be uninstalled!"); 302 } 303 return oldpkg; 304 } 305 return null; 306 } 307 308 @Override 309 public String getRelativeFilePath(File file) { 310 String path = file.getAbsolutePath(); 311 if (path.startsWith(serverPathPrefix)) { 312 return path.substring(serverPathPrefix.length()); 313 } 314 return path; 315 } 316 317}