Java tutorial
/***************************************************************************************** * *** BEGIN LICENSE BLOCK ***** * * Version: MPL 2.0 * * echocat NoDoodle, Copyright (c) 2010-2012 echocat * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * *** END LICENSE BLOCK ***** ****************************************************************************************/ package org.echocat.nodoodle.transport; import org.echocat.nodoodle.Handler; import org.echocat.nodoodle.NodoodleInformationFactory; import org.echocat.nodoodle.resources.JarResource; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import java.io.*; import java.util.*; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import static org.echocat.nodoodle.transport.TransportConstants.*; import static java.util.jar.Attributes.Name.*; public class HandlerPacker { private final NodoodleInformationFactory _informationFactory; private Collection<Class<?>> _dependencyTypes; private Collection<JarResource> _jarResourceDependencies; public HandlerPacker(NodoodleInformationFactory informationFactory) { if (informationFactory == null) { throw new NullPointerException(); } _informationFactory = informationFactory; } public void pack(Handler<?> handler, OutputStream to) throws IOException { if (handler == null) { throw new NullPointerException(); } if (!(handler instanceof Serializable)) { throw new IllegalArgumentException(handler + " is not of type " + Serializable.class.getName() + "."); } if (to == null) { throw new NullPointerException(); } final String dataFileName = getDataFileName(); // noinspection unchecked final Class<? extends Handler<?>> handlerType = (Class<? extends Handler<?>>) handler.getClass(); final Map<String, JarResource> fileNameToJarResource = buildFileNameToJarResource(); final Manifest manifest = createManifest(dataFileName, fileNameToJarResource.keySet()); final JarOutputStream jar = new JarOutputStream(to, manifest); final Set<String> namesOfWrittenTypeFiles = new HashSet<String>(); writeTypes(handlerType, jar, namesOfWrittenTypeFiles); writeJarResourcesDependencies(fileNameToJarResource, jar); writeData(handler, jar, dataFileName); jar.finish(); } protected Manifest createManifest(String dataFileName, Collection<String> dependencyFileNames) { if (dataFileName == null) { throw new NullPointerException(); } if (dependencyFileNames == null) { throw new NullPointerException(); } final Manifest manifest = new Manifest(); final Attributes mainAttributes = manifest.getMainAttributes(); mainAttributes.put(MANIFEST_VERSION, "1.0"); mainAttributes.put(new Attributes.Name("Created-By"), getSmappserVersionString()); mainAttributes.put(CLASS_PATH, StringUtils.join(dependencyFileNames, ' ')); final Attributes extensionAttributes = new Attributes(); extensionAttributes.put(MANIFEST_DATE_FILE, dataFileName); manifest.getEntries().put(MANIFEST_EXTENSION_NAME, extensionAttributes); return manifest; } private void writeTypes(Class<? extends Handler<?>> handlerType, JarOutputStream jar, Set<String> namesOfWrittenTypeFiles) throws IOException { if (handlerType == null) { throw new NullPointerException(); } if (jar == null) { throw new NullPointerException(); } if (namesOfWrittenTypeFiles == null) { throw new NullPointerException(); } writeType(handlerType, jar, namesOfWrittenTypeFiles); if (_dependencyTypes != null) { for (Class<?> dependencyType : _dependencyTypes) { writeType(dependencyType, jar, namesOfWrittenTypeFiles); } } } protected void writeType(Class<?> type, JarOutputStream jar, Set<String> namesOfWrittenFiles) throws IOException { if (jar == null) { throw new NullPointerException(); } if (namesOfWrittenFiles == null) { throw new NullPointerException(); } if (type != null) { final String fileName = type.getName().replace('.', '/') + ".class"; // noinspection UnnecessaryLocalVariable final String sourceFileName = fileName; final String targetFileName = TransportConstants.CLASSES_PREFIX + fileName; if (namesOfWrittenFiles.contains(targetFileName)) { throw new IllegalStateException("The target file '" + targetFileName + "' was already written to jar file. Is '" + type + "' the handler and in the list of dependencyTypes or is dependencyTypes simply not unique?"); } final InputStream inputStream = type.getClassLoader().getResourceAsStream(sourceFileName); try { final JarEntry e = new JarEntry(targetFileName); jar.putNextEntry(e); IOUtils.copy(inputStream, jar); jar.closeEntry(); } finally { inputStream.close(); } namesOfWrittenFiles.add(targetFileName); } } protected void writeJarResourcesDependencies(Map<String, JarResource> fileNameToJarResource, JarOutputStream jar) throws IOException { if (fileNameToJarResource == null) { throw new NullPointerException(); } if (jar == null) { throw new NullPointerException(); } if (_jarResourceDependencies != null) { for (Map.Entry<String, JarResource> fileNameAndJarResource : fileNameToJarResource.entrySet()) { writeJarResource(fileNameAndJarResource.getValue(), jar, fileNameAndJarResource.getKey()); } } } protected void writeJarResource(JarResource jarResource, JarOutputStream jar, String fileName) throws IOException { if (jar == null) { throw new NullPointerException(); } if (fileName == null) { throw new NullPointerException(); } if (jarResource != null) { final InputStream inputStream = jarResource.getResourceAsJar(); try { final JarEntry e = new JarEntry(fileName); jar.putNextEntry(e); IOUtils.copy(inputStream, jar); jar.closeEntry(); } finally { inputStream.close(); } } } protected void writeData(Handler<?> handler, JarOutputStream jar, String dataFileName) throws IOException { if (handler == null) { throw new NullPointerException(); } if (jar == null) { throw new NullPointerException(); } final JarEntry entry = new JarEntry(dataFileName); jar.putNextEntry(entry); final ObjectOutputStream oos = new ObjectOutputStream(jar); oos.writeObject(handler); oos.flush(); jar.closeEntry(); } protected Map<String, JarResource> buildFileNameToJarResource() throws IOException { final Map<String, JarResource> fileNameToJarResource = new LinkedHashMap<String, JarResource>(); if (_jarResourceDependencies != null) { for (JarResource jarResource : _jarResourceDependencies) { String targetFileName = LIB_PREFIX + jarResource.getName(); int i = 1; while (fileNameToJarResource.containsKey(targetFileName)) { targetFileName = LIB_PREFIX + (i++) + "-" + jarResource.getName(); } fileNameToJarResource.put(targetFileName, jarResource); } } return Collections.unmodifiableMap(fileNameToJarResource); } protected String getDataFileName() { return "data.bin"; } protected String getSmappserVersionString() { final String version = _informationFactory.getImplementationVersion(); return "nodoodle " + (version != null ? version : "?"); } public Collection<Class<?>> getDependencyTypes() { return _dependencyTypes; } public void setDependencyTypes(Collection<Class<?>> dependencyTypes) { _dependencyTypes = dependencyTypes; } public Collection<JarResource> getJarResourceDependencies() { return _jarResourceDependencies; } public void setJarResourceDependencies(Collection<JarResource> jarResourceDependencies) { _jarResourceDependencies = jarResourceDependencies; } }