/******************************************************************************* * Copyright (c) 2015 Pivotal, Inc. * 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: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.boot.dash.cloudfoundry.packaging; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.ui.jarpackager.IJarExportRunnable; import org.eclipse.jdt.ui.jarpackager.JarPackageData; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.springframework.boot.loader.tools.Libraries; import org.springframework.boot.loader.tools.Library; import org.springframework.boot.loader.tools.LibraryCallback; import org.springframework.boot.loader.tools.LibraryScope; import org.springframework.boot.loader.tools.Repackager; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.ApplicationManifestHandler; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.CloudFoundryUiUtil; import org.springframework.ide.eclipse.boot.dash.cloudfoundry.JavaPackageFragmentRootHandler; import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil; public class CloudApplicationArchiver implements ICloudApplicationArchiver { private IJavaProject javaProject; private String applicationName; private final ApplicationManifestHandler parser; private static final String TEMP_FOLDER_NAME = "springidetempFolderForJavaAppJar"; public CloudApplicationArchiver(IJavaProject javaProject, String applicationName, ApplicationManifestHandler parser) { this.javaProject = javaProject; this.applicationName = applicationName; this.parser = parser; } public File getApplicationArchive(IProgressMonitor monitor) throws Exception { File archive = getArchiveFromManifest(monitor); if (archive == null) { File packagedFile = null; JavaPackageFragmentRootHandler rootResolver = getPackageFragmentRootHandler(javaProject, monitor); final IPackageFragmentRoot[] roots = rootResolver.getPackageFragmentRoots(monitor); if (roots == null || roots.length == 0) { throw ExceptionUtil.coreException("Unable to package project" + javaProject.getElementName() + " as a jar application. Please verify that the project is a valid Java project and contains a main type in source."); } IType mainType = rootResolver.getMainType(monitor); JarPackageData jarPackageData = getJarPackageData(roots, mainType, monitor); // generate a manifest file. Note that manifest files // are only generated in the temporary jar meant for // deployment. // The associated Java project is no modified. jarPackageData.setGenerateManifest(true); // This ensures that folders in output folders appear at root // level // Example: src/main/resources, which is in the project's // classpath, contains non-Java templates folder and // has output folder target/classes. If not exporting output // folder, // templates will be packaged in the jar using this path: // resources/templates // This may cause problems with the application's dependencies // if they are looking for just /templates at top level of the // jar // If exporting output folders, templates folder will be // packaged at top level in the jar. jarPackageData.setExportOutputFolders(true); packagedFile = packageApplication(jarPackageData, monitor); bootRepackage(roots, packagedFile); archive = packagedFile; } return archive; } protected JavaPackageFragmentRootHandler getPackageFragmentRootHandler(IJavaProject javaProject, IProgressMonitor monitor) throws CoreException { return new JavaPackageFragmentRootHandler(javaProject); } protected void bootRepackage(final IPackageFragmentRoot[] roots, File packagedFile) throws Exception { Repackager bootRepackager = new Repackager(packagedFile); bootRepackager.repackage(new Libraries() { public void doWithLibraries(LibraryCallback callBack) throws IOException { for (IPackageFragmentRoot root : roots) { if (root.isArchive()) { File rootFile = new File(root.getPath().toOSString()); if (rootFile.exists()) { callBack.library(new Library(rootFile, LibraryScope.COMPILE)); } } } } }); } protected JarPackageData getJarPackageData(IPackageFragmentRoot[] roots, IType mainType, IProgressMonitor monitor) throws Exception { String filePath = getTempJarPath(); IPath location = new Path(filePath); // Note that if no jar builder is specified in the package data // then a default one is used internally by the data that does NOT // package any jar dependencies. JarPackageData packageData = new JarPackageData(); packageData.setJarLocation(location); // Don't create a manifest. A repackager should determine if a generated // manifest is necessary // or use a user-defined manifest. packageData.setGenerateManifest(false); // Since user manifest is not used, do not save to manifest (save to // manifest saves to user defined manifest) packageData.setSaveManifest(false); packageData.setManifestMainClass(mainType); packageData.setElements(roots); return packageData; } protected File packageApplication(final JarPackageData packageData, IProgressMonitor monitor) throws Exception { int progressWork = 10; final SubMonitor subProgress = SubMonitor.convert(monitor, progressWork); final File[] createdFile = new File[1]; final Exception[] error = new Exception[1]; Display.getDefault().syncExec(new Runnable() { @Override public void run() { Shell shell = CloudFoundryUiUtil.getShell(); IJarExportRunnable runnable = packageData.createJarExportRunnable(shell); try { runnable.run(subProgress); File file = new File(packageData.getJarLocation().toString()); createdFile[0] = file; } catch (InvocationTargetException e) { error[0] = e; } catch (InterruptedException ie) { error[0] = ie; } finally { subProgress.done(); } } }); if (error[0] != null) { throw error[0]; } return createdFile[0]; } public String getTempJarPath() throws Exception { File tempFolder = File.createTempFile(TEMP_FOLDER_NAME, null); tempFolder.delete(); tempFolder.mkdirs(); if (!tempFolder.exists()) { throw ExceptionUtil.coreException("Failed to create temporary jar file when packaging application for deployment: " + tempFolder.getAbsolutePath()); } File targetFile = new File(tempFolder, applicationName + ".jar"); targetFile.deleteOnExit(); String path = new Path(targetFile.getAbsolutePath()).toString(); System.out.println("getTempJarPath => "+path); return path; } public File getArchiveFromManifest(IProgressMonitor monitor) throws Exception { String archivePath = null; // Read the path again instead of deployment info, as a user may be // correcting the path after the module was creating and simply // attempting to push it again without the // deployment wizard if (parser.hasManifest()) { archivePath = parser.getApplicationProperty(applicationName, ApplicationManifestHandler.PATH_PROP, monitor); } File packagedFile = null; if (archivePath != null) { // Only support paths that point to archive files IPath path = new Path(archivePath); if (path.getFileExtension() != null) { if (!path.isAbsolute()) { // Check if it is project relative first IFile projectRelativeFile = javaProject.getProject().getFile(path); if (projectRelativeFile != null && projectRelativeFile.exists()) { packagedFile = projectRelativeFile.getLocation().toFile(); } else { // Case where file exists in file system but is not // present in workspace (i.e. IProject may be out of // synch with file system) IPath projectPath = javaProject.getProject().getLocation(); if (projectPath != null) { archivePath = projectPath.append(archivePath).toString(); File absoluteFile = new File(archivePath); if (absoluteFile.exists() && absoluteFile.canRead()) { packagedFile = absoluteFile; } } } } else { // See if it is an absolute path File absoluteFile = new File(archivePath); if (absoluteFile.exists() && absoluteFile.canRead()) { packagedFile = absoluteFile; } } } // If a path is specified but no file found stop further deployment if (packagedFile == null) { throw ExceptionUtil.coreException( "No file found at: " + path + ". Unable to package the application for deployment"); } else { return packagedFile; } } return null; } }