KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > object > tools > BootJar


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
3  * notice. All rights reserved.
4  */

5 package com.tc.object.tools;
6
7 import com.tc.logging.TCLogger;
8 import com.tc.logging.TCLogging;
9 import com.tc.object.NotInBootJar;
10 import com.tc.util.Assert;
11 import com.tc.util.ProductInfo;
12
13 import java.io.File JavaDoc;
14 import java.io.FileNotFoundException JavaDoc;
15 import java.io.FileOutputStream JavaDoc;
16 import java.io.IOException JavaDoc;
17 import java.util.Enumeration JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.Set JavaDoc;
23 import java.util.StringTokenizer JavaDoc;
24 import java.util.jar.Attributes JavaDoc;
25 import java.util.jar.JarEntry JavaDoc;
26 import java.util.jar.JarFile JavaDoc;
27 import java.util.jar.JarOutputStream JavaDoc;
28 import java.util.jar.Manifest JavaDoc;
29
30 public class BootJar {
31   private static final TCLogger logger = TCLogging.getLogger(BootJar.class);
32
33   private static final String JavaDoc DSO_BOOT_JAR_PATTERN = ".+dso-boot.*\\.jar$";
34   static final String JavaDoc JAR_NAME_PREFIX = "dso-boot-";
35
36   private static final String JavaDoc VERSION_1_1 = "1.1";
37
38   private static final State STATE_OPEN = new State("OPEN");
39   private static final State STATE_CLOSED = new State("CLOSED");
40
41   public static final String JavaDoc PREINSTRUMENTED_NAME = "Preinstrumented";
42
43   private final File JavaDoc file;
44   private final Map JavaDoc entries;
45   private final boolean openForWrite;
46   private final Manifest JavaDoc manifest;
47   private final BootJarMetaData metaData;
48   private final JarFile JavaDoc jarFileInput;
49   private final Set JavaDoc classes = new HashSet JavaDoc();
50   private State state;
51   private boolean creationErrorOccurred;
52
53   private BootJar(File JavaDoc file, boolean openForWrite, BootJarMetaData metaData, JarFile JavaDoc jarFile) {
54     Assert.assertNotNull(file);
55
56     this.file = file;
57     this.openForWrite = openForWrite;
58     this.metaData = metaData;
59     this.jarFileInput = jarFile;
60     this.manifest = new Manifest JavaDoc();
61     this.entries = new HashMap JavaDoc();
62
63     this.state = STATE_OPEN;
64     this.creationErrorOccurred = false;
65   }
66
67   public static BootJar getBootJarForWriting(File JavaDoc bootJar) throws UnsupportedVMException {
68     String JavaDoc vmSignature = BootJarSignature.getSignatureForThisVM().getSignature();
69     return getBootJarForWriting(bootJar, vmSignature);
70   }
71
72   public static BootJar getBootJarForReading(File JavaDoc bootJar) throws IOException JavaDoc, BootJarException {
73     return getBootJarForReading(bootJar, BootJarSignature.getSignatureForThisVM());
74   }
75
76   static BootJar getBootJarForWriting(File JavaDoc bootJar, String JavaDoc vmSignature) {
77     return getBootJarForWriting(bootJar, vmSignature, VERSION_1_1);
78   }
79
80   static BootJar getBootJarForWriting(File JavaDoc bootJar, String JavaDoc vmSignature, String JavaDoc metaDataVersion) {
81     return new BootJar(bootJar, true, new BootJarMetaData(vmSignature, metaDataVersion), null);
82   }
83
84   static BootJar getBootJarForReading(File JavaDoc bootJar, BootJarSignature expectedSignature) throws IOException JavaDoc,
85       BootJarException {
86
87     if (!existingFileIsAccessible(bootJar)) throw new FileNotFoundException JavaDoc("Cannot access file: "
88                                                                             + bootJar.getAbsolutePath());
89
90     JarFile JavaDoc jarFile = new JarFile JavaDoc(bootJar, false);
91     Manifest JavaDoc manifest = jarFile.getManifest();
92     BootJarMetaData metaData = new BootJarMetaData(manifest);
93
94     // verify VM signature
95
BootJarSignature signatureFromJar = new BootJarSignature(metaData.getVMSignature());
96
97     if (!expectedSignature.isCompatibleWith(signatureFromJar)) {
98       // make formatter sane
99
throw new InvalidJVMVersionException(
100                                            "Incompatible boot jar JVM version; expected '"
101                                                + expectedSignature
102                                                + "' but was (in boot jar) '"
103                                                + signatureFromJar
104                                                + "'; Please regenerate the DSO boot jar to match this VM, or switch to a VM compatible with this boot jar");
105     }
106
107     return new BootJar(bootJar, false, metaData, jarFile);
108   }
109
110   public static File JavaDoc findBootJar() throws FileNotFoundException JavaDoc {
111     return findBootJarByPattern(DSO_BOOT_JAR_PATTERN);
112   }
113
114   private static File JavaDoc findBootJarByPattern(String JavaDoc pattern) throws FileNotFoundException JavaDoc {
115     String JavaDoc bootClassPath = System.getProperty("sun.boot.class.path");
116     StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(bootClassPath, System.getProperty("path.separator"));
117     while (st.hasMoreTokens()) {
118       String JavaDoc element = st.nextToken();
119       if (element.matches(pattern)) { return new File JavaDoc(element); }
120     }
121     throw new FileNotFoundException JavaDoc("Can't find boot jar matching pattern (" + pattern + ") in boot classpath: "
122                                     + bootClassPath);
123   }
124
125   public static BootJar getDefaultBootJarForReading() throws BootJarException, IOException JavaDoc {
126     return getBootJarForReading(findBootJar());
127   }
128
129   private static boolean existingFileIsAccessible(File JavaDoc file) {
130     return file.exists() || file.isFile() || file.canRead();
131   }
132
133   public static String JavaDoc classNameToFileName(String JavaDoc className) {
134     return className.replace('.', '/') + ".class";
135   }
136
137   public static String JavaDoc fileNameToClassName(String JavaDoc filename) {
138     if (!filename.endsWith(".class")) throw new AssertionError JavaDoc("Invalid class file name: " + filename);
139     return filename.substring(0, filename.lastIndexOf('.')).replace('/', '.');
140   }
141
142   public boolean classLoaded(String JavaDoc className) {
143     return classes.contains(className);
144   }
145
146   public void loadClassIntoJar(String JavaDoc className, byte[] data, boolean isPreinstrumented) {
147     boolean added = classes.add(className);
148
149     // disallow duplicate entries into the boot jar. Even w/o this assertion, the jar
150
// stuff will blow up later if an entry is duplicated
151
Assert.assertTrue("Duplicate class added " + className, added);
152
153     if (className.equals(NotInBootJar.class.getName())) {
154       // make formatter sane
155
throw new AssertionError JavaDoc("Invalid class for boot jar: " + className);
156     }
157
158     String JavaDoc cn = classNameToFileName(className);
159     JarEntry JavaDoc jarEntry = new JarEntry JavaDoc(cn);
160     basicLoadClassIntoJar(jarEntry, data, isPreinstrumented);
161   }
162
163   private void assertWrite() {
164     if (!openForWrite) throw new AssertionError JavaDoc("boot jar not open for writing");
165   }
166
167   private synchronized void basicLoadClassIntoJar(JarEntry JavaDoc je, byte[] classBytes, boolean isPreinstrumented) {
168     assertWrite();
169
170     Attributes JavaDoc attributes = manifest.getAttributes(je.getName());
171     if (attributes == null) {
172       attributes = makeAttributesFor(je.getName());
173     }
174     attributes.put(new Attributes.Name JavaDoc(PREINSTRUMENTED_NAME), Boolean.toString(isPreinstrumented));
175     entries.put(new JarEntryWrapper(je, new Boolean JavaDoc(isPreinstrumented)), classBytes);
176   }
177
178   private Attributes JavaDoc makeAttributesFor(String JavaDoc resource) {
179     Attributes JavaDoc rv = new Attributes JavaDoc();
180     manifest.getEntries().put(resource, rv);
181     return rv;
182   }
183
184   public synchronized Set JavaDoc getAllPreInstrumentedClasses() throws IOException JavaDoc {
185     assertOpen();
186     Set JavaDoc rv = new HashSet JavaDoc();
187     for (Enumeration JavaDoc e = jarFileInput.entries(); e.hasMoreElements();) {
188       JarEntry JavaDoc entry = (JarEntry JavaDoc) e.nextElement();
189       String JavaDoc entryName = entry.getName();
190
191       // This condition used to only exclude "META-INF/MANIFEST.MF". Jar signing puts additional META-INF files into the
192
// boot jar, which caused an assertion error. So, now only try reading specs from actual class files present in
193
// the jar
194
if (entryName.toLowerCase().endsWith(".class")) {
195         if (isPreInstrumentedEntry(entry)) {
196           rv.add(fileNameToClassName(entry.getName()));
197         }
198       }
199     }
200     return rv;
201   }
202
203   private void assertOpen() {
204     if (state != STATE_OPEN) { throw new AssertionError JavaDoc("boot jar not open: " + state); }
205   }
206
207   private boolean isPreInstrumentedEntry(JarEntry JavaDoc entry) throws IOException JavaDoc {
208     Attributes JavaDoc attributes = entry.getAttributes();
209     if (attributes == null) throw new AssertionError JavaDoc("Invalid jar file: No attributes for jar entry: "
210                                                      + entry.getName());
211     String JavaDoc value = attributes.getValue(PREINSTRUMENTED_NAME);
212     if (value == null) throw new AssertionError JavaDoc("Invalid jar file: No " + PREINSTRUMENTED_NAME
213                                                 + " attribute for jar entry: " + entry.getName());
214     return Boolean.valueOf(value).booleanValue();
215   }
216
217   private void writeEntries(JarOutputStream JavaDoc out) throws IOException JavaDoc {
218     for (Iterator JavaDoc i = entries.keySet().iterator(); i.hasNext();) {
219       JarEntryWrapper je = (JarEntryWrapper) i.next();
220       byte[] classBytes = (byte[]) entries.get(je);
221       out.putNextEntry(je.getJarEntry());
222       out.write(classBytes);
223     }
224   }
225
226   public void setCreationErrorOccurred(boolean errorOccurred) {
227     this.creationErrorOccurred = errorOccurred;
228   }
229
230   public synchronized void close() throws IOException JavaDoc {
231     if (state == STATE_OPEN) {
232       if (openForWrite && !this.creationErrorOccurred) {
233         metaData.write(manifest);
234         JarOutputStream JavaDoc out = new JarOutputStream JavaDoc(new FileOutputStream JavaDoc(file, false), manifest);
235         writeEntries(out);
236         out.flush();
237         out.close();
238       }
239
240       if (jarFileInput != null) {
241         jarFileInput.close();
242       }
243     }
244
245     state = STATE_CLOSED;
246   }
247
248   /**
249    * Check the embedded TC_VERSION information against current product version.
250    *
251    * @return <code>true</code> TC_VERSION matches current product version.
252    */

253   public boolean checkSourceVersion() {
254     return false;
255   }
256
257   static class BootJarMetaData {
258     private static final String JavaDoc META_DATA_ATTRIBUTE_NAME = "DSO_BOOTJAR_METADATA";
259     private static final String JavaDoc TC_VERSION = "TC_VERSION";
260     private static final String JavaDoc TC_MONIKER = "TC_MONIKER";
261     private static final String JavaDoc VERSION = "VERSION";
262     private static final String JavaDoc VM_SIGNATURE = "VM_SIGNATURE";
263
264     private final String JavaDoc vmSignature;
265     private final String JavaDoc version;
266     private final String JavaDoc tcversion;
267     private final String JavaDoc tcmoniker;
268
269     BootJarMetaData(String JavaDoc vmSignature, String JavaDoc version) {
270       Assert.assertNotNull(vmSignature);
271       Assert.assertNotNull(version);
272       this.vmSignature = vmSignature;
273       this.version = version;
274       this.tcversion = null;
275       this.tcmoniker = null;
276     }
277
278     BootJarMetaData(Manifest JavaDoc manifest) throws BootJarException {
279       Assert.assertNotNull(manifest);
280       Attributes JavaDoc attributes = (Attributes JavaDoc) manifest.getEntries().get(META_DATA_ATTRIBUTE_NAME);
281       if (attributes == null) throw new InvalidBootJarMetaDataException(
282                                                                         "Missing attributes in jar manifest. Please regenerate boot jar");
283
284       version = attributes.getValue(VERSION);
285       if (version == null) throw new InvalidBootJarMetaDataException("Missing metadata: version");
286
287       String JavaDoc expect_version = VERSION_1_1;
288       if (expect_version.equals(version)) {
289         vmSignature = attributes.getValue(VM_SIGNATURE);
290         if (vmSignature == null) throw new InvalidJVMVersionException("Missing vm signature");
291       } else {
292         throw new InvalidBootJarMetaDataException("Incompatible DSO meta data: version; expected '" + expect_version
293                                                   + "' but was (in boot jar): '" + version
294                                                   + "'; please regenerate the DSO boot jar");
295       }
296
297       tcversion = attributes.getValue(TC_VERSION);
298       if (tcversion == null) throw new InvalidBootJarMetaDataException("Missing metadata: tcversion");
299
300       tcmoniker = attributes.getValue(TC_MONIKER);
301       if (tcmoniker == null) throw new InvalidBootJarMetaDataException("Missing metadata: tcmoniker");
302
303       ProductInfo productInfo = ProductInfo.getThisProductInfo();
304       String JavaDoc expect_tcversion = productInfo.buildVersion();
305
306       if (productInfo.isDevMode()) logger
307           .warn("The value for the DSO meta data, tcversion is: '"
308                 + expect_tcversion
309                 + "'; this might not be correct, this value is used only under development mode or when tests are being run.");
310
311       if (!productInfo.isDevMode() && !expect_tcversion.equals(tcversion)) throw new InvalidBootJarMetaDataException(
312                                                                                                                                      "Incompatible DSO meta data: tcversion; expected '"
313                                                                                                                                          + expect_tcversion
314                                                                                                                                          + "' but was (in boot jar): '"
315                                                                                                                                          + tcversion
316                                                                                                                                          + "'; please regenerate the DSO boot jar");
317     }
318
319     public void write(Manifest JavaDoc manifest) {
320       if (VERSION_1_1.equals(version)) {
321         ProductInfo productInfo = ProductInfo.getThisProductInfo();
322         Attributes JavaDoc attributes = new Attributes JavaDoc();
323         attributes.put(new Attributes.Name JavaDoc(TC_MONIKER), productInfo.moniker());
324         attributes.put(new Attributes.Name JavaDoc(TC_VERSION), productInfo.buildVersion());
325         attributes.put(new Attributes.Name JavaDoc(VERSION), getVersion());
326         attributes.put(new Attributes.Name JavaDoc(VM_SIGNATURE), getVMSignature());
327         Object JavaDoc prev = manifest.getEntries().put(META_DATA_ATTRIBUTE_NAME, attributes);
328         Assert.assertNull(prev);
329       } else {
330         throw new AssertionError JavaDoc("Unexptected metadata for version, expecting '" + VERSION_1_1 + "', but was '" + version + "'");
331       }
332     }
333
334     public String JavaDoc getTCVersion() {
335       return this.tcversion;
336     }
337
338     public String JavaDoc getVMSignature() {
339       return this.vmSignature;
340     }
341
342     public String JavaDoc getVersion() {
343       return this.version;
344     }
345   }
346
347   private static class JarEntryWrapper {
348     private final JarEntry JavaDoc jarEntry;
349     private final Boolean JavaDoc isPreinstrumented;
350
351     private JarEntryWrapper(JarEntry JavaDoc jarEntry, Boolean JavaDoc isPreinstrumented) {
352       this.jarEntry = jarEntry;
353       this.isPreinstrumented = isPreinstrumented;
354     }
355
356     public JarEntry JavaDoc getJarEntry() {
357       return jarEntry;
358     }
359
360     public Boolean JavaDoc isPreistrumented() {
361       return isPreinstrumented;
362     }
363   }
364
365   private static class State {
366     private final String JavaDoc name;
367
368     private State(String JavaDoc name) {
369       this.name = name;
370     }
371
372     public String JavaDoc toString() {
373       return this.name;
374     }
375   }
376 }
377
Popular Tags