| /******************************************************************************* |
| * Copyright (c) 2008, 2011 Sonatype Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Sonatype Inc. - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.tycho.packaging; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.URL; |
| import java.util.Map; |
| |
| import org.apache.maven.execution.MavenSession; |
| import org.codehaus.plexus.archiver.ArchiverException; |
| import org.codehaus.plexus.archiver.zip.ZipArchiver; |
| import org.codehaus.plexus.archiver.zip.ZipUnArchiver; |
| import org.codehaus.plexus.component.repository.exception.ComponentLookupException; |
| import org.codehaus.plexus.util.FileUtils; |
| import org.codehaus.plexus.util.IOUtil; |
| import org.eclipse.tycho.ReactorProject; |
| import org.eclipse.tycho.core.ArtifactDependencyVisitor; |
| import org.eclipse.tycho.core.FeatureDescription; |
| import org.eclipse.tycho.core.PluginDescription; |
| import org.eclipse.tycho.locking.facade.FileLockService; |
| import org.eclipse.tycho.locking.facade.FileLocker; |
| import org.eclipse.tycho.model.PluginRef; |
| |
| /** |
| * Assembles standard eclipse update site directory structure on local filesystem. |
| * |
| * @author igor |
| */ |
| public class UpdateSiteAssembler extends ArtifactDependencyVisitor { |
| |
| public static final String PLUGINS_DIR = "plugins/"; |
| |
| public static final String FEATURES_DIR = "features/"; |
| |
| private final MavenSession session; |
| |
| private final File target; |
| |
| private Map<String, String> archives; |
| |
| /** |
| * If true, generated update site will include plugins folders for plugins with |
| * PluginRef.unpack. If false, will include plugin jars regardless of PluginRef.unpack. |
| */ |
| private boolean unpackPlugins; |
| |
| /** |
| * If true, generated update site will include feature directories. If false, generated update |
| * site will include feature jars. |
| */ |
| private boolean unpackFeatures; |
| |
| public UpdateSiteAssembler(MavenSession session, File target) { |
| this.session = session; |
| this.target = target; |
| } |
| |
| @Override |
| public boolean visitFeature(FeatureDescription feature) { |
| File location = feature.getLocation(); |
| String artifactId = feature.getKey().getId(); |
| String version = feature.getKey().getVersion(); |
| |
| ReactorProject featureProject = feature.getMavenProject(); |
| |
| if (featureProject != null) { |
| version = featureProject.getExpandedVersion(); |
| |
| location = featureProject.getArtifact(feature.getClassifier()); |
| |
| if (location == null) { |
| throw new IllegalStateException(featureProject.getId() |
| + " does not provide an artifact with classifier '" + feature.getClassifier() + "'"); |
| } |
| |
| if (location.isDirectory()) { |
| throw new IllegalStateException("Should at least run ``package'' phase"); |
| } |
| } |
| |
| if (unpackFeatures) { |
| File outputJar = getOutputFile(FEATURES_DIR, artifactId, version, null); |
| if (location.isDirectory()) { |
| copyDir(location, outputJar); |
| } else { |
| unpackJar(location, outputJar); |
| } |
| } else { |
| File outputJar = getOutputFile(FEATURES_DIR, artifactId, version, ".jar"); |
| if (location.isDirectory()) { |
| packDir(location, outputJar); |
| } else { |
| copyFile(location, outputJar); |
| } |
| } |
| |
| return true; // keep visiting |
| } |
| |
| private File getOutputFile(String prefix, String id, String version, String extension) { |
| StringBuilder sb = new StringBuilder(prefix); |
| sb.append(id); |
| sb.append('_'); |
| sb.append(version); |
| if (extension != null) { |
| sb.append(extension); |
| } |
| |
| return new File(target, sb.toString()); |
| } |
| |
| @Override |
| public void visitPlugin(PluginDescription plugin) { |
| String bundleId = plugin.getKey().getId(); |
| String version = plugin.getKey().getVersion(); |
| |
| String relPath = PLUGINS_DIR + bundleId + "_" + version + ".jar"; |
| if (archives != null && archives.containsKey(relPath)) { |
| copyUrl(archives.get(relPath), new File(target, relPath)); |
| return; |
| } |
| |
| if (plugin.getLocation() == null) { |
| throw new IllegalStateException("Unresolved bundle reference " + bundleId + "_" + version); |
| } |
| |
| ReactorProject bundleProject = plugin.getMavenProject(); |
| File location = null; |
| if (bundleProject != null) { |
| location = bundleProject.getArtifact(plugin.getClassifier()); |
| if (location == null) { |
| throw new IllegalStateException(bundleProject.getId() |
| + " does not provide an artifact with classifier '" + plugin.getClassifier() + "'"); |
| } |
| if (location.isDirectory()) { |
| throw new RuntimeException("Bundle project " + bundleProject.getId() |
| + " artifact is a directory. The build should at least run ``package'' phase."); |
| } |
| version = bundleProject.getExpandedVersion(); |
| } else { |
| location = plugin.getLocation(); |
| } |
| |
| if (unpackPlugins && isDirectoryShape(plugin, location)) { |
| // need a directory |
| File outputJar = getOutputFile(PLUGINS_DIR, bundleId, version, null); |
| |
| if (location.isDirectory()) { |
| copyDir(location, outputJar); |
| } else { |
| unpackJar(location, outputJar); |
| } |
| } else { |
| // need a jar |
| File outputJar = getOutputFile(PLUGINS_DIR, bundleId, version, ".jar"); |
| |
| if (location.isDirectory()) { |
| packDir(location, outputJar); |
| } else { |
| copyFile(location, outputJar); |
| } |
| } |
| } |
| |
| protected boolean isDirectoryShape(PluginDescription plugin, File location) { |
| PluginRef pluginRef = plugin.getPluginRef(); |
| return ((pluginRef != null && pluginRef.isUnpack()) || location.isDirectory()); |
| } |
| |
| private void unpackJar(File location, File outputJar) { |
| ZipUnArchiver unzip; |
| FileLockService fileLockService; |
| try { |
| unzip = (ZipUnArchiver) session.lookup(ZipUnArchiver.ROLE, "zip"); |
| fileLockService = (FileLockService) session.lookup(FileLockService.class.getName()); |
| } catch (ComponentLookupException e) { |
| throw new RuntimeException("Could not lookup required component", e); |
| } |
| |
| outputJar.mkdirs(); |
| |
| if (!outputJar.isDirectory()) { |
| throw new RuntimeException("Could not create output directory " + outputJar.getAbsolutePath()); |
| } |
| |
| unzip.setSourceFile(location); |
| unzip.setDestDirectory(outputJar); |
| FileLocker locker = fileLockService.getFileLocker(location); |
| locker.lock(); |
| try { |
| unzip.extract(); |
| } catch (ArchiverException e) { |
| throw new RuntimeException("Could not unpack jar", e); |
| } finally { |
| locker.release(); |
| } |
| } |
| |
| private void copyDir(File location, File outputJar) { |
| try { |
| FileUtils.copyDirectoryStructure(location, outputJar); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not copy directory", e); |
| } |
| } |
| |
| private void copyUrl(String source, File destination) { |
| try { |
| URL url = new URL(source); |
| try (InputStream is = url.openStream()) { |
| OutputStream os = new BufferedOutputStream(new FileOutputStream(destination)); |
| try { |
| IOUtil.copy(is, os); |
| } finally { |
| os.close(); |
| } |
| } |
| } catch (IOException e) { |
| throw new RuntimeException("Could not copy URL contents", e); |
| } |
| } |
| |
| private void copyFile(File source, File destination) { |
| try { |
| FileUtils.copyFile(source, destination); |
| } catch (IOException e) { |
| throw new RuntimeException("Could not copy file", e); |
| } |
| } |
| |
| private void packDir(File sourceDir, File targetZip) { |
| ZipArchiver archiver; |
| try { |
| archiver = (ZipArchiver) session.lookup(ZipArchiver.ROLE, "zip"); |
| } catch (ComponentLookupException e) { |
| throw new RuntimeException("Unable to resolve ZipArchiver", e); |
| } |
| |
| archiver.setDestFile(targetZip); |
| try { |
| archiver.addDirectory(sourceDir); |
| archiver.createArchive(); |
| } catch (IOException | ArchiverException e) { |
| throw new RuntimeException("Error packing zip", e); |
| } |
| } |
| |
| public void setArchives(Map<String, String> archives) { |
| this.archives = archives; |
| } |
| |
| public void setUnpackPlugins(boolean unpack) { |
| this.unpackPlugins = unpack; |
| } |
| |
| public void setUnpackFeatures(boolean unpack) { |
| this.unpackFeatures = unpack; |
| } |
| } |