001/*
002 * (C) Copyright 2006-2012 Nuxeo SA (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 */
018package org.nuxeo.connect.client.we;
019
020import java.util.List;
021
022import javax.ws.rs.GET;
023import javax.ws.rs.POST;
024import javax.ws.rs.Path;
025import javax.ws.rs.PathParam;
026import javax.ws.rs.Produces;
027import javax.ws.rs.QueryParam;
028import javax.ws.rs.core.Response;
029
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.connect.client.vindoz.InstallAfterRestart;
033import org.nuxeo.connect.data.DownloadablePackage;
034import org.nuxeo.connect.packages.PackageManager;
035import org.nuxeo.connect.update.LocalPackage;
036import org.nuxeo.connect.update.Package;
037import org.nuxeo.connect.update.PackageException;
038import org.nuxeo.connect.update.PackageUpdateService;
039import org.nuxeo.connect.update.ValidationStatus;
040import org.nuxeo.connect.update.task.Task;
041import org.nuxeo.connect.update.task.standalone.UninstallTask;
042import org.nuxeo.ecm.admin.runtime.PlatformVersionHelper;
043import org.nuxeo.ecm.webengine.WebException;
044import org.nuxeo.ecm.webengine.model.WebObject;
045import org.nuxeo.ecm.webengine.model.impl.DefaultObject;
046import org.nuxeo.runtime.api.Framework;
047
048/**
049 * Provides REST bindings for {@link Package} install management.
050 *
051 * @author <a href="mailto:td@nuxeo.com">Thierry Delprat</a>
052 */
053@WebObject(type = "uninstallHandler")
054public class UninstallHandler extends DefaultObject {
055
056    protected static final Log log = LogFactory.getLog(UninstallHandler.class);
057
058    @GET
059    @Produces("text/html")
060    @Path(value = "start/{pkgId}")
061    public Object startUninstall(@PathParam("pkgId") String pkgId, @QueryParam("source") String source,
062            @QueryParam("filterOnPlatform") Boolean filterOnPlatform) {
063        try {
064            PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class);
065            LocalPackage pkg = pus.getPackage(pkgId);
066            Task uninstallTask = pkg.getUninstallTask();
067            ValidationStatus status = uninstallTask.validate();
068            if (status.hasErrors()) {
069                return getView("canNotUninstall").arg("status", status).arg("pkg", pkg).arg("source", source);
070            }
071            PackageManager pm = Framework.getLocalService(PackageManager.class);
072            List<DownloadablePackage> pkgToRemove = pm.getUninstallDependencies(pkg,
073                    getTargetPlatform(filterOnPlatform));
074            if (pkgToRemove.size() > 0) {
075                return getView("displayDependencies").arg("pkg", pkg).arg("pkgToRemove", pkgToRemove).arg("source",
076                        source);
077            }
078            return getView("startUninstall").arg("status", status).arg("uninstallTask", uninstallTask).arg("pkg", pkg).arg(
079                    "source", source);
080        } catch (PackageException e) {
081            log.error("Error during first step of installation", e);
082            return getView("uninstallError").arg("e", e);
083        }
084    }
085
086    /**
087     * @param filterOnPlatform
088     * @return
089     */
090    private String getTargetPlatform(Boolean filterOnPlatform) {
091        if (filterOnPlatform != Boolean.TRUE) {
092            return null;
093        }
094        return PlatformVersionHelper.getPlatformFilter();
095    }
096
097    @GET
098    @Produces("text/html")
099    @Path(value = "run/{pkgId}")
100    public Object doUninstall(@PathParam("pkgId") String pkgId, @QueryParam("source") String source,
101            @QueryParam("filterOnPlatform") Boolean filterOnPlatform) {
102        PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class);
103        try {
104            LocalPackage pkg = pus.getPackage(pkgId);
105            PackageManager pm = Framework.getLocalService(PackageManager.class);
106            List<DownloadablePackage> pkgToRemove = pm.getUninstallDependencies(pkg,
107                    getTargetPlatform(filterOnPlatform));
108            boolean restartRequired = InstallAfterRestart.isNeededForPackage(pkg);
109            if (!restartRequired) {
110                for (DownloadablePackage rpkg : pkgToRemove) {
111                    if (InstallAfterRestart.isNeededForPackage(rpkg)) {
112                        restartRequired = true;
113                        break;
114                    }
115                }
116            }
117            if (restartRequired) {
118                InstallAfterRestart.addPackageForUnInstallation(pkg.getName());
119                return getView("uninstallOnRestart").arg("pkg", pkg).arg("source", source);
120            } else {
121                log.debug("Uninstalling: " + pkgToRemove);
122                Task uninstallTask;
123                for (DownloadablePackage rpkg : pkgToRemove) {
124                    LocalPackage localPackage = pus.getPackage(rpkg.getId());
125                    performUninstall(localPackage);
126                }
127                uninstallTask = performUninstall(pkg);
128                return getView("uninstallDone").arg("uninstallTask", uninstallTask).arg("pkg", pkg).arg("source",
129                        source);
130            }
131        } catch (PackageException e) {
132            log.error("Error during uninstall of " + pkgId, e);
133            return getView("uninstallError").arg("e", e).arg("source", source);
134        }
135    }
136
137    /**
138     * Run UninstallTask of given local package
139     *
140     * @since 5.6
141     * @param localPackage Package to uninstall
142     * @return {@link UninstallTask} of {@code localPackage}
143     * @throws PackageException If uninstall fails. A rollback is done before the exception is raised.
144     */
145    protected Task performUninstall(LocalPackage localPackage) throws PackageException {
146        log.info("Uninstalling " + localPackage.getId());
147        Task uninstallTask = localPackage.getUninstallTask();
148        try {
149            uninstallTask.run(null);
150        } catch (PackageException e) {
151            log.error("Error during uninstall of " + localPackage.getId(), e);
152            uninstallTask.rollback();
153            throw e;
154        }
155        return uninstallTask;
156    }
157
158    @POST
159    @Path("restart")
160    public Object restartServer() {
161        PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class);
162        try {
163            pus.restart();
164        } catch (PackageException e) {
165            throw WebException.wrap(e);
166        }
167        // TODO create a page that waits for the server to restart
168        return Response.ok().build();
169    }
170
171}