View Javadoc

1   /*
2    * Copyright (c) 2003-2004, Henri Yandell
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or 
6    * without modification, are permitted provided that the 
7    * following conditions are met:
8    * 
9    * + Redistributions of source code must retain the above copyright notice, 
10   *   this list of conditions and the following disclaimer.
11   * 
12   * + Redistributions in binary form must reproduce the above copyright notice, 
13   *   this list of conditions and the following disclaimer in the documentation 
14   *   and/or other materials provided with the distribution.
15   * 
16   * + Neither the name of OSJava nor the names of its contributors 
17   *   may be used to endorse or promote products derived from this software 
18   *   without specific prior written permission.
19   * 
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
23   * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
30   * POSSIBILITY OF SUCH DAMAGE.
31   */
32  package org.osjava.payload;
33  
34  import java.util.jar.JarOutputStream;
35  import java.util.jar.JarEntry;
36  import java.util.jar.JarFile;
37  
38  import java.util.Enumeration;
39  import java.util.Properties;
40  
41  import java.io.IOException;
42  import java.io.File;
43  import java.io.FileOutputStream;
44  import java.io.FileInputStream;
45  import java.io.InputStream;
46  
47  /***
48   * Builds a self-extracting jar. 
49   */
50  public class PayloadContractor {
51  
52      public static final boolean DEBUG = (System.getProperty("PAYLOAD.DEBUG")!=null);
53  
54  // Allow payload/ to be specified as a system property
55  
56  // java -jar payload.jar target.jar payload.properties target-files
57  // TODO: Add the paylet.jar's. -x paylet.jar?
58      public static void main(String[] args) {
59          System.out.print("Payload contraction setup");
60  
61          // when run with -jar, the class path is the jar file
62          String jarFile = System.getProperty("java.class.path");
63          if(jarFile.indexOf(":") != -1) {
64              jarFile = null;
65              // get the jarFile as a -j argument
66          }
67          System.out.println(".");
68  if(DEBUG) System.out.println("DEBUG turned on. ");
69  
70          if(args.length == 0) {
71              System.err.println("\nUnable to contract a payload as no target jar has been specified. ");
72              System.exit(1);
73          }
74          if(!args[0].endsWith(".jar")) {
75              System.err.println("\nRefusing to target a non-jar file. " +
76                                 "Please use a .jar filename then rename afterwards. ");
77              System.exit(1);
78          }
79          String targetJar = args[0];
80          if(args.length == 1) {
81              System.err.println("\nUnable to contract a payload as no target files have been specified. ");
82              System.exit(1);
83          }
84  
85          // start writing to targetJar file
86          FileInputStream fin = null;
87          JarOutputStream jout = null;
88          try {
89              System.out.print("Contracting payload header");
90              fin = new FileInputStream(new File(jarFile));
91              jout = new JarOutputStream(new FileOutputStream(new File(targetJar)));
92  
93              JarFile jar = new JarFile(new File(jarFile));
94              Enumeration enumeration = jar.entries();
95              while(enumeration.hasMoreElements()) {
96                  JarEntry entry = (JarEntry) enumeration.nextElement();
97                  InputStream in = jar.getInputStream( entry );
98                  JarEntry entry2 = new JarEntry(entry.getName());
99                  entry2.setTime(entry.getTime());
100                 entry2.setSize(entry.getSize());
101                 jout.putNextEntry(entry2);
102                 if(entry.getName().equals("META-INF/MANIFEST.MF")) {
103                     String mf = IOUtils.readToString(in);
104                     mf = mf.replaceAll("Main-Class: org//.osjava//.payload//.PayloadContractor", 
105                                        "Main-Class: org.osjava.payload.PayloadExtractor");
106                     jout.write(mf.getBytes());
107                 } else {
108                     IOUtils.pushBytes(in, jout);
109                 }
110                 jout.closeEntry();
111                 System.out.print(".");
112             }
113             System.out.println("");
114 
115             // TODO: Look for args[1] being a .properties file 
116             //       with the org.osjava.payload=true property set.
117             // If so, store at the top level.
118             int start = 1;
119             if(args[1].endsWith(".properties")) {
120                 FileInputStream propsFin = null;
121                 try {
122                     File propsFile = new File(args[1]);
123                     propsFin = new FileInputStream(propsFile);
124                     Properties tmp = new Properties();
125                     tmp.load(propsFin);
126                     if("true".equals(tmp.getProperty("org.osjava.payload"))) {
127                         System.out.print("Inlining "+args[1]+" as payload.properties");
128                         storeFile(jout, propsFile, "payload.properties");
129                         System.out.println("");
130                         start = 2;
131                     }
132                 } catch(IOException ioe) {
133                     ioe.printStackTrace();   //  reasons?
134                 } finally {
135                     IOUtils.closeQuietly(propsFin);
136                 }
137             }
138 
139             // loop over every argument, handling recursion, 
140             // and pushing into the payload/ directory
141             System.out.print("Contracting payload body");
142             boolean expectingPayletJar = false;
143             for(int i=start; i<args.length; i++) {
144                 if("-x".equals(args[i])) {
145                     expectingPayletJar = true;
146                     continue;
147                 }
148                 String filename = args[i];
149                 File file = new File(filename);
150                 if(expectingPayletJar) {
151                     JarFile payletJar = new JarFile(file);
152                     Enumeration payletEnum = payletJar.entries();
153                     while(payletEnum.hasMoreElements()) {
154                         JarEntry entry = (JarEntry) payletEnum.nextElement();
155                         // Skip any file already in the destination jar. 
156                         // No cascading.
157                         if(jar.getEntry(entry.getName()) != null) {
158                             continue;
159                         }
160                         InputStream in = payletJar.getInputStream( entry );
161                         JarEntry entry2 = new JarEntry(entry.getName());
162                         entry2.setTime(entry.getTime());
163                         entry2.setSize(entry.getSize());
164                         jout.putNextEntry(entry2);
165                         IOUtils.pushBytes(in, jout);
166                         jout.closeEntry();
167                         System.out.print(":");
168                     }
169                     expectingPayletJar = false;
170                 } else {
171                     storeFile(jout, file);
172                 }
173             }
174 
175             System.out.println("\nFinished. ");
176 
177         } catch(IOException ioe) {
178             System.err.println("\nIOException in building new jar. ");
179             ioe.printStackTrace();
180         } finally {
181             IOUtils.closeQuietly(fin);
182             IOUtils.closeQuietly(jout);
183         }
184     }
185 
186     // overloaded so that payload.properties may be written to the top level
187     private static void storeFile(JarOutputStream jout, File file) throws IOException {
188         String fileUnix = file.toString().replaceAll("////", "/");
189         storeFile(jout, file, "payload/"+fileUnix);
190     }
191     private static void storeFile(JarOutputStream jout, File file, String targetName) throws IOException {
192         if(file.isDirectory()) {
193             // store directory
194             JarEntry entry = new JarEntry(targetName+"/");
195             // TODO: read time of file
196             entry.setTime(System.currentTimeMillis());
197             jout.putNextEntry(entry);
198 
199             // recurse
200             File[] children = file.listFiles();
201             int sz = children.length;
202             for(int i=0; i<sz; i++) {
203                 storeFile(jout, children[i]);
204             }
205         } else {
206             FileInputStream fis = new FileInputStream(file);
207             JarEntry entry = new JarEntry(targetName);
208             // TODO: read time of file
209             entry.setTime(System.currentTimeMillis());
210             entry.setSize( file.length() );
211             jout.putNextEntry(entry);
212             IOUtils.pushBytes(fis, jout);
213             jout.closeEntry();
214             System.out.print(".");
215         }
216     }
217 }