Java tutorial
/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under 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. */ package org.apache.apex.common.util; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.security.CodeSource; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; @InterfaceAudience.Public @InterfaceStability.Evolving /** * @since 3.6.0 */ public class JarHelper { private static final Logger logger = LoggerFactory.getLogger(JarHelper.class); private static final String APEX_DEPENDENCIES = "apex-dependencies"; private final Map<URL, String> sourceToJar = new HashMap<>(); public static String createJar(String prefix, File dir, boolean deleteOnExit) throws IOException { if (!dir.exists() || !dir.isDirectory()) { throw new IllegalArgumentException(String.format("dir %s must be an existing directory.", dir)); } File temp = File.createTempFile(prefix, ".jar"); if (deleteOnExit) { temp.deleteOnExit(); } new JarCreator(temp).createJar(dir); return temp.getAbsolutePath(); } public String getJar(Class<?> jarClass, boolean makeJarFromFolder) { String jar = null; final CodeSource codeSource = jarClass.getProtectionDomain().getCodeSource(); if (codeSource != null) { URL location = codeSource.getLocation(); jar = sourceToJar.get(location); if (jar == null) { // don't create jar file from folders multiple times if ("jar".equals(location.getProtocol())) { try { location = ((JarURLConnection) location.openConnection()).getJarFileURL(); } catch (IOException e) { throw new AssertionError("Cannot resolve jar file for " + jarClass, e); } } if ("file".equals(location.getProtocol())) { jar = location.getFile(); final File dir = new File(jar); if (dir.isDirectory()) { if (!makeJarFromFolder) { throw new AssertionError( "Cannot resolve jar file for " + jarClass + ". URL " + location); } try { jar = createJar("apex-", dir, false); } catch (IOException e) { throw new AssertionError( "Cannot resolve jar file for " + jarClass + ". URL " + location, e); } } } else { throw new AssertionError("Cannot resolve jar file for " + jarClass + ". URL " + location); } sourceToJar.put(location, jar); logger.debug("added sourceLocation {} as {}", location, jar); } if (jar == null) { throw new AssertionError("Cannot resolve jar file for " + jarClass); } } return jar; } public String getJar(Class<?> jarClass) { return getJar(jarClass, true); } /** * Returns a full path to the jar-file that contains the given class and all full paths to dependent jar-files * that are defined in the property "apex-dependencies" of the manifest of the root jar-file. * If the class is an independent file the method makes jar file from the folder that contains the class * @param jarClass Class * @param makeJarFromFolder True if the method should make jar from folder that contains the independent class * @param addJarDependencies True if the method should include dependent jar files * @return Set of names of the jar-files */ public Set<String> getJars(Class<?> jarClass, boolean makeJarFromFolder, boolean addJarDependencies) { String jar = getJar(jarClass, makeJarFromFolder); Set<String> set = new HashSet<>(); if (jar != null) { set.add(jar); if (addJarDependencies) { try { getDependentJarsFromManifest(new JarFile(jar), set); } catch (IOException ex) { logger.warn("Cannot open Jar-file {}", jar); } } } return set; } /** * Returns a full path to the jar-file that contains the given class and all full paths to dependent jar-files * that are defined in the property "apex-dependencies" of the manifest of the root jar-file. * If the class is an independent file the method makes jar file from the folder that contains the class * @param jarClass Class * @return Set of names of the jar-files */ public Set<String> getJars(Class<?> jarClass) { return getJars(jarClass, true, true); } /** * Adds dependent jar-files from manifest to the target list of jar-files * @param jarFile Jar file * @param set Set of target jar-files * @throws IOException */ public void getDependentJarsFromManifest(JarFile jarFile, Set<String> set) throws IOException { String value = jarFile.getManifest().getMainAttributes().getValue(APEX_DEPENDENCIES); if (!StringUtils.isEmpty(value)) { Path folderPath = Paths.get(jarFile.getName()).getParent(); for (String jar : value.split(",")) { File file = folderPath.resolve(jar).toFile(); if (file.exists()) { set.add(file.getPath()); logger.debug("The file {} was added as a dependent of the jar {}", file.getPath(), jarFile.getName()); } else { logger.warn("The dependent file {} of the jar {} does not exist", file.getPath(), jarFile.getName()); } } } } private static class JarCreator { private final JarOutputStream jos; private JarCreator(File file) throws IOException { jos = new JarOutputStream(new FileOutputStream(file)); } private void createJar(File dir) throws IOException { try { File manifestFile = new File(dir, JarFile.MANIFEST_NAME); if (!manifestFile.exists()) { jos.putNextEntry(new JarEntry(JarFile.MANIFEST_NAME)); new Manifest().write(jos); jos.closeEntry(); } else { addEntry(manifestFile, JarFile.MANIFEST_NAME); } final Path root = dir.toPath(); Files.walkFileTree(root, new SimpleFileVisitor<Path>() { String relativePath; @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { relativePath = root.relativize(dir).toString(); if (!relativePath.isEmpty()) { if (!relativePath.endsWith("/")) { relativePath += "/"; } addEntry(dir.toFile(), relativePath); } return super.preVisitDirectory(dir, attrs); } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { String name = relativePath + file.getFileName().toString(); if (!JarFile.MANIFEST_NAME.equals(name)) { addEntry(file.toFile(), relativePath + file.getFileName().toString()); } return super.visitFile(file, attrs); } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { relativePath = root.relativize(dir.getParent()).toString(); if (!relativePath.isEmpty() && !relativePath.endsWith("/")) { relativePath += "/"; } return super.postVisitDirectory(dir, exc); } }); } finally { jos.close(); } } private void addEntry(File file, String name) throws IOException { final JarEntry ze = new JarEntry(name); ze.setTime(file.lastModified()); jos.putNextEntry(ze); if (file.isFile()) { try (final FileInputStream input = new FileInputStream(file)) { IOUtils.copy(input, jos); } } jos.closeEntry(); } } }