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.JarEntry;
35  import java.util.jar.JarFile;
36  
37  import java.util.Enumeration;
38  import java.util.Properties;
39  
40  import java.io.ByteArrayInputStream;
41  import java.io.ByteArrayOutputStream;
42  import java.io.IOException;
43  import java.io.File;
44  import java.io.FileOutputStream;
45  import java.io.FileInputStream;
46  import java.io.OutputStream;
47  import java.io.InputStream;
48  
49  import java.util.zip.*;
50  
51  /***
52   * Extracts itself from the jar it is in, assuming it is run with java -jar.
53   */
54  public class PayloadExtractor {
55  
56      public static final boolean DEBUG = (System.getProperty("PAYLOAD.DEBUG")!=null);
57  
58      public static void main(String[] args) {
59  if(DEBUG) System.out.println("DEBUG turned on. ");
60          System.out.print("Payload extraction setup");
61  
62          // when run with -jar, the class path is the jar file
63          String jarFile = System.getProperty("java.class.path");
64          if(jarFile.indexOf(":") != -1) {
65              jarFile = null;
66              // get the jarFile as a -j argument
67          }
68          String jarName = jarFile.substring( 0, jarFile.length() - ".jar".length() );
69          System.out.print(".");
70  
71          Properties props = null;
72          if(args.length == 0) {
73              System.err.println("\nNo properties file specified, will output without interpolation. ");
74          } else {
75              for(int i=0; i<args.length; i++) {
76                  FileInputStream fin = null;
77                  try {
78                      fin = new FileInputStream(new File(args[i]));
79                      if(props == null) {
80                          props = new Properties();
81                      }
82                      props.load(fin);
83  if(DEBUG) System.out.println("\n"+args[i]+" being used as interpolation values. ");
84  if(DEBUG) System.out.println(props.toString());
85                  } catch(IOException ioe) {
86                      System.err.println("\nUnable to find properties file, will skip it. ");
87                  } finally {
88                      if(fin != null) {
89                          try {
90                              fin.close();
91                          } catch(IOException ioe) {
92                              ; // ignore
93                          }
94                      }
95                  }
96              }
97          }
98          System.out.println(".");
99  
100         // loop....
101         try {
102             JarFile jar = new JarFile(new File(jarFile));
103 
104             PayloadConfiguration configuration = null;
105 
106             // need to find a way to ensure the interpolation is read 
107             // first. possibly scan through the zip first?
108             Enumeration enumeration = jar.entries();
109             while(enumeration.hasMoreElements()) {
110                 JarEntry entry = (JarEntry) enumeration.nextElement();
111                 if(entry.getName().equals("payload.properties")) {
112 if(DEBUG) System.out.println("Custom interpolation being used. ");
113                     InputStream in = jar.getInputStream( entry );
114                     String txt = IOUtils.readToString(in);
115                     configuration = new PayloadConfiguration(txt);
116                     break;
117                 }
118             }
119 
120 
121             if(configuration == null) {
122 if(DEBUG) System.out.println("Default configuration being used. ");
123                 configuration = PayloadConfiguration.DEFAULT;
124             }
125 
126             Interpolation interpolation = new Interpolation(configuration);
127             PayletExecutor payletExecutor = new PayletExecutor(configuration);
128 
129             System.out.print("Payload extracting");
130 
131             enumeration = jar.entries();
132             while(enumeration.hasMoreElements()) {
133                 JarEntry entry = (JarEntry) enumeration.nextElement();
134                 if(!entry.getName().startsWith("payload")) {
135                     continue;
136                 }
137                 if(entry.getName().equals("payload.properties")) {
138                     continue;
139                 }
140                 // remove payload/
141                 String inName = entry.getName().substring("payload/".length());
142                 String outName = jarName + File.separator + inName;
143                 File outFile = new File(outName);
144                 if(entry.isDirectory()) {
145                     outFile.mkdirs();
146                     continue;
147                 } else {
148                     outFile.getParentFile().mkdirs();
149                 }
150 
151                 InputStream in = jar.getInputStream( entry );
152 
153                 // TODO: configurable interpolation targets
154                 // trusting that we're not interpolating anything 
155                 // that can't fit in memory
156                 boolean interpolated = false;
157                 if( props != null && interpolation.interpolatable(outName)) {
158                     // interpolate push
159 if(DEBUG) System.out.println("Interpolating "+outName);
160                     String text = IOUtils.readToString(in);
161                     text = interpolation.interpolate(text, props);
162                     in.close();
163                     in = new ByteArrayInputStream(text.getBytes());
164                     interpolated = true;
165                 }
166 
167                 boolean interpolateArchive = false;
168 
169                 // if an archive, then interpolate in the archive
170                 // TODO: Make this configurable
171                 if(props != null && interpolation.interpolatableArchive(outName)) {
172                     // first pass. See if any interpolatables
173                     // if so, then flag this for a zip-handler
174                     byte[] bytes = IOUtils.readToBytes(in);
175                     ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(bytes));
176                     ZipEntry zEntry = null;
177                     while( (zEntry = zin.getNextEntry()) != null) {
178                         if(interpolation.interpolatable(zEntry.getName())) {
179                             interpolateArchive = true;
180                             break;
181                         }
182                     }
183                     zin.close();
184                     in = new ByteArrayInputStream(bytes);
185 
186                 }
187 
188                 if(interpolateArchive) {
189                     FileOutputStream out = new FileOutputStream( outFile );
190                     interpolateArchive(out, in, interpolation, props);
191                     out.close();
192                     System.out.print("#");
193                 } else {
194                     OutputStream out = new FileOutputStream( outFile );
195                     IOUtils.pushBytes(in, out);
196                     out.close();
197                     in.close();
198                     if(interpolated) {
199                         System.out.print("$");
200                     } else {
201                         System.out.print(".");
202                     }
203                 }
204 
205             }
206 
207             payletExecutor.execute(props);
208         } catch(IOException ioe) { ioe.printStackTrace(); }
209 
210 
211         System.out.println("");
212         System.out.println("Payload has arrived. ");
213     }
214 
215 
216     private static void interpolateArchive(OutputStream out, InputStream in, Interpolation interpolation, Properties props) throws IOException {
217         ZipOutputStream zout = new ZipOutputStream(out);
218         ZipInputStream zin = new ZipInputStream(in);
219         ZipEntry zEntry = null;
220         InputStream tmpin = null;
221         while( (zEntry = zin.getNextEntry()) != null) {
222             tmpin = zin;
223             long size = zEntry.getSize();
224             long crc = zEntry.getCrc();
225             if(props != null && interpolation.interpolatable(zEntry.getName())) {
226 if(DEBUG) System.out.println("Interpolating in archive");
227                 String text = IOUtils.readToString(zin);
228                 text = interpolation.interpolate(text, props);
229                 tmpin = new ByteArrayInputStream(text.getBytes());
230                 size = text.getBytes().length;
231                 CRC32 crc32 = new CRC32();
232                 crc32.update(text.getBytes());
233                 crc = crc32.getValue();
234             }
235             // if interpolatable archive, then recurse.....
236             if(props != null && interpolation.interpolatableArchive(zEntry.getName())) {
237                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
238                 IOUtils.pushBytes(zin, baos);
239                 tmpin = new ByteArrayInputStream( baos.toByteArray() );
240                 baos = new ByteArrayOutputStream();
241 if(DEBUG) System.out.println("Recursing into sub-archive");
242                 interpolateArchive(baos, tmpin, interpolation, props);
243                 tmpin = new ByteArrayInputStream( baos.toByteArray() );
244                 size = baos.toByteArray().length;
245                 CRC32 crc32 = new CRC32();
246                 crc32.update(baos.toByteArray());
247                 crc = crc32.getValue();
248             }
249 
250             ZipEntry newEntry = new ZipEntry(zEntry.getName());
251             if(zEntry.getComment() != null) {
252                 newEntry.setComment(zEntry.getComment());
253             }
254             if(zEntry.getExtra() != null) {
255                 newEntry.setExtra(zEntry.getExtra());
256             }
257             if(zEntry.getTime() != -1) {
258                 newEntry.setTime(zEntry.getTime());
259             }
260             if(zEntry.getMethod() != -1) {
261                 newEntry.setMethod(zEntry.getMethod());
262                 zout.setMethod(zEntry.getMethod());
263             }
264             if(crc != -1) {
265                 newEntry.setCrc(crc);
266             }
267             if(zEntry.getSize() != -1) {
268                 newEntry.setSize(size);
269             }
270             /* Oddly fails, unsure why. 
271             if(zEntry.getCompressedSize() != -1 && size == zEntry.getSize()) {
272                 newEntry.setCompressedSize(zEntry.getCompressedSize());
273             }
274             */
275             zout.putNextEntry(newEntry);
276 
277             IOUtils.pushBytes(tmpin, zout);
278             zin.closeEntry();
279             zout.closeEntry();
280         }
281         zout.finish();
282         zin.close();
283     }
284 
285 }