001/* 002 * (C) Copyright 2016 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 * Nuxeo 018 */ 019 020package org.nuxeo.ecm.admin.operation; 021 022import java.util.List; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.common.utils.ExceptionUtils; 027import org.nuxeo.connect.client.vindoz.InstallAfterRestart; 028import org.nuxeo.connect.client.we.StudioSnapshotHelper; 029import org.nuxeo.connect.connector.ConnectServerError; 030import org.nuxeo.connect.data.DownloadablePackage; 031import org.nuxeo.connect.data.DownloadingPackage; 032import org.nuxeo.connect.packages.PackageManager; 033import org.nuxeo.connect.packages.dependencies.TargetPlatformFilterHelper; 034import org.nuxeo.connect.update.LocalPackage; 035import org.nuxeo.connect.update.PackageException; 036import org.nuxeo.connect.update.PackageState; 037import org.nuxeo.connect.update.PackageUpdateService; 038import org.nuxeo.connect.update.ValidationStatus; 039import org.nuxeo.connect.update.task.Task; 040import org.nuxeo.ecm.admin.runtime.PlatformVersionHelper; 041import org.nuxeo.ecm.automation.core.Constants; 042import org.nuxeo.ecm.automation.core.annotations.Context; 043import org.nuxeo.ecm.automation.core.annotations.Operation; 044import org.nuxeo.ecm.automation.core.annotations.OperationMethod; 045import org.nuxeo.ecm.automation.core.annotations.Param; 046import org.nuxeo.ecm.core.api.CoreSession; 047import org.nuxeo.ecm.core.api.NuxeoException; 048import org.nuxeo.ecm.core.api.NuxeoPrincipal; 049import org.nuxeo.runtime.api.Framework; 050 051/** 052 * Operation to trigger a Hot reload of the Studio Snapshot package. You must be an administrator to trigger it. 053 * 054 * @since 8.2 055 */ 056@Operation(id = HotReloadStudioSnapshot.ID, category = Constants.CAT_SERVICES, label = "Hot Reload Studio Snapshot Package", description = "Updates Studio project with latest snapshot.") 057public class HotReloadStudioSnapshot { 058 059 public static final String ID = "Service.HotReloadStudioSnapshot"; 060 061 protected static boolean updateInProgress = false; 062 063 private static final Log log = LogFactory.getLog(HotReloadStudioSnapshot.class); 064 065 @Context 066 protected CoreSession session; 067 068 @Context 069 protected PackageManager pm; 070 071 @Param(name = "validate", required = false) 072 protected boolean validate; 073 074 @OperationMethod 075 public void run() throws Exception { 076 if (updateInProgress) { 077 return; 078 } 079 080 if (!((NuxeoPrincipal) session.getPrincipal()).isAdministrator()) { 081 throw new NuxeoException("Must be Administrator to use this function"); 082 } 083 084 List<DownloadablePackage> pkgs = pm.listRemoteAssociatedStudioPackages(); 085 DownloadablePackage snapshotPkg = StudioSnapshotHelper.getSnapshot(pkgs); 086 087 if (snapshotPkg == null) { 088 throw new NuxeoException("No Snapshot Package was found."); 089 } 090 091 try { 092 updateInProgress = true; 093 hotReloadPackage(snapshotPkg); 094 } finally { 095 updateInProgress = false; 096 } 097 } 098 099 public void hotReloadPackage(DownloadablePackage remotePkg) { 100 if (validate) { 101 pm.flushCache(); 102 103 String targetPlatform = PlatformVersionHelper.getPlatformFilter(); 104 if (!TargetPlatformFilterHelper.isCompatibleWithTargetPlatform(remotePkg, targetPlatform)) { 105 throw new NuxeoException(String.format("This package is not validated for your current platform: %s", 106 targetPlatform)); 107 } 108 } 109 110 // Effective install 111 if (Framework.isDevModeSet()) { 112 try { 113 PackageUpdateService pus = Framework.getLocalService(PackageUpdateService.class); 114 String packageId = remotePkg.getId(); 115 LocalPackage pkg = pus.getPackage(packageId); 116 117 // Uninstall and/or remove if needed 118 if (pkg != null) { 119 removePackage(pus, pkg); 120 } 121 122 // Download 123 DownloadingPackage downloadingPkg = pm.download(packageId); 124 while (!downloadingPkg.isCompleted()) { 125 log.debug("Downloading studio snapshot package: " + packageId); 126 Thread.sleep(100); 127 } 128 129 // Install 130 log.info("Installing " + packageId); 131 pkg = pus.getPackage(packageId); 132 if (pkg == null || PackageState.DOWNLOADED != pkg.getPackageState()) { 133 throw new NuxeoException("Error while downloading studio snapshot " + pkg); 134 } 135 Task installTask = pkg.getInstallTask(); 136 try { 137 performTask(installTask); 138 } catch (PackageException e) { 139 installTask.rollback(); 140 throw e; 141 } 142 } catch (InterruptedException e) { 143 ExceptionUtils.checkInterrupt(e); 144 throw new NuxeoException("Error while downloading studio snapshot", e); 145 } catch (PackageException | ConnectServerError e) { 146 throw new NuxeoException("Error while installing studio snapshot", e); 147 } 148 } else { 149 InstallAfterRestart.addPackageForInstallation(remotePkg.getId()); 150 } 151 } 152 153 protected static void removePackage(PackageUpdateService pus, LocalPackage pkg) throws PackageException { 154 log.info(String.format("Removing package %s before update...", pkg.getId())); 155 if (pkg.getPackageState().isInstalled()) { 156 // First remove it to allow SNAPSHOT upgrade 157 log.info("Uninstalling " + pkg.getId()); 158 Task uninstallTask = pkg.getUninstallTask(); 159 try { 160 performTask(uninstallTask); 161 } catch (PackageException e) { 162 uninstallTask.rollback(); 163 throw e; 164 } 165 } 166 pus.removePackage(pkg.getId()); 167 } 168 169 protected static void performTask(Task task) throws PackageException { 170 ValidationStatus validationStatus = task.validate(); 171 if (validationStatus.hasErrors()) { 172 throw new PackageException("Failed to validate package " + task.getPackage().getId() + " -> " 173 + validationStatus.getErrors()); 174 } 175 if (validationStatus.hasWarnings()) { 176 log.warn("Got warnings on package validation " + task.getPackage().getId() + " -> " 177 + validationStatus.getWarnings()); 178 } 179 task.run(null); 180 } 181}