/*******************************************************************************
* Copyright (c) 2012, 2014 Pivotal Software, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License,
* Version 2.0 (the "License�); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
********************************************************************************/
package org.cloudfoundry.ide.eclipse.server.core.internal;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.cloudfoundry.client.lib.domain.CloudService;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jst.server.core.IJ2EEModule;
import org.eclipse.jst.server.core.IWebModule;
import org.eclipse.osgi.util.NLS;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.IModuleType;
import org.eclipse.wst.server.core.internal.ProgressUtil;
import org.eclipse.wst.server.core.internal.Server;
import org.eclipse.wst.server.core.internal.ServerPlugin;
import org.eclipse.wst.server.core.model.IModuleFile;
import org.eclipse.wst.server.core.model.IModuleFolder;
import org.eclipse.wst.server.core.model.IModuleResource;
import org.eclipse.wst.server.core.util.ModuleFile;
import org.eclipse.wst.server.core.util.ModuleFolder;
import org.eclipse.wst.server.core.util.PublishHelper;
/**
* @author Christian Dupuis
* @author Terry Denney
* @author Steffen Pingel
* @author Leo Dos Santos
* @author Nieraj Singh
*/
@SuppressWarnings("restriction")
public class CloudUtil {
public static final int DEFAULT_MEMORY = 512;
private static final IStatus[] EMPTY_STATUS = new IStatus[0];
public static IWebModule getWebModule(IModule[] modules) {
IModuleType moduleType = modules[0].getModuleType();
if (modules.length == 1 && moduleType != null && "jst.web".equals(moduleType.getId())) { //$NON-NLS-1$
return (IWebModule) modules[0].loadAdapter(IWebModule.class, null);
}
return null;
}
/**
* Creates a partial war file containing only the resources listed in the
* list to filter in. Note that at least one content must be present in the
* list to filter in, otherwise null is returned.
* @param resources
* @param module
* @param server
* @param monitor
* @return partial war file with resources specified in the filter in list,
* or null if filter list is empty or null
* @throws CoreException
*/
public static File createWarFile(List<IModuleResource> allResources, IModule module,
Set<IModuleResource> filterInResources, IProgressMonitor monitor) throws CoreException {
if (allResources == null || allResources.isEmpty() || filterInResources == null || filterInResources.isEmpty()) {
return null;
}
List<IStatus> result = new ArrayList<IStatus>();
try {
File tempDirectory = getTempFolder(module);
// tempFile needs to be in the same location as the war file
// otherwise PublishHelper will fail
String fileName = module.getName() + ".war"; //$NON-NLS-1$
File warFile = new File(tempDirectory, fileName);
warFile.createNewFile();
warFile.deleteOnExit();
List<IModuleResource> newResources = new ArrayList<IModuleResource>();
for (IModuleResource mr : allResources) {
newResources.add(processModuleResource(mr));
}
IStatus[] status = publishZip(allResources, warFile, filterInResources, monitor);
merge(result, status);
throwException(result, "Publishing of : " + module.getName() + " failed"); //$NON-NLS-1$ //$NON-NLS-2$
return warFile;
}
catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR, CloudFoundryPlugin.PLUGIN_ID,
"Failed to create war file: "+ e.getMessage(), e)); //$NON-NLS-1$
}
}
public static File createWarFile(IModule[] modules, Server server, IProgressMonitor monitor) throws CoreException {
List<IStatus> result = new ArrayList<IStatus>();
try {
File tempFile = getTempFolder(modules[0]);
// tempFile needs to be in the same location as the war file
// otherwise PublishHelper will fail
File targetFile = new File(tempFile, modules[0].getName() + ".war"); //$NON-NLS-1$
targetFile.deleteOnExit();
PublishHelper helper = new PublishHelper(tempFile);
ArrayList<IModuleResource> resources = new ArrayList<IModuleResource>(Arrays.asList(server
.getResources(modules)));
IWebModule webModule = getWebModule(modules);
if (webModule != null) {
IModule[] children = webModule.getModules();
if (children != null) {
for (IModule child : children) {
String childUri = null;
if (webModule != null) {
childUri = webModule.getURI(child);
}
IJ2EEModule childModule = (IJ2EEModule) child.loadAdapter(IJ2EEModule.class, monitor);
boolean isBinary = false;
if (childModule != null) {
isBinary = childModule.isBinary();
}
if (isBinary) {
// binaries are copied to the destination
// directory
if (childUri == null) {
childUri = "WEB-INF/lib/" + child.getName(); //$NON-NLS-1$
}
IPath jarPath = new Path(childUri);
File jarFile = new File(tempFile, jarPath.lastSegment());
jarPath = jarPath.removeLastSegments(1);
IModuleResource[] mr = server.getResources(new IModule[] { child });
IStatus[] status = helper.publishToPath(mr, new Path(jarFile.getAbsolutePath()), monitor);
merge(result, status);
resources.add(new ModuleFile(jarFile, jarFile.getName(), jarPath));
}
else {
// other modules are assembled into a jar
if (childUri == null) {
childUri = "WEB-INF/lib/" + child.getName() + ".jar"; //$NON-NLS-1$ //$NON-NLS-2$
}
IPath jarPath = new Path(childUri);
File jarFile = new File(tempFile, jarPath.lastSegment());
jarPath = jarPath.removeLastSegments(1);
IModuleResource[] mr = server.getResources(new IModule[] { child });
IStatus[] status = helper.publishZip(mr, new Path(jarFile.getAbsolutePath()), monitor);
merge(result, status);
resources.add(new ModuleFile(jarFile, jarFile.getName(), jarPath));
}
}
}
}
List<IModuleResource> newResources = new ArrayList<IModuleResource>();
for (IModuleResource mr : resources) {
newResources.add(processModuleResource(mr));
}
IStatus[] status = helper.publishZip(newResources.toArray(new IModuleResource[0]),
new Path(targetFile.getAbsolutePath()), monitor);
merge(result, status);
throwException(result, "Publishing of " + modules[0].getName() + " failed"); //$NON-NLS-1$ //$NON-NLS-2$
return targetFile;
}
catch (IOException e) {
throw new CoreException(new Status(IStatus.ERROR, CloudFoundryPlugin.PLUGIN_ID,
"Failed to create war file: " + e.getMessage(), e)); //$NON-NLS-1$
}
}
private static IModuleResource processModuleResource(IModuleResource or) {
if (or instanceof IModuleFolder) {
IModuleFolder of = (IModuleFolder) or;
IPath p = of.getModuleRelativePath();
if (p.isAbsolute()) {
p = p.makeRelative();
}
ModuleFolder nf = new ModuleFolder(null, of.getName(), p);
List<IModuleResource> c = new ArrayList<IModuleResource>();
for (IModuleResource mc : of.members()) {
c.add(processModuleResource(mc));
}
nf.setMembers(c.toArray(new IModuleResource[0]));
return nf;
}
return or;
}
private static File getTempFolder(IModule module) throws IOException {
File tempFile = File.createTempFile("tempFileForWar", null); //$NON-NLS-1$
tempFile.delete();
tempFile.mkdirs();
return tempFile;
}
protected static void throwException(List<IStatus> status, String message) throws CoreException {
if (status == null || status.size() == 0) {
return;
}
throw new CoreException(new MultiStatus(CloudFoundryPlugin.PLUGIN_ID, 0, status.toArray(new IStatus[0]),
message, null));
}
public static IStatus[] publishZip(List<IModuleResource> allResources, File tempFile,
Set<IModuleResource> filterInFiles, IProgressMonitor monitor) {
monitor = ProgressUtil.getMonitorFor(monitor);
try {
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(tempFile));
ZipOutputStream zout = new ZipOutputStream(bout);
addZipEntries(zout, allResources, filterInFiles);
zout.close();
}
catch (CoreException e) {
return new IStatus[] { e.getStatus() };
}
catch (Exception e) {
return new Status[] { new Status(IStatus.ERROR, ServerPlugin.PLUGIN_ID, 0, NLS.bind(
Messages.ERROR_CREATE_ZIP, tempFile.getName(), e.getLocalizedMessage()), e) };
}
finally {
if (tempFile != null && tempFile.exists())
tempFile.deleteOnExit();
}
return EMPTY_STATUS;
}
private static final int BUFFER = 65536;
private static byte[] buf = new byte[BUFFER];
public static String getZipRelativeName(IModuleResource resource) {
IPath path = resource.getModuleRelativePath().append(resource.getName());
String entryPath = path.toPortableString();
if (resource instanceof IModuleFolder && !entryPath.endsWith("/")) { //$NON-NLS-1$
entryPath += '/';
}
return entryPath;
}
private static void addZipEntries(ZipOutputStream out, List<IModuleResource> allResources,
Set<IModuleResource> filterInFiles) throws Exception {
if (allResources == null)
return;
for (IModuleResource resource : allResources) {
if (resource instanceof IModuleFolder) {
IModuleResource[] folderResources = ((IModuleFolder) resource).members();
String entryPath = getZipRelativeName(resource);
ZipEntry zipEntry = new ZipEntry(entryPath);
long timeStamp = 0;
IContainer folder = (IContainer) resource.getAdapter(IContainer.class);
if (folder != null) {
timeStamp = folder.getLocalTimeStamp();
}
if (timeStamp != IResource.NULL_STAMP && timeStamp != 0) {
zipEntry.setTime(timeStamp);
}
out.putNextEntry(zipEntry);
out.closeEntry();
addZipEntries(out, Arrays.asList(folderResources), filterInFiles);
continue;
}
IModuleFile moduleFile = (IModuleFile) resource;
// Only add files that are in the filterInList
if (!filterInFiles.contains(moduleFile)) {
continue;
}
String entryPath = getZipRelativeName(resource);
ZipEntry zipEntry = new ZipEntry(entryPath);
InputStream input = null;
long timeStamp = 0;
IFile iFile = (IFile) moduleFile.getAdapter(IFile.class);
if (iFile != null) {
timeStamp = iFile.getLocalTimeStamp();
input = iFile.getContents();
}
else {
File file = (File) moduleFile.getAdapter(File.class);
timeStamp = file.lastModified();
input = new FileInputStream(file);
}
if (timeStamp != IResource.NULL_STAMP && timeStamp != 0) {
zipEntry.setTime(timeStamp);
}
out.putNextEntry(zipEntry);
try {
int n = 0;
while (n > -1) {
n = input.read(buf);
if (n > 0) {
out.write(buf, 0, n);
}
}
}
finally {
input.close();
}
out.closeEntry();
}
}
/**
* @param cloudService
* @return
*/
public static String getServiceVendor(CloudService cloudService) {
// Handle both v1 and v2 services. For v2, vendors are "labels".
return cloudService.getLabel();
}
/**
* Creates a temporary folder and file with the given names. It is the
* responsibility of the caller to properly dispose the folder and file
* after it is created
* @param folderName
* @param fileName
* @return
* @throws IOException
*/
public static File createTemporaryFile(String folderName, String fileName) throws IOException {
File tempFolder = File.createTempFile(folderName, null);
// Delete an existing one
tempFolder.delete();
tempFolder.mkdirs();
tempFolder.setExecutable(true);
File targetFile = new File(tempFolder, fileName);
// delete existing file
targetFile.delete();
targetFile.createNewFile();
targetFile.setExecutable(true);
targetFile.setWritable(true);
return targetFile;
}
public static void merge(List<IStatus> result, IStatus[] status) {
if (result == null || status == null || status.length == 0) {
return;
}
int size = status.length;
for (int i = 0; i < size; i++) {
result.add(status[i]);
}
}
}