001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.language.programming.java;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.util.List;
022: import java.util.StringTokenizer;
023:
024: import org.apache.avalon.framework.activity.Disposable;
025: import org.apache.avalon.framework.activity.Initializable;
026: import org.apache.avalon.framework.logger.LogEnabled;
027: import org.apache.avalon.framework.parameters.ParameterException;
028: import org.apache.avalon.framework.parameters.Parameters;
029: import org.apache.avalon.framework.service.ServiceException;
030: import org.apache.avalon.framework.service.ServiceManager;
031: import org.apache.avalon.framework.service.Serviceable;
032: import org.apache.avalon.framework.thread.ThreadSafe;
033:
034: import org.apache.cocoon.components.classloader.ClassLoaderManager;
035: import org.apache.cocoon.components.language.LanguageException;
036: import org.apache.cocoon.components.language.markup.xsp.XSLTExtension;
037: import org.apache.cocoon.components.language.programming.CompiledProgrammingLanguage;
038: import org.apache.cocoon.components.language.programming.CompilerError;
039: import org.apache.cocoon.components.language.programming.LanguageCompiler;
040: import org.apache.cocoon.util.ClassUtils;
041: import org.apache.cocoon.util.JavaArchiveFilter;
042: import org.apache.commons.lang.SystemUtils;
043:
044: /**
045: * The Java programming language processor
046: *
047: * @author <a href="mailto:ricardo@apache.org">Ricardo Rocha</a>
048: * @version CVS $Id: JavaLanguage.java 433543 2006-08-22 06:22:54Z crossley $
049: */
050: public class JavaLanguage extends CompiledProgrammingLanguage implements
051: Initializable, ThreadSafe, Serviceable, Disposable {
052:
053: /** The class loader */
054: private ClassLoaderManager classLoaderManager;
055:
056: /** The service manager */
057: protected ServiceManager manager = null;
058:
059: /** Classpath */
060: private String classpath;
061:
062: /** The Class Loader Class Name */
063: private String classLoaderClass;
064:
065: /** Source code version */
066: private int compilerComplianceLevel;
067:
068: /**
069: * Return the language's canonical source file extension.
070: *
071: * @return The source file extension
072: */
073: public String getSourceExtension() {
074: return "java";
075: }
076:
077: /**
078: * Return the language's canonical object file extension.
079: *
080: * @return The object file extension
081: */
082: public String getObjectExtension() {
083: return "class";
084: }
085:
086: /**
087: * Set the configuration parameters. This method instantiates the
088: * sitemap-specified <code>ClassLoaderManager</code>
089: *
090: * @param params The configuration parameters
091: * @throws ParameterException If the class loader manager cannot be
092: * instantiated or looked up.
093: */
094: public void parameterize(Parameters params)
095: throws ParameterException {
096: super .parameterize(params);
097:
098: this .classLoaderClass = params.getParameter("class-loader",
099: null);
100: if (this .classLoaderClass != null) {
101: try {
102: this .classLoaderManager = (ClassLoaderManager) ClassUtils
103: .newInstance(this .classLoaderClass);
104: } catch (Exception e) {
105: throw new ParameterException(
106: "Unable to load class loader: "
107: + this .classLoaderClass, e);
108: }
109: } else {
110: try {
111: getLogger().debug(
112: "Looking up " + ClassLoaderManager.ROLE);
113: this .classLoaderManager = (ClassLoaderManager) manager
114: .lookup(ClassLoaderManager.ROLE);
115: } catch (ServiceException e) {
116: throw new ParameterException(
117: "Lookup of ClassLoaderManager failed", e);
118: }
119: }
120: // Get the compiler compliance level (source Code version)
121: String sourceVer = params.getParameter(
122: "compiler-compliance-level", "auto");
123: if (sourceVer.equalsIgnoreCase("auto")) {
124: this .compilerComplianceLevel = SystemUtils.JAVA_VERSION_INT;
125: } else {
126: try {
127: compilerComplianceLevel = new Float(Float
128: .parseFloat(sourceVer) * 100).intValue();
129: } catch (NumberFormatException e) {
130: throw new ParameterException(
131: "XSP: compiler-compliance-level parameter value not valid!",
132: e);
133: }
134: }
135: }
136:
137: /**
138: * Set the global service manager.
139: *
140: * @param manager The global service manager
141: */
142: public void service(ServiceManager manager) throws ServiceException {
143: this .manager = manager;
144: }
145:
146: public void initialize() throws Exception {
147:
148: // Initialize the classpath
149: String systemBootClasspath = System
150: .getProperty("sun.boot.class.path");
151: String systemClasspath = SystemUtils.JAVA_CLASS_PATH;
152: String systemExtDirs = SystemUtils.JAVA_EXT_DIRS;
153: String systemExtClasspath = null;
154:
155: try {
156: systemExtClasspath = expandDirs(systemExtDirs);
157: } catch (Exception e) {
158: getLogger().warn(
159: "Could not expand Directory:" + systemExtDirs, e);
160: }
161:
162: this .classpath = ((super .classpath != null) ? File.pathSeparator
163: + super .classpath
164: : "")
165: + ((systemBootClasspath != null) ? File.pathSeparator
166: + systemBootClasspath : "")
167: + ((systemClasspath != null) ? File.pathSeparator
168: + systemClasspath : "")
169: + ((systemExtClasspath != null) ? File.pathSeparator
170: + systemExtClasspath : "");
171: }
172:
173: /**
174: * Actually load an object program from a class file.
175: *
176: * @param name The object program base file name
177: * @param baseDirectory The directory containing the object program file
178: * @return The loaded object program
179: * @exception LanguageException If an error occurs during loading
180: */
181: protected Class loadProgram(String name, File baseDirectory)
182: throws LanguageException {
183: try {
184: this .classLoaderManager.addDirectory(baseDirectory);
185: return this .classLoaderManager.loadClass(name.replace(
186: File.separatorChar, '.'));
187: } catch (Exception e) {
188: throw new LanguageException(
189: "Could not load class for program '" + name
190: + "' due to a " + e.getClass().getName()
191: + ": " + e.getMessage());
192: }
193: }
194:
195: /**
196: * Compile a source file yielding a loadable class file.
197: *
198: * @param name The object program base file name
199: * @param baseDirectory The directory containing the object program file
200: * @param encoding The encoding expected in the source file or
201: * <code>null</code> if it is the platform's default encoding
202: * @exception LanguageException If an error occurs during compilation
203: */
204: protected void compile(String name, File baseDirectory,
205: String encoding) throws LanguageException {
206:
207: try {
208: LanguageCompiler compiler = (LanguageCompiler) this .compilerClass
209: .newInstance();
210: // AbstractJavaCompiler is LogEnabled
211: if (compiler instanceof LogEnabled) {
212: ((LogEnabled) compiler).enableLogging(getLogger());
213: }
214: if (compiler instanceof Serviceable) {
215: ((Serviceable) compiler).service(this .manager);
216: }
217:
218: int pos = name.lastIndexOf(File.separatorChar);
219: String filename = name.substring(pos + 1);
220:
221: final String basePath = baseDirectory.getCanonicalPath();
222: String filepath = basePath + File.separator + name + "."
223: + getSourceExtension();
224:
225: compiler.setFile(filepath);
226: compiler.setSource(basePath);
227: compiler.setDestination(basePath);
228: compiler.setClasspath(basePath + this .classpath);
229: compiler
230: .setCompilerComplianceLevel(compilerComplianceLevel);
231:
232: if (encoding != null) {
233: compiler.setEncoding(encoding);
234: }
235:
236: if (getLogger().isDebugEnabled()) {
237: getLogger().debug("Compiling " + filepath);
238: }
239: if (!compiler.compile()) {
240: StringBuffer message = new StringBuffer(
241: "Error compiling ");
242: message.append(filename);
243: message.append(":\n");
244:
245: List errors = compiler.getErrors();
246: CompilerError[] compilerErrors = new CompilerError[errors
247: .size()];
248: errors.toArray(compilerErrors);
249:
250: throw new LanguageException(message.toString(),
251: filepath, compilerErrors);
252: }
253:
254: } catch (InstantiationException e) {
255: getLogger().warn("Could not instantiate the compiler", e);
256: throw new LanguageException(
257: "Could not instantiate the compiler: "
258: + e.getMessage());
259: } catch (IllegalAccessException e) {
260: getLogger().warn("Could not access the compiler class", e);
261: throw new LanguageException(
262: "Could not access the compiler class: "
263: + e.getMessage());
264: } catch (IOException e) {
265: getLogger().warn("Error during compilation", e);
266: throw new LanguageException("Error during compilation: "
267: + e.getMessage());
268: } catch (ServiceException e) {
269: getLogger().warn("Could not initialize the compiler", e);
270: throw new LanguageException(
271: "Could not initialize the compiler: "
272: + e.getMessage());
273: }
274: }
275:
276: /**
277: * Unload a previously loaded class. This method simply reinstantiates the
278: * class loader to ensure that a new version of the same class will be
279: * correctly loaded in a future loading operation
280: *
281: * @param program A previously loaded class
282: * @exception LanguageException If an error occurs during unloading
283: */
284: public void doUnload(Object program) throws LanguageException {
285: this .classLoaderManager.reinstantiate();
286: }
287:
288: /**
289: * Escape a <code>String</code> according to the Java string constant
290: * encoding rules.
291: *
292: * @param constant The string to be escaped
293: * @return The escaped string
294: */
295: public String quoteString(String constant) {
296: return XSLTExtension.escapeJavaString(constant);
297: }
298:
299: /**
300: * Expand a directory path or list of directory paths (File.pathSeparator
301: * delimited) into a list of file paths of all the jar files in those
302: * directories.
303: *
304: * @param dirPaths The string containing the directory path or list of
305: * directory paths.
306: * @return The file paths of the jar files in the directories. This is an
307: * empty string if no files were found, and is terminated by an
308: * additional pathSeparator in all other cases.
309: */
310: private String expandDirs(String dirPaths) {
311: StringTokenizer st = new StringTokenizer(dirPaths,
312: File.pathSeparator);
313: StringBuffer buffer = new StringBuffer();
314: while (st.hasMoreTokens()) {
315: String d = st.nextToken();
316: File dir = new File(d);
317: if (!dir.isDirectory()) {
318: // The absence of a listed directory may not be an error.
319: if (getLogger().isWarnEnabled()) {
320: getLogger().warn(
321: "Attempted to retrieve directory listing of non-directory "
322: + dir.toString());
323: }
324: } else {
325: File[] files = dir.listFiles(new JavaArchiveFilter());
326: for (int i = 0; i < files.length; i++) {
327: buffer.append(files[i]).append(File.pathSeparator);
328: }
329: }
330: }
331: return buffer.toString();
332: }
333:
334: /**
335: * dispose
336: */
337: public void dispose() {
338: if (this.classLoaderClass == null
339: && this.classLoaderManager != null) {
340: manager.release(this.classLoaderManager);
341: this.classLoaderManager = null;
342: }
343: }
344: }
|