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}