org.ow2.util.maven.jbuilding.jar.ClientJarProvider Maven / Gradle / Ivy
/**
* OW2 Util
* Copyright (C) 2008 Bull S.A.S.
* Contact: [email protected]
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
* --------------------------------------------------------------------------
* $Id: ClientJarProvider.java 4389 2008-12-15 13:48:57Z alitokmen $
* --------------------------------------------------------------------------
*/
package org.ow2.util.maven.jbuilding.jar;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import org.ow2.util.archive.api.ArchiveException;
import org.ow2.util.archive.api.IArchive;
import org.ow2.util.archive.impl.ArchiveManager;
import org.ow2.util.ee.deploy.api.deployable.IDeployable;
import org.ow2.util.ee.deploy.impl.helper.DeployableHelper;
import org.ow2.util.ee.deploy.impl.helper.UnpackDeployableHelper;
import org.ow2.util.url.URLUtils;
/**
* Generates a big jar with the bundles content.
* @author Guillaume Sauthier
*/
public class ClientJarProvider {
/**
* Source directories.
*/
private List watched;
/**
* Source directories.
*/
private List sources;
/**
* Target file to create if inexistant or outdated.
*/
private File target;
/**
* Generated jar content.
*/
private List content;
/**
* Excluded patterns.
*/
private List exclusions;
/**
* Default constructor.
*/
public ClientJarProvider() {
this.watched = new ArrayList();
this.sources = new ArrayList();
this.content = new ArrayList();
this.exclusions = new ArrayList();
}
/**
* @throws Exception if unable to create the ClassLoader
*/
public void createClientJarFile(final String version, final String mainClass) throws Exception {
// Is there a target somewhere ?
if (!target.exists() || isOutOfDate()) {
// non existant or out of date
// re-creates it
createTargetJarFile(version, mainClass);
}
}
/**
* Initialize the client jar file.
* @throws IOException if libraries pack/unpack fails.
*/
private void createTargetJarFile(final String version, final String mainClass) throws IOException {
// Delete the existing outdated file if there was one.
if (target.exists()) {
target.delete();
}
// Build the META_INF/MANIFEST.MF
content.add("META-INF/MANIFEST.MF");
Manifest m = new Manifest();
Attributes attr = m.getMainAttributes();
attr.put(Attributes.Name.MANIFEST_VERSION, "1.0"); // mandatory!
attr.put(Attributes.Name.IMPLEMENTATION_VERSION, version); // for getVersion()
if (mainClass != null) {
attr.put(Attributes.Name.MAIN_CLASS, mainClass); // set the main class.
}
// Create the target file
OutputStream os = new FileOutputStream(target);
JarOutputStream jos = new JarOutputStream(os, m);
// Iterates over the watched directories
for (File directory : watched) {
// Only accept file ending with .jar
File[] childs = directory.listFiles(new FileFilter() {
public boolean accept(final File pathname) {
return pathname.isFile()
&& pathname.getName().endsWith(".jar")
&& !pathname.equals(target);
}
});
// Iterates over the bundles
for (int i = 0; i < childs.length; i++) {
File bundle = childs[i];
// Add the bundle content in the client.jar
processJarFile(bundle, jos);
}
}
// Iterates over the jar sources
for (File jar : sources) {
processJarFile(jar, jos);
}
// Finish the client.jar
jos.flush();
jos.close();
}
/**
* Extract the jar file content in the client.jar.
* @param bundle Jar file to be processed.
* @param jos Output
* @throws IOException if some pack/unpack operations were not run successfully
*/
@SuppressWarnings("unchecked")
private void processJarFile(final File bundle, final JarOutputStream jos) throws IOException
{
// Look inside of the bundle for all .jar
JarFile jf = new JarFile(bundle);
// Pack this jar
pack(jf, jos);
// Browse the manifest for ClassPath entries
Manifest manifest = jf.getManifest();
String bundleClasspath = manifest.getMainAttributes().getValue("Bundle-Classpath");
if (bundleClasspath != null) {
// need to unpack it
IDeployable dep = null;
try {
IArchive archive = ArchiveManager.getInstance().getArchive(bundle);
dep = DeployableHelper.getDeployable(archive);
dep = UnpackDeployableHelper.unpack(dep, "unpacked");
} catch (Exception e) {
IOException ioe = new IOException("Unable to unpack " + bundle);
ioe.initCause(e);
throw ioe;
}
// Got a Bundle: add its inner classpath
String[] pathElements = bundleClasspath.split(",");
for (int j = 0; j < pathElements.length; j++) {
String path = pathElements[j];
// Do not manage '.'
if (!".".equals(path)) {
URL url = null;
try {
url = dep.getArchive().getResource(path);
} catch (ArchiveException e) {
IOException ioe = new IOException("Unable to get resource " + path + " from " + bundle);
ioe.initCause(e);
throw ioe;
}
// unpack the jar content.
pack(new JarFile(URLUtils.urlToFile(url)), jos);
}
}
}
// Do not forget to close
jf.close();
}
/**
* Pack the source content.
* @param source Jar file.
* @param jos the output stream
* @throws IOException if pack fail
*/
private void pack(final JarFile source, final JarOutputStream jos) throws IOException {
// Pack all the source entries
for (Enumeration e = source.entries(); e.hasMoreElements();) {
JarEntry entry = e.nextElement();
// Do not copy jar files, excluded content or already added entries
if (!entry.getName().endsWith(".jar")
&& !content.contains(entry.getName())
&& !match(entry.getName())) {
InputStream is = source.getInputStream(entry);
JarEntry je = new JarEntry(entry.getName());
jos.putNextEntry(je);
// Copy the content
int read = 0;
final int MAX = 4096;
byte[] buffer = new byte[MAX];
while ((read = is.read(buffer)) != -1) {
jos.write(buffer, 0, read);
}
content.add(entry.getName());
is.close();
}
}
}
/**
* Simple pattern matching method (startsWith).
* @param name name to be matched against exclusions patterns.
* @return true if the name match, false otherwise
*/
private boolean match(final String name) {
for (String pattern : this.exclusions) {
boolean match = name.startsWith(pattern);
if (match) {
return true;
}
}
return false;
}
/**
* @return true if the target if older than one of its sources.
*/
private boolean isOutOfDate() {
final long lastTargetChange = target.lastModified();
boolean aligned = true;
// Iterates over the watched directories
for (File directory : watched) {
// Only accept file ending with .jar
File[] newer = directory.listFiles(new FileFilter() {
public boolean accept(final File pathname) {
return pathname.isFile()
&& pathname.getName().endsWith(".jar")
&& pathname.lastModified() > lastTargetChange;
}
});
if (newer.length != 0) {
// newer files
aligned &= false;
}
}
// Iterates over the sources files
for (File source : sources) {
boolean newer = source.getName().endsWith(".jar")
&& source.lastModified() > lastTargetChange;
if (newer) {
// newer files
aligned &= false;
}
}
return !aligned;
}
/**
* Add a directory whose contained jar files will be processed.
* @param dir watched directory
*/
public void addWatchDirectory(final File dir) {
watched.add(dir);
}
/**
* Add a jar file name to be processed.
* @param source source jar file name
*/
public void addSourceFile(final File source) {
sources.add(source);
}
/**
* Set the target File.
* @param target target File
*/
public void setTargetJarFile(final File target) {
this.target = target;
}
/**
* Add an exclusion pattern.
* @param pattern exclusion pattern
*/
public void addExclusionPattern(final String pattern) {
this.exclusions.add(pattern);
}
}