001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037:
038: package oracle.toplink.essentials.weaving;
039:
040: import java.io.File;
041: import java.io.FileOutputStream;
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.io.Writer;
045: import java.lang.instrument.IllegalClassFormatException;
046: import java.net.MalformedURLException;
047: import java.net.URISyntaxException;
048: import java.net.URL;
049: import java.net.URLClassLoader;
050: import java.util.Iterator;
051: import java.util.jar.JarEntry;
052: import java.util.jar.JarOutputStream;
053:
054: import oracle.toplink.essentials.ejb.cmp3.persistence.Archive;
055: import oracle.toplink.essentials.ejb.cmp3.persistence.ArchiveFactoryImpl;
056: import oracle.toplink.essentials.ejb.cmp3.persistence.PersistenceUnitProcessor;
057: import oracle.toplink.essentials.exceptions.StaticWeaveException;
058: import oracle.toplink.essentials.internal.localization.ToStringLocalization;
059: import oracle.toplink.essentials.internal.weaving.AbstractStaticWeaveOutputHandler;
060: import oracle.toplink.essentials.internal.weaving.StaticWeaveClassTransformer;
061: import oracle.toplink.essentials.internal.weaving.StaticWeaveDirectoryOutputHandler;
062: import oracle.toplink.essentials.internal.weaving.StaticWeaveJAROutputHandler;
063: import oracle.toplink.essentials.logging.AbstractSessionLog;
064: import oracle.toplink.essentials.logging.DefaultSessionLog;
065: import oracle.toplink.essentials.logging.SessionLog;
066:
067: /**
068: * <p>
069: * <b>Description</b>: The StaticWeaveProcessor controls the static weaving process. It is invoked by both the command line
070: * StaticWeave class and the StaticWeaveAntTask.
071: * <p>
072: * <b>Responsibilities</b>:Process the source classes, performs weaving as necessary out outputs to the target
073: **/
074: public class StaticWeaveProcessor {
075: private URL source;
076: private URL target;
077: private URL persistenceInfo;
078: private Writer logWriter;
079: private ClassLoader classLoader;
080: private int logLevel = SessionLog.OFF;
081:
082: /**
083: * Constructs an instance of StaticWeaveProcessor
084: * @param source the name of the location to be weaved
085: * @param target the name of the location to be weaved to
086: * @throws MalformedURLException
087: */
088: public StaticWeaveProcessor(String source, String target)
089: throws MalformedURLException {
090: if (source != null) {
091: this .source = new File(source).toURL();
092: }
093: if (target != null) {
094: this .target = new File(target).toURL();
095: }
096: }
097:
098: /**
099: * Constructs an instance of StaticWeaveProcessor
100: * @param source the File object of the source to be weaved
101: * @param target the File object of the target to be weaved to
102: * @throws MalformedURLException
103: */
104: public StaticWeaveProcessor(File source, File target)
105: throws MalformedURLException {
106: this .source = source.toURL();
107: this .target = target.toURL();
108: }
109:
110: /**
111: * Constructs an instance of StaticWeaveProcessor
112: * @param source the URL of the source to be weaved
113: * @param target the URL of the target to be weaved to
114: */
115: public StaticWeaveProcessor(URL source, URL target) {
116: this .source = source;
117: this .target = target;
118: }
119:
120: /**
121: * The method allows user to specify the ouput for the log message.
122: * @param log writer - the lcation where the log message writes to. the default value is standardout
123: */
124: public void setLog(Writer logWriter) {
125: this .logWriter = logWriter;
126: }
127:
128: /**
129: * The method allows user to define nine levels toplink logging.
130: * @param level - the integer value of log level. default is OFF.
131: */
132: public void setLogLevel(int level) {
133: this .logLevel = level;
134: }
135:
136: /**
137: * Set the user classloader.
138: * @param classLoader
139: */
140: public void setClassLoader(ClassLoader classLoader) {
141: this .classLoader = classLoader;
142: }
143:
144: /**
145: * Set an explicitly identified URL of the location containing persistence.xml.
146: * @param persistenceInfo the URL of the location containing persistence.xml, the URL
147: * must point to the root of META-INF/persistence.xml
148: */
149: public void setPersistenceInfo(URL persistenceInfo) {
150: this .persistenceInfo = persistenceInfo;
151: }
152:
153: /**
154: * Set an explicitly identified the location containing persistence.xml.
155: * @param persistenceinfo the path of the location containing persistence.xml, the path
156: * must point to the root of META-INF/persistence.xml
157: */
158: public void setPersistenceInfo(String persistenceInfoPath)
159: throws MalformedURLException {
160: if (persistenceInfoPath != null) {
161: this .persistenceInfo = new File(persistenceInfoPath)
162: .toURL();
163: }
164: }
165:
166: /**
167: * Set an explicitly identified the location containing persistence.xml.
168: * @param persistenceinfo the file containing persistence.xml, the file
169: * should contain META-INF/persistence.xml
170: */
171: public void setPersistenceInfo(File persistenceInfoFile)
172: throws MalformedURLException {
173: if (persistenceInfoFile != null) {
174: this .persistenceInfo = persistenceInfoFile.toURL();
175: }
176: }
177:
178: /**
179: * This method performs weaving function on the class individually from the specified source.
180: * @throws Exception.
181: */
182: public void performWeaving() throws URISyntaxException,
183: MalformedURLException, IOException {
184: preProcess();
185: process();
186: }
187:
188: /*
189: * INTERNAL:
190: * This method perform all necessary steps(verification, pre-build the target directory)
191: * prior to the invokation of the weaving function.
192: */
193: private void preProcess() throws URISyntaxException,
194: MalformedURLException {
195: //Instantiate default session log
196: AbstractSessionLog.getLog().setLevel(this .logLevel);
197: if (logWriter != null) {
198: ((DefaultSessionLog) AbstractSessionLog.getLog())
199: .setWriter(logWriter);
200: }
201:
202: //Make sure the source is existing
203: if (!(new File(source.toURI())).exists()) {
204: throw StaticWeaveException.missingSource();
205: }
206:
207: //Verification target and source, two use cases create warning or exception.
208: //1. If source is directory and target is jar -
209: // This will lead unkown outcome, user attempt to use this tool to pack outcome into a Jar.
210: // Warning message will be logged, this is can be workarounded by other utilities.
211: //2. Both source and target are specified as a same jar -
212: // User was tryint to perform waving in same Jar which is not support, Exception will be thrown.
213: if (isDirectory(source)
214: && target.toURI().toString().endsWith(".jar")) {
215: AbstractSessionLog.getLog().log(
216: SessionLog.WARNING,
217: ToStringLocalization.buildMessage(
218: "staticweave_processor_unknown_outcome",
219: new Object[] { null }));
220: }
221:
222: if (!isDirectory(source)
223: && target.toString().equals(source.toString())) {
224: throw StaticWeaveException.weaveInplaceForJar(source
225: .toString());
226: }
227:
228: //pre-create target if it is directory and dose not exsit.
229: //Using the method File.isDirectory() is not enough to determine what the type(dir or jar)
230: //of the target(specified by URL)that user want to create. File.isDirectory() will return false in
231: //two possibilities, the location either is not directory or the location dose not exist.
232: //Therefore pre-build of the directory target is required. Pre-build for the file(JAR) target
233: //is not required since it gets built automically by opening outputstream.
234: if (!(new File(target.toURI())).exists()) {
235: if (!target.toURI().toString().endsWith(".jar")) {
236: (new File(target.toURI())).mkdirs();
237: //if directory fails to build, which may leads to unknown outcome since it will
238: //be treated as single file in the class StaticWeaveHandler and automicatlly gets built
239: //by outputstream.
240:
241: //re-assign URL.
242: target = (new File(target.toURI())).toURL();
243: }
244: }
245: }
246:
247: /*
248: * INTERNAL:
249: * The method performs weaving function
250: */
251: private void process() throws IOException, URISyntaxException {
252: //Instantiate output handler
253: AbstractStaticWeaveOutputHandler swoh;
254: if (isDirectory(this .target)) {
255: swoh = new StaticWeaveDirectoryOutputHandler(this .source,
256: this .target);
257: } else {
258: swoh = new StaticWeaveJAROutputHandler(
259: new JarOutputStream(new FileOutputStream(new File(
260: this .target.toURI()))));
261: }
262:
263: //Instantiate classloader
264: this .classLoader = (this .classLoader == null) ? Thread
265: .currentThread().getContextClassLoader()
266: : this .classLoader;
267: this .classLoader = new URLClassLoader(getURLs(),
268: this .classLoader);
269:
270: //Instantiate the classtransformer, we check if the persistenceinfo URL has been specified.
271: StaticWeaveClassTransformer classTransformer = null;
272: if (persistenceInfo != null) {
273: classTransformer = new StaticWeaveClassTransformer(
274: persistenceInfo, this .classLoader, this .logWriter,
275: this .logLevel);
276: } else {
277: classTransformer = new StaticWeaveClassTransformer(source,
278: this .classLoader, this .logWriter, this .logLevel);
279: }
280:
281: //Starting process...
282: Archive sourceArchive = (new ArchiveFactoryImpl())
283: .createArchive(source);
284: Iterator entries = sourceArchive.getEntries();
285: while (entries.hasNext()) {
286: String entryName = (String) entries.next();
287: InputStream entryInputStream = sourceArchive
288: .getEntry(entryName);
289: String className = PersistenceUnitProcessor
290: .buildClassNameFromEntryString(entryName);
291:
292: //Add a directory entry
293: swoh.addDirEntry(getDirectoryFromEntryName(entryName));
294:
295: //Add a regular entry
296: JarEntry newEntry = new JarEntry(entryName);
297:
298: byte[] originalClassBytes = null;
299: byte[] transferredClassBytes = null;
300: try {
301: Class this Class = this .classLoader.loadClass(className);
302: //if the class is not in the classpath, we simply copy the entry
303: //to the target(no weaving).
304: if (this Class == null) {
305: swoh.addEntry(entryInputStream, newEntry);
306: continue;
307: }
308:
309: //Try to read the loaded class bytes, the class bytes is required for
310: //classtransformer to perform transfer. Simply copy entry to the target(no weaving)
311: //if the class bytes can't be read.
312: InputStream is = this .classLoader
313: .getResourceAsStream(entryName);
314: if (is != null) {
315: originalClassBytes = new byte[is.available()];
316: is.read(originalClassBytes);
317: } else {
318: swoh.addEntry(entryInputStream, newEntry);
319: continue;
320: }
321:
322: //If everything is OK so far, we perform the weaving. we need three paramteres in order to
323: //class to perform weaving for that class, the class name,the class object and class bytes.
324: transferredClassBytes = classTransformer.transform(
325: className.replace('.', '/'), this Class,
326: originalClassBytes);
327:
328: //if transferredClassBytes is null means the class dose not get woven.
329: if (transferredClassBytes != null) {
330: swoh.addEntry(newEntry, transferredClassBytes);
331: } else {
332: swoh.addEntry(entryInputStream, newEntry);
333: }
334: } catch (IllegalClassFormatException e) {
335: //Anything went wrong, we need log a warning message, copy the entry to the target and
336: //process next entry.
337: swoh.addEntry(entryInputStream, newEntry);
338: continue;
339: } catch (ClassNotFoundException e) {
340: swoh.addEntry(entryInputStream, newEntry);
341: continue;
342: } finally {
343: //need close the inputstream for current entry before processing next one.
344: entryInputStream.close();
345: }
346: }
347: swoh.closeOutputStream();
348: }
349:
350: //Extract directory from entry name.
351: public static String getDirectoryFromEntryName(String entryName) {
352: String result = "";
353: if (entryName == null) {
354: return result;
355: }
356: if (entryName.lastIndexOf("/") >= 0) {
357: result = entryName.substring(0, entryName.lastIndexOf("/"))
358: + File.separator;
359: }
360: return result;
361: }
362:
363: /*
364: * Determine whether or not the URL is pointing to directory.
365: */
366: private boolean isDirectory(URL url) throws URISyntaxException {
367: File file = new File(url.toURI());
368: if (file.isDirectory()) {
369: return true;
370: } else {
371: return false;
372: }
373: }
374:
375: /*
376: * Generate URL array for specified source and persistenceinfo
377: */
378: private URL[] getURLs() {
379: if ((this .source != null) && (this .persistenceInfo != null)) {
380: return new URL[] { this .persistenceInfo, this .source };
381: } else if (this .source != null) {
382: return new URL[] { this .source };
383: } else if (this .persistenceInfo != null) {
384: return new URL[] { this .persistenceInfo };
385: }
386: return new URL[] {};
387: }
388: }
|