001/* 002 * (C) Copyright 2006-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 * Bogdan Stefanescu 018 * Mathieu Guillaume 019 * Yannis JULIENNE 020 */ 021package org.nuxeo.connect.update.util; 022 023import java.io.ByteArrayInputStream; 024import java.io.File; 025import java.io.FileOutputStream; 026import java.io.IOException; 027import java.io.InputStream; 028import java.util.ArrayList; 029import java.util.LinkedHashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.zip.ZipEntry; 033import java.util.zip.ZipOutputStream; 034 035import org.apache.commons.io.IOUtils; 036import org.nuxeo.connect.update.LocalPackage; 037import org.nuxeo.connect.update.PackageDependency; 038import org.nuxeo.connect.update.PackageType; 039import org.nuxeo.connect.update.Version; 040import org.nuxeo.connect.update.model.PackageDefinition; 041import org.nuxeo.connect.update.model.TaskDefinition; 042import org.nuxeo.connect.update.xml.FormDefinition; 043import org.nuxeo.connect.update.xml.FormsDefinition; 044import org.nuxeo.connect.update.xml.PackageDefinitionImpl; 045import org.nuxeo.connect.update.xml.TaskDefinitionImpl; 046import org.nuxeo.connect.update.xml.XmlSerializer; 047import org.nuxeo.runtime.api.Framework; 048 049/** 050 * Build an XML representation of a package. 051 * 052 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 053 */ 054public class PackageBuilder { 055 056 protected final PackageDefinition def; 057 058 protected final List<FormDefinition> installForms; 059 060 protected final List<FormDefinition> uninstallForms; 061 062 protected final List<FormDefinition> validationForms; 063 064 protected final List<String> platforms; 065 066 protected final List<PackageDependency> dependencies; 067 068 protected final List<PackageDependency> optionalDependencies; 069 070 protected final List<PackageDependency> conflicts; 071 072 protected final List<PackageDependency> provides; 073 074 protected final LinkedHashMap<String, InputStream> entries; 075 076 public PackageBuilder() { 077 def = new PackageDefinitionImpl(); 078 platforms = new ArrayList<>(); 079 dependencies = new ArrayList<>(); 080 optionalDependencies = new ArrayList<>(); 081 conflicts = new ArrayList<>(); 082 provides = new ArrayList<>(); 083 entries = new LinkedHashMap<>(); 084 installForms = new ArrayList<>(); 085 validationForms = new ArrayList<>(); 086 uninstallForms = new ArrayList<>(); 087 } 088 089 public PackageBuilder name(String name) { 090 def.setName(name); 091 return this; 092 } 093 094 public PackageBuilder version(Version version) { 095 def.setVersion(version); 096 return this; 097 } 098 099 public PackageBuilder version(String version) { 100 def.setVersion(new Version(version)); 101 return this; 102 } 103 104 public PackageBuilder type(String type) { 105 def.setType(PackageType.getByValue(type)); 106 return this; 107 } 108 109 public PackageBuilder type(PackageType type) { 110 def.setType(type); 111 return this; 112 } 113 114 public PackageBuilder title(String title) { 115 def.setTitle(title); 116 return this; 117 } 118 119 public PackageBuilder description(String description) { 120 def.setDescription(description); 121 return this; 122 } 123 124 public PackageBuilder classifier(String classifier) { 125 def.setClassifier(classifier); 126 return this; 127 } 128 129 public PackageBuilder vendor(String vendor) { 130 def.setVendor(vendor); 131 return this; 132 } 133 134 public PackageBuilder installer(TaskDefinition task) { 135 def.setInstaller(task); 136 return this; 137 } 138 139 public PackageBuilder installer(String type, boolean restart) { 140 def.setInstaller(new TaskDefinitionImpl(type, restart)); 141 return this; 142 } 143 144 public PackageBuilder uninstaller(TaskDefinition task) { 145 def.setUninstaller(task); 146 return this; 147 } 148 149 public PackageBuilder uninstaller(String type, boolean restart) { 150 def.setUninstaller(new TaskDefinitionImpl(type, restart)); 151 return this; 152 } 153 154 public PackageBuilder hotReloadSupport(boolean hotReloadSupport) { 155 try { 156 def.getClass().getMethod("setHotReloadSupport", boolean.class); 157 def.setHotReloadSupport(hotReloadSupport); 158 } catch (NoSuchMethodException e) { 159 // Ignore setHotReloadSupport with old Connect Client versions 160 } 161 return this; 162 } 163 164 public PackageBuilder requireTermsAndConditionsAcceptance(boolean requireTermsAndConditionsAcceptance) { 165 try { 166 def.getClass().getMethod("setRequireTermsAndConditionsAcceptance", boolean.class); 167 def.setRequireTermsAndConditionsAcceptance(requireTermsAndConditionsAcceptance); 168 } catch (NoSuchMethodException e) { 169 // Ignore setRequireTermsAndConditionsAcceptance with old Connect 170 // Client versions 171 } 172 return this; 173 } 174 175 public PackageBuilder validator(String validator) { 176 def.setValidator(validator); 177 return this; 178 } 179 180 public PackageBuilder platform(String platform) { 181 platforms.add(platform); 182 return this; 183 } 184 185 public PackageBuilder platformName(String platformName) { 186 def.setTargetPlatformName(platformName); 187 return this; 188 } 189 190 public PackageBuilder platformRange(String platformRange) { 191 def.setTargetPlatformRange(platformRange); 192 return this; 193 } 194 195 public PackageBuilder dependency(String expr) { 196 dependencies.add(new PackageDependency(expr)); 197 return this; 198 } 199 200 /** 201 * @since 6.0-HF33 202 */ 203 public PackageBuilder optionalDependency(String expr) { 204 optionalDependencies.add(new PackageDependency(expr)); 205 return this; 206 } 207 208 public PackageBuilder conflict(String expr) { 209 conflicts.add(new PackageDependency(expr)); 210 return this; 211 } 212 213 public PackageBuilder provide(String expr) { 214 provides.add(new PackageDependency(expr)); 215 return this; 216 } 217 218 public PackageBuilder addInstallForm(FormDefinition form) { 219 installForms.add(form); 220 return this; 221 } 222 223 public PackageBuilder addUninstallForm(FormDefinition form) { 224 uninstallForms.add(form); 225 return this; 226 } 227 228 public PackageBuilder addValidationForm(FormDefinition form) { 229 validationForms.add(form); 230 return this; 231 } 232 233 public PackageBuilder addLicense(String content) { 234 return addLicense(new ByteArrayInputStream(content.getBytes())); 235 } 236 237 public PackageBuilder addLicense(InputStream in) { 238 return addEntry(LocalPackage.LICENSE, in); 239 } 240 241 public PackageBuilder addInstallScript(String content) { 242 return addInstallScript(new ByteArrayInputStream(content.getBytes())); 243 } 244 245 public PackageBuilder addInstallScript(InputStream in) { 246 return addEntry(LocalPackage.INSTALL, in); 247 } 248 249 public PackageBuilder addUninstallScript(String content) { 250 return addUninstallScript(new ByteArrayInputStream(content.getBytes())); 251 } 252 253 public PackageBuilder addUninstallScript(InputStream in) { 254 return addEntry(LocalPackage.UNINSTALL, in); 255 } 256 257 public PackageBuilder addTermsAndConditions(String content) { 258 return addTermsAndConditions(new ByteArrayInputStream(content.getBytes())); 259 } 260 261 public PackageBuilder addTermsAndConditions(InputStream in) { 262 return addEntry(LocalPackage.TERMSANDCONDITIONS, in); 263 } 264 265 /** 266 * The entry content will be copied into the zip at build time and the given input stream will be closed. (event if 267 * an exception occurs) - so you don't need to handle stream closing. 268 */ 269 public PackageBuilder addEntry(String path, InputStream in) { 270 entries.put(path, in); 271 return this; 272 } 273 274 public String buildManifest() { 275 if (!platforms.isEmpty()) { 276 def.setTargetPlatforms(platforms.toArray(new String[platforms.size()])); 277 } 278 if (!dependencies.isEmpty()) { 279 def.setDependencies(dependencies.toArray(new PackageDependency[dependencies.size()])); 280 } 281 if (!optionalDependencies.isEmpty()) { 282 def.setOptionalDependencies( 283 optionalDependencies.toArray(new PackageDependency[optionalDependencies.size()])); 284 } 285 if (!conflicts.isEmpty()) { 286 def.setConflicts(conflicts.toArray(new PackageDependency[conflicts.size()])); 287 } 288 if (!provides.isEmpty()) { 289 def.setProvides(provides.toArray(new PackageDependency[provides.size()])); 290 } 291 return new XmlSerializer().toXML(def); 292 } 293 294 public File build() throws IOException { 295 try { 296 String mf = buildManifest(); 297 File file = Framework.createTempFile(def.getId(), ".zip"); 298 Framework.trackFile(file, file); 299 ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(file)); 300 try { 301 ZipEntry entry = new ZipEntry(LocalPackage.MANIFEST); 302 zout.putNextEntry(entry); 303 zout.write(mf.getBytes()); 304 zout.closeEntry(); 305 for (Map.Entry<String, InputStream> stream : entries.entrySet()) { 306 entry = new ZipEntry(stream.getKey()); 307 zout.putNextEntry(entry); 308 IOUtils.copy(stream.getValue(), zout); 309 zout.closeEntry(); 310 } 311 if (!installForms.isEmpty()) { 312 addForms(installForms, LocalPackage.INSTALL_FORMS, zout); 313 } 314 if (!uninstallForms.isEmpty()) { 315 addForms(uninstallForms, LocalPackage.UNINSTALL_FORMS, zout); 316 } 317 if (!validationForms.isEmpty()) { 318 addForms(validationForms, LocalPackage.VALIDATION_FORMS, zout); 319 } 320 } finally { 321 zout.close(); 322 } 323 return file; 324 } finally { // close streams 325 for (InputStream in : entries.values()) { 326 IOUtils.closeQuietly(in); 327 } 328 } 329 } 330 331 protected void addForms(List<FormDefinition> formDefs, String path, ZipOutputStream zout) throws IOException { 332 int i = 0; 333 FormsDefinition forms = new FormsDefinition(); 334 FormDefinition[] ar = new FormDefinition[formDefs.size()]; 335 for (FormDefinition form : formDefs) { 336 ar[i++] = form; 337 } 338 forms.setForms(ar); 339 String xml = new XmlSerializer().toXML(forms); 340 ZipEntry entry = new ZipEntry(path); 341 zout.putNextEntry(entry); 342 IOUtils.copy(new ByteArrayInputStream(xml.getBytes()), zout); 343 zout.closeEntry(); 344 } 345 346}