001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.reef.util; 020 021import org.apache.commons.compress.utils.IOUtils; 022 023import java.io.*; 024import java.util.jar.JarEntry; 025import java.util.jar.JarOutputStream; 026import java.util.jar.Manifest; 027import java.util.logging.Level; 028import java.util.logging.Logger; 029 030/** 031 * Helper class to create JAR files. 032 */ 033public class JARFileMaker implements AutoCloseable { 034 035 private static final Logger LOG = Logger.getLogger(JARFileMaker.class.getName()); 036 037 private final JarOutputStream jarOutputStream; 038 039 public JARFileMaker(final File outputFile) throws IOException { 040 this(outputFile, null); 041 } 042 043 public JARFileMaker(final File outputFile, final Manifest manifest) throws IOException { 044 LOG.log(Level.FINER, "Output jar: {0}", outputFile); 045 final FileOutputStream outputStream = new FileOutputStream(outputFile); 046 this.jarOutputStream = manifest == null ? 047 new JarOutputStream(outputStream) : new JarOutputStream(outputStream, manifest); 048 } 049 050 /** 051 * Adds a file to the JAR. If inputFile is a folder, it will be added recursively. 052 * @param inputFile file or directory to be added to the jar. 053 * @throws IOException if cannot create a jar. 054 */ 055 public JARFileMaker add(final File inputFile) throws IOException { 056 return this.add(inputFile, null); 057 } 058 059 public JARFileMaker addChildren(final File folder) throws IOException { 060 LOG.log(Level.FINEST, "Add children: {0}", folder); 061 for (final File nestedFile : CollectionUtils.nullToEmpty(folder.listFiles())) { 062 this.add(nestedFile); 063 } 064 return this; 065 } 066 067 private JARFileMaker add(final File inputFile, final String prefix) throws IOException { 068 069 final String fileNameInJAR = createPathInJar(inputFile, prefix); 070 LOG.log(Level.FINEST, "Add {0} as {1}", new Object[] {inputFile, fileNameInJAR}); 071 072 final JarEntry entry = new JarEntry(fileNameInJAR); 073 entry.setTime(inputFile.lastModified()); 074 this.jarOutputStream.putNextEntry(entry); 075 076 if (inputFile.isDirectory()) { 077 this.jarOutputStream.closeEntry(); 078 for (final File nestedFile : CollectionUtils.nullToEmpty(inputFile.listFiles())) { 079 this.add(nestedFile, fileNameInJAR); 080 } 081 } else { 082 try (final BufferedInputStream in = new BufferedInputStream(new FileInputStream(inputFile))) { 083 IOUtils.copy(in, this.jarOutputStream); 084 } catch (final FileNotFoundException ex) { 085 LOG.log(Level.WARNING, "Skip the file: " + inputFile, ex); 086 } finally { 087 this.jarOutputStream.closeEntry(); 088 } 089 } 090 091 return this; 092 } 093 094 private static String createPathInJar(final File inputFile, final String prefix) { 095 final StringBuilder buf = new StringBuilder(); 096 if (prefix != null) { 097 buf.append(prefix); 098 } 099 buf.append(inputFile.getName()); 100 if (inputFile.isDirectory()) { 101 buf.append('/'); 102 } 103 return buf.toString(); 104 } 105 106 @Override 107 public void close() throws IOException { 108 this.jarOutputStream.close(); 109 } 110}