/* * (C) Copyright 2006-2014 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Nuxeo - initial API and implementation * */ package org.nuxeo.connect.client.we; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.connect.client.vindoz.InstallAfterRestart; import org.nuxeo.connect.packages.PackageManager; import org.nuxeo.connect.packages.dependencies.DependencyResolution; import org.nuxeo.connect.packages.dependencies.TargetPlatformFilterHelper; import org.nuxeo.connect.update.LocalPackage; import org.nuxeo.connect.update.Package; import org.nuxeo.connect.update.PackageException; import org.nuxeo.connect.update.PackageUpdateService; import org.nuxeo.connect.update.ValidationStatus; import org.nuxeo.connect.update.Version; import org.nuxeo.connect.update.model.Field; import org.nuxeo.connect.update.model.Form; import org.nuxeo.connect.update.task.Task; import org.nuxeo.ecm.admin.runtime.PlatformVersionHelper; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.webengine.forms.FormData; import org.nuxeo.ecm.webengine.model.WebObject; import org.nuxeo.ecm.webengine.model.impl.DefaultObject; import org.nuxeo.runtime.api.Framework; /** * Provides REST bindings for {@link Package} install management. * * @author <a href="mailto:td@nuxeo.com">Thierry Delprat</a> */ @WebObject(type = "installHandler") public class InstallHandler extends DefaultObject { protected static final Log log = LogFactory.getLog(InstallHandler.class); protected static final String INSTALL_PARAM_MAPS = "org.nuxeo.connect.updates.install.params"; protected String getStorageKey(String pkgId) { return INSTALL_PARAM_MAPS + "_" + pkgId; } @SuppressWarnings("unchecked") protected Map<String, String> getInstallParameters(String pkgId) { Map<String, String> params = (Map<String, String>) getContext().getRequest().getAttribute(getStorageKey(pkgId)); if (params == null) { params = new HashMap<>(); } return params; } protected void storeInstallParameters(String pkgId, Map<String, String> params) { getContext().getRequest().setAttribute(getStorageKey(pkgId), params); } protected void clearInstallParameters(String pkgId) { getContext().getRequest().setAttribute(getStorageKey(pkgId), null); } @GET @Produces("text/html") @Path(value = "showTermsAndConditions/{pkgId}") public Object showTermsAndConditions(@PathParam("pkgId") String pkgId, @QueryParam("source") String source, @QueryParam("depCheck") Boolean depCheck) { if (depCheck == null) { depCheck = true; } try { PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class); LocalPackage pkg = pus.getPackage(pkgId); String content = pkg.getTermsAndConditionsContent(); return getView("termsAndConditions").arg("pkg", pkg).arg("source", source).arg("content", content).arg( "depCheck", depCheck); } catch (PackageException e) { log.error("Error during terms and conditions phase ", e); return getView("installError").arg("e", e).arg("source", source); } } @GET @Produces("text/html") @Path(value = "start/{pkgId}") public Object startInstall(@PathParam("pkgId") String pkgId, @QueryParam("source") String source, @QueryParam("tacAccepted") Boolean acceptedTAC, @QueryParam("depCheck") Boolean depCheck, @QueryParam("autoMode") Boolean autoMode) { try { PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class); LocalPackage pkg = pus.getPackage(pkgId); if (pkg == null) { throw new NuxeoException("Can not find package " + pkgId); } if (pkg.requireTermsAndConditionsAcceptance() && !Boolean.TRUE.equals(acceptedTAC)) { return showTermsAndConditions(pkgId, source, depCheck); } if (!Boolean.FALSE.equals(depCheck)) { // check deps requirements if (pkg.getDependencies() != null && pkg.getDependencies().length > 0) { PackageManager pm = Framework.getLocalService(PackageManager.class); DependencyResolution resolution = pm.resolveDependencies(Collections.singletonList(pkgId), Collections.emptyList(), Collections.emptyList(), PlatformVersionHelper.getPlatformFilter()); if (resolution.isFailed() && PlatformVersionHelper.getPlatformFilter() != null) { // retry without PF filter ... resolution = pm.resolveDependencies(Collections.singletonList(pkgId), Collections.emptyList(), Collections.emptyList(), null); } if (resolution.isFailed()) { return getView("dependencyError").arg("resolution", resolution).arg("pkg", pkg).arg("source", source); } else { if (resolution.requireChanges()) { if (autoMode == null) { autoMode = true; } return getView("displayDependencies").arg("resolution", resolution).arg("pkg", pkg).arg( "source", source).arg("autoMode", autoMode); } // no dep changes => can continue standard install // process } } } Task installTask = pkg.getInstallTask(); ValidationStatus status = installTask.validate(); String targetPlatform = PlatformVersionHelper.getPlatformFilter(); if (!TargetPlatformFilterHelper.isCompatibleWithTargetPlatform(pkg, targetPlatform)) { status.addWarning("This package is not validated for you current platform: " + targetPlatform); } if (status.hasErrors()) { return getView("canNotInstall").arg("status", status).arg("pkg", pkg).arg("source", source); } boolean needWizard = false; Form[] forms = installTask.getPackage().getInstallForms(); if (forms != null && forms.length > 0) { needWizard = true; } return getView("startInstall").arg("status", status).arg("needWizard", needWizard).arg("installTask", installTask).arg("pkg", pkg).arg("source", source); } catch (PackageException e) { log.error("Error during first step of installation", e); return getView("installError").arg("e", e).arg("source", source); } } @GET @Produces("text/html") @Path(value = "form/{pkgId}/{formId}") public Object showInstallForm(@PathParam("pkgId") String pkgId, @PathParam("formId") int formId, @QueryParam("source") String source) { PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class); try { LocalPackage pkg = pus.getPackage(pkgId); Task installTask = pkg.getInstallTask(); Form[] forms = installTask.getPackage().getInstallForms(); if (forms == null || forms.length < formId - 1) { return getView("installError").arg("e", new NuxeoException("No form with Id " + formId + " for package " + pkgId)).arg("source", source); } return getView("showInstallForm").arg("form", forms[formId]).arg("pkg", pkg).arg("source", source).arg( "step", formId + 1).arg("steps", forms.length); } catch (PackageException e) { log.error("Error during displaying Form nb " + formId, e); return getView("installError").arg("e", e).arg("source", source); } } @POST @Produces("text/html") @Path(value = "form/{pkgId}/{formId}") public Object processInstallForm(@PathParam("pkgId") String pkgId, @PathParam("formId") int formId, @QueryParam("source") String source) { PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class); try { LocalPackage pkg = pus.getPackage(pkgId); Task installTask = pkg.getInstallTask(); Form[] forms = installTask.getPackage().getInstallForms(); if (forms == null || forms.length < formId - 1) { return getView("installError").arg("e", new NuxeoException("No form with Id " + formId + " for package " + pkgId)).arg("source", source); } Form form = forms[formId]; FormData fdata = getContext().getForm(); Map<String, String> params = getInstallParameters(pkgId); for (Field field : form.getFields()) { String data = fdata.getString(field.getName()); if (data != null) { params.put(field.getName(), data); } // XXX validation, and type checking ... } storeInstallParameters(pkgId, params); if (formId + 1 == forms.length) { // this was the last form screen : start the install return doInstall(pkgId, source); } else { return showInstallForm(pkgId, formId + 1, source); } } catch (PackageException e) { log.error("Error during processing Form nb " + formId, e); return getView("installError").arg("e", e).arg("source", source); } } @GET @Produces("text/html") @Path(value = "bulkRun/{pkgId}") public Object doBulkInstall(@PathParam("pkgId") String pkgId, @QueryParam("source") String source, @QueryParam("confirm") Boolean confirm) { if (!RequestHelper.isInternalLink(getContext())) { return getView("installError").arg("e", new NuxeoException("Installation seems to have been started from an external link.")).arg( "source", source); } PackageManager pm = Framework.getLocalService(PackageManager.class); PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class); try { DependencyResolution resolution = pm.resolveDependencies(Collections.singletonList(pkgId), Collections.emptyList(), Collections.emptyList(), PlatformVersionHelper.getPlatformFilter()); if (resolution.isFailed() && PlatformVersionHelper.getPlatformFilter() != null) { // retry without PF filter ... resolution = pm.resolveDependencies(Collections.singletonList(pkgId), Collections.emptyList(), Collections.emptyList(), null); } List<String> downloadPackagesIds = resolution.getDownloadPackageIds(); if (downloadPackagesIds.size() > 0) { return getView("installError").arg("e", new NuxeoException("Some packages need to be downloaded before running bulk installation")).arg( "source", source); } List<String> pkgIds = resolution.getOrderedPackageIdsToInstall(); List<String> warns = new ArrayList<>(); List<String> descs = new ArrayList<>(); if (!pkgIds.contains(pkgId)) { pkgIds.add(pkgId); } List<String> rmPkgIds = new ArrayList<>(); for (Entry<String, Version> rmEntry : resolution.getLocalPackagesToRemove().entrySet()) { String id = rmEntry.getKey() + "-" + rmEntry.getValue().toString(); rmPkgIds.add(id); } for (String id : pkgIds) { Package pkg = pus.getPackage(id); if (pkg == null) { return getView("installError").arg("e", new NuxeoException("Unable to find local package " + id)).arg( "source", source); } String targetPlatform = PlatformVersionHelper.getPlatformFilter(); if (!TargetPlatformFilterHelper.isCompatibleWithTargetPlatform(pkg, targetPlatform)) { warns.add("Package " + id + " is not validated for your current platform: " + targetPlatform); } descs.add(pkg.getDescription()); } if (Boolean.TRUE.equals(confirm)) { for (String id : rmPkgIds) { InstallAfterRestart.addPackageForUnInstallation(id); } for (String id : pkgIds) { InstallAfterRestart.addPackageForInstallation(id); } return getView("bulkInstallOnRestart").arg("pkgIds", pkgIds).arg("rmPkgIds", rmPkgIds).arg("source", source); } else { return getView("bulkInstallOnRestartConfirm").arg("pkgIds", pkgIds).arg("rmPkgIds", rmPkgIds).arg( "warns", warns).arg("descs", descs).arg("source", source).arg("pkgId", pkgId); } } catch (PackageException e) { log.error("Error during installation of " + pkgId, e); return getView("installError").arg("e", e).arg("source", source); } } @GET @Produces("text/html") @Path(value = "run/{pkgId}") public Object doInstall(@PathParam("pkgId") String pkgId, @QueryParam("source") String source) { if (!RequestHelper.isInternalLink(getContext())) { return getView("installError").arg("e", new NuxeoException("Installation seems to have been started from an external link.")).arg( "source", source); } PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class); try { LocalPackage pkg = pus.getPackage(pkgId); if (InstallAfterRestart.isNeededForPackage(pkg)) { InstallAfterRestart.addPackageForInstallation(pkg.getId()); return getView("installOnRestart").arg("pkg", pkg).arg("source", source); } Task installTask = pkg.getInstallTask(); Map<String, String> params = getInstallParameters(pkgId); try { installTask.run(params); } catch (PackageException e) { log.error("Error during installation of " + pkgId, e); installTask.rollback(); return getView("installError").arg("e", e).arg("source", source); } clearInstallParameters(pkgId); return getView("installedOK").arg("installTask", installTask).arg("pkg", pkg).arg("source", source); } catch (PackageException e) { log.error("Error during installation of " + pkgId, e); return getView("installError").arg("e", e).arg("source", source); } } }