/******************************************************************************* * Copyright (c) 2011 Gerd Wuetherich (gerd@gerd-wuetherich.de). * 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: * Gerd Wuetherich (gerd@gerd-wuetherich.de) - initial API and implementation ******************************************************************************/ package org.bundlemaker.core.osgi.exporter.bundle; import static java.lang.String.format; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.Manifest; import org.bundlemaker.core.common.IResource; import org.bundlemaker.core.common.Resource2JarFileExporter; import org.bundlemaker.core.common.ResourceType; import org.bundlemaker.core.exporter.IModuleExporterContext; import org.bundlemaker.core.exporter.ITemplateProvider; import org.bundlemaker.core.exporter.util.ModuleExporterUtils; import org.bundlemaker.core.osgi.exporter.AbstractManifestAwareExporter; import org.bundlemaker.core.osgi.manifest.IBundleManifestCreator; import org.bundlemaker.core.osgi.manifest.IManifestPreferences; import org.bundlemaker.core.osgi.utils.JarFileManifestWriter; import org.bundlemaker.core.osgi.utils.ManifestUtils; import org.bundlemaker.core.project.IProjectContentResource; import org.bundlemaker.core.resource.IModularizedSystem; import org.bundlemaker.core.resource.IModule; import org.bundlemaker.core.resource.IModuleResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.virgo.bundlor.ManifestWriter; /** * <p> * </p> * * @author Gerd Wütherich (gerd@gerd-wuetherich.de) */ public class JarFileBundleExporter extends AbstractManifestAwareExporter { /** * If set to <tt>true</tt> sources will be included in the resulting JAR (beneath OSGI-OPT/src subfolder) * * <p>Defaults to <tt>false</tt> */ private boolean _includeSources = false; private boolean _createEclipseSourceBundle = false; /** * <p> * Creates a new instance of type {@link JarFileBundleExporter}. * </p> */ public JarFileBundleExporter() { super(null, null, null); } /** * <p> * Creates a new instance of type {@link JarFileBundleExporter}. * </p> * * @param templateProvider */ public JarFileBundleExporter(ITemplateProvider templateProvider, IBundleManifestCreator bundleManifestCreator, IManifestPreferences manifestPreferences) { super(templateProvider, bundleManifestCreator, manifestPreferences); } /** * {@inheritDoc} * * @throws CoreException */ @Override protected void doExport(IProgressMonitor progressMonitor) throws CoreException { // create new file if repackaging is required if (isIncludeSources() || !getTemplateProvider().getAdditionalResources(getCurrentModule(), getCurrentModularizedSystem(), getCurrentContext()).isEmpty() || ModuleExporterUtils.requiresRepackaging(getCurrentModule(), ResourceType.BINARY)) { // create new File createNewJarFile(); } // copy (and patch) the original else { // get the root file File rootFile = ModuleExporterUtils.getRootFile(getCurrentModule(), ResourceType.BINARY); // get the manifest writer ManifestWriter manifestWriter = new JarFileManifestWriter(rootFile, getDestinationJarFile()); // manifestWriter.write(getManifestContents()); } if (isCreateEclipseSourceBundle()) { createEclipseSourceBundle(); } } private void createEclipseSourceBundle() throws CoreException { File sourceFile = getDestinationSourceJarFile(); OutputStream outputStream; try { outputStream = new FileOutputStream(sourceFile); Manifest manifest = createSourceManifest(); Resource2JarFileExporter.createJarArchive(getCurrentModule().getResources(ResourceType.SOURCE), manifest, null, outputStream); outputStream.close(); } catch (IOException e) { throw new CoreException(new Status(IStatus.ERROR, "", "Could not create Source Bundle " + sourceFile + ": " + e, e)); } } /** * @return */ private Manifest createSourceManifest() { IModule currentModule = getCurrentModule(); String sourceBundleName = currentModule.getModuleIdentifier().getName()+".source"; String bundleVersion = currentModule.getModuleIdentifier().getVersion(); Manifest manifest = new Manifest(); Attributes mainAttributes = manifest.getMainAttributes(); mainAttributes.putValue("Bundle-ManifestVersion", "2"); mainAttributes.putValue("Bundle-SymbolicName", sourceBundleName); mainAttributes.putValue("Bundle-Version", bundleVersion); String sourceBundleHeader = format("%s;version=\"%s\"", currentModule.getModuleIdentifier().getName(), bundleVersion); mainAttributes.putValue("Eclipse-SourceBundle", sourceBundleHeader); return manifest; } /** * <p> * </p> * * @throws CoreException */ private void createNewJarFile() throws CoreException { try { // create the output stream OutputStream outputStream = createOutputStream(getCurrentModularizedSystem(), getCurrentModule(), getCurrentContext()); // Set<IResource> resourceKeys = getTemplateProvider().getAdditionalResources(getCurrentModule(), getCurrentModularizedSystem(), getCurrentContext()); Set<IResource> additionalResources; if (isIncludeSources()) { additionalResources = new HashSet<IResource>(); // add files from template provider additionalResources.addAll(resourceKeys); // add sources Set<? extends IProjectContentResource> sources = getCurrentModule().getResources(ResourceType.SOURCE); additionalResources.addAll(wrapSourceResources(sources)); } else { // add only resources from template provider additionalResources = resourceKeys; } // export the jar archive Resource2JarFileExporter.createJarArchive(getCurrentModule().getResources(ResourceType.BINARY), ManifestUtils.toManifest(getManifestContents()), additionalResources, outputStream); // close the output stream outputStream.close(); } catch (IOException e) { // TODO e.printStackTrace(); throw new CoreException(new Status(IStatus.ERROR, "", "")); } catch (Exception e) { // TODO e.printStackTrace(); throw new CoreException(new Status(IStatus.ERROR, "", "")); } } /** * "virtually" moves the specified resources to OSGI-OPT/src * @param sources * @return */ private Collection<? extends IResource> wrapSourceResources(Set<? extends IProjectContentResource> sources) { Set<IResource> movedSources = new HashSet<IResource>(); // wrap sources in new IReadableResource that has it's path pointing to OSGI-OPT/src for (final IResource source : sources) { final IResource movedSource = new IResource() { @Override public String getRoot() { return source.getRoot(); } @Override public String getPath() { return "OSGI-OPT/src/" + source.getPath(); } @Override public String getName() { return source.getName(); } @Override public String getDirectory() { return "OSGI-OPT/src/" + getDirectory(); } @Override public byte[] getContent() { return source.getContent(); } @Override public long getCurrentTimestamp() { return source.getCurrentTimestamp(); } }; // add to result movedSources.add(movedSource); } // return return movedSources; } /** * <p> * </p> * * @param modularizedSystem * @param module * @param context * @return * @throws Exception */ protected OutputStream createOutputStream(IModularizedSystem modularizedSystem, IModule module, IModuleExporterContext context) throws Exception { File targetFile = getDestinationJarFile(); // return a new file output stream return new FileOutputStream(targetFile); } protected File getDestinationFile(String name) { // create the target file File targetFile = new File(getCurrentContext().getDestinationDirectory(), name); // create the parent directories if (!targetFile.getParentFile().exists()) { targetFile.getParentFile().mkdirs(); } return targetFile; } /** * <p>Returns the destination Jar File * </p> * * @param modularizedSystem * @param module * @param context * @return */ protected File getDestinationJarFile() { return getDestinationFile(computeJarFileName(getCurrentModule())); } protected File getDestinationSourceJarFile() { return getDestinationFile(computeSourceJarFileName(getCurrentModule())); } protected String computeSourceJarFileName(IModule module) { return module.getModuleIdentifier().getName() + ".source_" + module.getModuleIdentifier().getVersion() + ".jar"; } /** * <p> * </p> * * @param module * @return */ protected String computeJarFileName(IModule module) { // return module.getModuleIdentifier().getName() + "_" + module.getModuleIdentifier().getVersion() + ".jar"; } /** * @return the includeSources */ public boolean isIncludeSources() { return _includeSources; } /** * Set to true to include sources in the jar file (in OSGI-OPT/src) * * @param includeSources * the includeSources to set */ public void setIncludeSources(boolean includeSources) { _includeSources = includeSources; } /** * @param createEclipseSourceBundle the createEclipseSourceBundle to set */ public void setCreateEclipseSourceBundle(boolean createEclipseSourceBundle) { _createEclipseSourceBundle = createEclipseSourceBundle; } /** * @return the createEclipseSourceBundle */ public boolean isCreateEclipseSourceBundle() { return _createEclipseSourceBundle; } }