001/* 002 * (C) Copyright 2006-2012 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.update; 020 021import java.io.File; 022import java.util.Map; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026import org.nuxeo.connect.update.PackageException; 027import org.nuxeo.connect.update.ValidationStatus; 028import org.nuxeo.connect.update.task.Command; 029import org.nuxeo.connect.update.task.Task; 030import org.nuxeo.connect.update.task.standalone.AbstractTask; 031import org.nuxeo.connect.update.task.standalone.commands.AbstractCommand; 032import org.nuxeo.connect.update.task.standalone.commands.CompositeCommand; 033import org.nuxeo.connect.update.task.standalone.commands.DeployPlaceholder; 034import org.nuxeo.connect.update.task.update.JarUtils.Match; 035import org.nuxeo.connect.update.xml.XmlWriter; 036import org.w3c.dom.Element; 037 038/** 039 * @since 5.5 040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 041 */ 042public class Update extends AbstractCommand { 043 044 protected static final Log log = LogFactory.getLog(Update.class); 045 046 public static final String ID = "update"; 047 048 /** 049 * The source file. It can be a file or a directory. 050 */ 051 protected File file; 052 053 /** 054 * The target file. It can be a directory since 5.5 055 */ 056 protected File todir; 057 058 protected boolean removeOnExit; 059 060 protected boolean allowDowngrade = false; 061 062 protected boolean upgradeOnly = false; 063 064 protected Update(String id) { 065 super(id); 066 } 067 068 public Update() { 069 this(ID); 070 } 071 072 @Override 073 public void initialize(Element element) throws PackageException { 074 super.initialize(element); 075 } 076 077 @Override 078 public void readFrom(Element element) throws PackageException { 079 File dir = null; 080 String v = element.getAttribute("dir"); 081 if (v.length() > 0) { 082 dir = new File(v); 083 } 084 v = element.getAttribute("file"); 085 if (v.length() > 0) { 086 if (dir != null) { 087 file = new File(dir, v); 088 } else { 089 file = new File(v); 090 } 091 guardVars.put("file", file); 092 } else { 093 file = dir; 094 guardVars.put("dir", dir); 095 } 096 097 v = element.getAttribute("todir"); 098 if (v.length() > 0) { 099 todir = new File(v); 100 guardVars.put("todir", todir); 101 } 102 103 v = element.getAttribute("removeOnExit"); 104 if (v.length() > 0) { 105 removeOnExit = Boolean.parseBoolean(v); 106 } 107 v = element.getAttribute("allowDowngrade"); 108 if (v.length() > 0) { 109 allowDowngrade = Boolean.parseBoolean(v); 110 } 111 v = element.getAttribute("upgradeOnly"); 112 if (v.length() > 0) { 113 upgradeOnly = Boolean.parseBoolean(v); 114 } 115 } 116 117 @Override 118 public void writeTo(XmlWriter writer) { 119 writer.start(ID); 120 if (file != null) { 121 writer.attr("file", file.getAbsolutePath()); 122 } 123 if (todir != null) { 124 writer.attr("todir", todir.getAbsolutePath()); 125 } 126 if (removeOnExit) { 127 writer.attr("removeOnExit", "true"); 128 } 129 if (allowDowngrade) { 130 writer.attr("allowDowngrade", "true"); 131 } 132 if (upgradeOnly) { 133 writer.attr("upgradeOnly", "true"); 134 } 135 writer.end(); 136 } 137 138 @Override 139 protected void doValidate(Task task, ValidationStatus status) throws PackageException { 140 if (file == null || todir == null) { 141 status.addError("Cannot execute command in installer." 142 + " Invalid update syntax: file or todir was not specified."); 143 } 144 if (todir.isFile()) { 145 status.addError("Cannot execute command in installer." 146 + " Invalid update command: todir should be a directory!"); 147 } 148 if (file.isFile()) { 149 Match<String> match = JarUtils.findJarVersion(file.getName()); 150 if (match == null) { 151 status.addError("Cannot execute command in installer." 152 + " Cannot use 'update' command for non versioned files!. File name must contain a version: " 153 + file.getName()); 154 } 155 } else if (!file.isDirectory()) { 156 status.addWarning("Ignored command in installer." + " Source file not found! " + file.getName()); 157 } 158 } 159 160 @Override 161 protected Command doRun(Task task, Map<String, String> prefs) throws PackageException { 162 if (!file.exists()) { 163 log.warn("Can't update using " + file + ". File is missing."); 164 return null; 165 } 166 UpdateManager mgr = ((AbstractTask) task).getUpdateManager(); 167 168 Command rollback; 169 if (file.isDirectory()) { 170 rollback = updateDirectory(task, file, mgr); 171 } else { 172 rollback = updateFile(task, file, mgr); 173 } 174 175 Command deploy = getDeployCommand(mgr, rollback); 176 if (deploy != null) { 177 deploy.run(task, prefs); 178 } 179 180 return rollback; 181 } 182 183 protected CompositeCommand updateDirectory(Task task, File dir, UpdateManager mgr) throws PackageException { 184 CompositeCommand cmd = new CompositeCommand(); 185 File[] files = dir.listFiles(); 186 if (files != null) { 187 for (File fileInDir : files) { 188 cmd.addCommand(updateFile(task, fileInDir, mgr)); 189 } 190 } 191 return cmd; 192 } 193 194 protected Rollback updateFile(Task task, File fileToUpdate, UpdateManager mgr) throws PackageException { 195 UpdateOptions opt = UpdateOptions.newInstance(task.getPackage().getId(), fileToUpdate, todir); 196 if (opt == null) { 197 return null; 198 } 199 opt.setAllowDowngrade(allowDowngrade); 200 opt.setUpgradeOnly(upgradeOnly); 201 opt.deleteOnExit = removeOnExit; 202 try { 203 RollbackOptions r = mgr.update(opt); 204 return new Rollback(r); 205 } catch (VersionAlreadyExistException e) { 206 // should never happen 207 log.error(e, e); 208 return null; 209 } 210 } 211 212 /** 213 * Method to be overridden by subclasses to provide a deploy command for hot reload 214 * 215 * @since 5.6 216 */ 217 protected Command getDeployCommand(UpdateManager updateManager, Command rollbackCommand) { 218 return new DeployPlaceholder(file); 219 } 220 221}