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.classloading.FileClassLoader; import org.apache.commons.io.IOUtils; import java.io.*; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.jar.*; import java.util.zip.ZipEntry; import static org.echocat.nodoodle.transport.TransportConstants.*; public class HandlerUnpacker { private static final Field ZIP_ENTRY_NAME_FIELD; static { try { ZIP_ENTRY_NAME_FIELD = ZipEntry.class.getDeclaredField("name"); ZIP_ENTRY_NAME_FIELD.setAccessible(true); // Access check... ZIP_ENTRY_NAME_FIELD.set(new ZipEntry("test"), "test2"); } catch (Exception e) { final UnsupportedClassVersionError toThrow = new UnsupportedClassVersionError( "It was not possible to access the name field of '" + ZipEntry.class.getName() + "'."); toThrow.initCause(e); throw toThrow; } } public static class Result { private final String _id; private final File _targetDirectory; private final ClassLoader _classLoader; private final Handler<?> _handler; private final Date _created; private Result(String id, File targetDirectory, ClassLoader classLoader, Handler<?> handler) { if (id == null) { throw new NullPointerException(); } if (targetDirectory == null) { throw new NullPointerException(); } if (classLoader == null) { throw new NullPointerException(); } if (handler == null) { throw new NullPointerException(); } _id = id; _targetDirectory = targetDirectory; _classLoader = classLoader; _handler = handler; _created = new Date(); } public String getId() { return _id; } public File getTargetDirectory() { return _targetDirectory; } public ClassLoader getClassLoader() { return _classLoader; } public Handler<?> getHandler() { return _handler; } public Date getCreated() { return _created; } } private final ClassLoader _classLoader; private File _workDirectory = new File(System.getProperty("java.io.tmpdir", "tmp"), "nodoodle"); public HandlerUnpacker() { this(null); } public HandlerUnpacker(ClassLoader classLoader) { _classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader(); } public Result unpack(String id, InputStream inputStream) throws Exception { if (id == null) { throw new NullPointerException(); } if (inputStream == null) { throw new NullPointerException(); } final File targetDirectory = new File(_workDirectory, id).getCanonicalFile(); if (!targetDirectory.mkdirs() && !targetDirectory.isDirectory()) { throw new IOException("Could not create " + targetDirectory + "."); } final SplitResult splitResult = splitMultipleJarIntoJars(inputStream, targetDirectory); final ClassLoader classLoader = new FileClassLoader(splitResult.jarFiles, _classLoader); final Handler<?> handler = loadInstance(splitResult, classLoader); return new Result(id, targetDirectory, classLoader, handler); } protected Handler<?> loadInstance(SplitResult splitResult, final ClassLoader classLoader) throws IOException { if (splitResult == null) { throw new NullPointerException(); } if (classLoader == null) { throw new NullPointerException(); } final Attributes extensionAttributes = splitResult.manifest.getAttributes(MANIFEST_EXTENSION_NAME); if (extensionAttributes == null) { throw new IOException("There is no valid manifest in the provided jar."); } final Object dataFile = extensionAttributes.get(MANIFEST_DATE_FILE); if (!(dataFile instanceof String)) { throw new IOException("There is no valid " + MANIFEST_DATE_FILE + " attribute defined in manifest."); } final InputStream inputStream = classLoader.getResourceAsStream((String) dataFile); if (inputStream == null) { throw new FileNotFoundException("Could not find the resource " + dataFile + " which was defined by " + MANIFEST_DATE_FILE + " manifest entry of provided jar file."); } try { final ObjectInputStream ois = new ObjectInputStream(inputStream) { @Override protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException { return classLoader.loadClass(desc.getName()); } }; final Object result; try { result = ois.readObject(); } catch (Exception e) { throw new IOException("Could not parse the resource " + dataFile + " which was defined by " + MANIFEST_DATE_FILE + " manifest entry of provided jar file.", e); } if (!(result instanceof Handler)) { throw new IOException("The resource " + dataFile + " which was defined by " + MANIFEST_DATE_FILE + " manifest entry of provided jar file does contain " + result + " and not an object of type " + Handler.class.getName() + "."); } return (Handler) result; } finally { IOUtils.closeQuietly(inputStream); } } @SuppressWarnings({ "ProtectedInnerClass", "InstanceVariableNamingConvention", "ParameterHidesMemberVariable" }) protected class SplitResult { public final Collection<File> jarFiles; public final Manifest manifest; protected SplitResult(Collection<File> jarFiles, Manifest manifest) { if (jarFiles == null) { throw new NullPointerException(); } if (manifest == null) { throw new NullPointerException(); } this.jarFiles = jarFiles; this.manifest = manifest; } } protected SplitResult splitMultipleJarIntoJars(InputStream inputStream, File targetDirectory) throws IOException { if (inputStream == null) { throw new NullPointerException(); } if (targetDirectory == null) { throw new NullPointerException(); } final Collection<File> jarFiles = new ArrayList<File>(); final File mainJarFile = getMainJarFile(targetDirectory); jarFiles.add(mainJarFile); final JarInputStream jarInput = new JarInputStream(inputStream); final Manifest manifest = jarInput.getManifest(); final FileOutputStream mainJarFileStream = new FileOutputStream(mainJarFile); try { final JarOutputStream jarOutput = new JarOutputStream(mainJarFileStream, manifest); try { JarEntry entry = jarInput.getNextJarEntry(); while (entry != null) { final String entryName = entry.getName(); if (!entry.isDirectory() && entryName.startsWith("lib/")) { final File targetFile = new File(targetDirectory, entryName); if (!targetFile.getParentFile().mkdirs()) { throw new IOException("Could not create parent directory of " + targetFile + "."); } final OutputStream outputStream = new FileOutputStream(targetFile); try { IOUtils.copy(jarInput, outputStream); } finally { IOUtils.closeQuietly(outputStream); } jarFiles.add(targetFile); } else { if (entryName.startsWith(TransportConstants.CLASSES_PREFIX) && entryName.length() > TransportConstants.CLASSES_PREFIX.length()) { try { ZIP_ENTRY_NAME_FIELD.set(entry, entryName.substring(CLASSES_PREFIX.length())); } catch (IllegalAccessException e) { throw new RuntimeException("Could not set " + ZIP_ENTRY_NAME_FIELD + ".", e); } } jarOutput.putNextEntry(entry); IOUtils.copy(jarInput, jarOutput); jarOutput.closeEntry(); } entry = jarInput.getNextJarEntry(); } } finally { IOUtils.closeQuietly(jarOutput); } } finally { IOUtils.closeQuietly(mainJarFileStream); } return new SplitResult(Collections.unmodifiableCollection(jarFiles), manifest); } protected File getMainJarFile(File targetDirectory) { return new File(targetDirectory, "main.jar"); } public File getWorkDirectory() { return _workDirectory; } public void setWorkDirectory(File workDirectory) { if (workDirectory == null) { throw new NullPointerException(); } _workDirectory = workDirectory; } }