/* * Milyn - Copyright (C) 2006 - 2010 * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License (version 2.1) as published * by the Free Software Foundation. * * 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: * http://www.gnu.org/licenses/lgpl.txt */ package org.milyn; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.jar.JarInputStream; import java.util.zip.ZipInputStream; import org.milyn.archive.Archive; import org.milyn.assertion.AssertArgument; /** * ResourceMerger is able to merge java archives (jars) and in the process merge * any text based resource files. </p> * * The primary use case for this class is when building the smooks-all.jar which * consists of all the jars in the Smooks project. In Smooks there are a few * types of resource files like 'content-handlers.inf', 'data-decoders.inf' that * exist in mulitple jars. This works well when loading resources from the * classpath as all jars will be searched and read. But when there is only a * single jar then only one of the jars resource files will be in the merged jar * and the others will be ignored. </p> * * @author Daniel Bevenius * */ public class ResourceMerger { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); private List<String> resourcePaths = new ArrayList<String>(); /** * @param resourcePath * The path to the resource that should be merged into the jar * produced by the merge method. */ public ResourceMerger(final String resourcePath) { AssertArgument.isNotNull(resourcePath, "resourcePath"); resourcePaths.add(resourcePath); } /** * @param resourcesPaths * The path to the resource that should be merged into the jar * produced by the merge method. */ public ResourceMerger(final List<String> resourcesPaths) { AssertArgument.isNotNull(resourcePaths, "resourcePaths"); this.resourcePaths.addAll(resourcesPaths); } /** * Will merge the jar files and produce a new jar with the contents of the resources * configured merge together. * * @param jarname * Can be an existing jar file or will be the name of the new * archive created. * @param archives * An array of jar that are to be merge together. * @return {@link Archive} A new jar that will be the result of merging * the jars and will have the contents of the resources merged. * @throws IOException */ public Archive mergeJars(final String jarname, final File... archives) throws IOException { AssertArgument.isNotNull(jarname, "jarname"); AssertArgument.isNotNull(archives, "archives"); final List<Archive> jars = new ArrayList<Archive>(); for (File jar : archives) { jars.add(new Archive(new JarInputStream(new FileInputStream(jar)))); } return mergeJars(jarname, jars); } /** * Will merge the jar files and produce a new jar with the contents of the resources * configured merge together. * * @param jarname * Can be an existing jar file or will be the name of the new * archive created. * @param archives * An array of jar that are to be merge together. * @return {@link Archive} A new jar that will be the result of merging * the jars and will have the contents of the resources merged. * @throws IOException */ public Archive mergeJars(final String jarname, final List<Archive> archives) throws IOException { AssertArgument.isNotNull(jarname, "jarname"); AssertArgument.isNotNull(archives, "archives"); final Archive all = getOrCreateArchive(jarname); final Map<String, List<byte[]>> pathToBytesMap = new HashMap<String, List<byte[]>>(); for (Archive jar : archives) { for (String resourcePath : resourcePaths) { byte[] content = jar.getEntryBytes(resourcePath); if (content != null) { List<byte[]> list = getContentListForResource(pathToBytesMap, resourcePath); list.add(content); pathToBytesMap.put(resourcePath, list); } } all.merge(jar); } return mergeResources(pathToBytesMap, all); } private Archive getOrCreateArchive(final String jarname) throws FileNotFoundException, IOException { final File jarfile = new File(jarname); if (jarfile.exists()) { return new Archive(new ZipInputStream(new FileInputStream(jarfile))); } else { return new Archive(jarname); } } private Archive mergeResources(Map<String, List<byte[]>> pathToBytesMap, Archive jar) throws IOException { final Set<Entry<String, List<byte[]>>> entrySet = pathToBytesMap.entrySet(); for (Entry<String, List<byte[]>> resourceEntries : entrySet) { final String resourcePath = resourceEntries.getKey(); final List<byte[]> nodes = resourceEntries.getValue(); final StringWriter stringWriter = new StringWriter(); for (byte[] content : nodes) { append(content, stringWriter); } jar.addEntry(resourcePath, stringWriter.toString()); } return jar; } private void append(final byte[] content, final StringWriter to) throws IOException { BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(content))); String line; while ((line = in.readLine()) != null) { to.write(line); to.write(LINE_SEPARATOR); } } finally { if (in != null) { in.close(); } } } private List<byte[]> getContentListForResource(final Map<String, List<byte[]>> map, final String resourcePath) { List<byte[]> list = map.get(resourcePath); if (list == null) { list = new ArrayList<byte[]>(); } return list; } }