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 FileOutputStream fileOutputStream; 038 private final JarOutputStream jarOutputStream; 039 private String relativeStartCanonicalPath = null; 040 041 public JARFileMaker(final File outputFile, final Manifest manifest) throws IOException { 042 this.fileOutputStream = new FileOutputStream(outputFile); 043 this.jarOutputStream = new JarOutputStream(this.fileOutputStream, manifest); 044 } 045 046 public JARFileMaker(final File outputFile) throws IOException { 047 this.fileOutputStream = new FileOutputStream(outputFile); 048 this.jarOutputStream = new JarOutputStream(this.fileOutputStream); 049 } 050 051 /** 052 * Adds a file to the JAR. If inputFile is a folder, it will be added recursively. 053 * 054 * @param inputFile 055 * @throws IOException 056 */ 057 public JARFileMaker add(final File inputFile) throws IOException { 058 059 final String fileNameInJAR = makeRelative(inputFile); 060 if (inputFile.isDirectory()) { 061 final JarEntry entry = new JarEntry(fileNameInJAR); 062 entry.setTime(inputFile.lastModified()); 063 this.jarOutputStream.putNextEntry(entry); 064 this.jarOutputStream.closeEntry(); 065 for (final File nestedFile : inputFile.listFiles()) { 066 add(nestedFile); 067 } 068 return this; 069 } 070 071 final JarEntry entry = new JarEntry(fileNameInJAR); 072 entry.setTime(inputFile.lastModified()); 073 this.jarOutputStream.putNextEntry(entry); 074 try (final BufferedInputStream in = new BufferedInputStream(new FileInputStream(inputFile))) { 075 IOUtils.copy(in, this.jarOutputStream); 076 this.jarOutputStream.closeEntry(); 077 } catch (final FileNotFoundException ex) { 078 LOG.log(Level.WARNING, "Skip the file: " + inputFile, ex); 079 } 080 return this; 081 } 082 083 public JARFileMaker addChildren(final File folder) throws IOException { 084 this.relativeStartCanonicalPath = folder.getCanonicalPath(); 085 for (final File f : folder.listFiles()) { 086 this.add(f); 087 } 088 this.relativeStartCanonicalPath = null; 089 return this; 090 } 091 092 private String makeRelative(final File input) throws IOException { 093 final String result; 094 if (this.relativeStartCanonicalPath == null) { 095 result = input.getCanonicalPath(); 096 } else { 097 result = input.getCanonicalPath() 098 .replace(this.relativeStartCanonicalPath, "") // Drop the absolute prefix 099 .substring(1); // drop the '/' at the beginning 100 } 101 if (input.isDirectory()) { 102 return result.replace("\\", "/") + "/"; 103 } else { 104 return result.replace("\\", "/"); 105 } 106 107 } 108 109 @Override 110 public void close() throws IOException { 111 this.jarOutputStream.close(); 112 this.fileOutputStream.close(); 113 } 114}