001: /*
002: License $Id: JspModel.java,v 1.27 2005/07/06 18:31:01 hendriks73 Exp $
003:
004: Copyright (c) 2001-2005 tagtraum industries.
005:
006: LGPL
007: ====
008:
009: jo! is free software; you can redistribute it and/or
010: modify it under the terms of the GNU Lesser General Public
011: License as published by the Free Software Foundation; either
012: version 2.1 of the License, or (at your option) any later version.
013:
014: jo! is distributed in the hope that it will be useful,
015: but WITHOUT ANY WARRANTY; without even the implied warranty of
016: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: Lesser General Public License for more details.
018:
019: You should have received a copy of the GNU Lesser General Public
020: License along with this library; if not, write to the Free Software
021: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
022:
023: For LGPL see <http://www.fsf.org/copyleft/lesser.txt>
024:
025:
026: Sun license
027: ===========
028:
029: This release contains software by Sun Microsystems. Therefore
030: the following conditions have to be met, too. They apply to the
031: files
032:
033: - lib/mail.jar
034: - lib/activation.jar
035: - lib/jsse.jar
036: - lib/jcert.jar
037: - lib/jaxp.jar
038: - lib/crimson.jar
039: - lib/servlet.jar
040: - lib/jnet.jar
041: - lib/jaas.jar
042: - lib/jaasmod.jar
043:
044: contained in this release.
045:
046: a. Licensee may not modify the Java Platform
047: Interface (JPI, identified as classes contained within the javax
048: package or any subpackages of the javax package), by creating additional
049: classes within the JPI or otherwise causing the addition to or modification
050: of the classes in the JPI. In the event that Licensee creates any
051: Java-related API and distribute such API to others for applet or
052: application development, you must promptly publish broadly, an accurate
053: specification for such API for free use by all developers of Java-based
054: software.
055:
056: b. Software is confidential copyrighted information of Sun and
057: title to all copies is retained by Sun and/or its licensors. Licensee
058: shall not modify, decompile, disassemble, decrypt, extract, or otherwise
059: reverse engineer Software. Software may not be leased, assigned, or
060: sublicensed, in whole or in part. Software is not designed or intended
061: for use in on-line control of aircraft, air traffic, aircraft navigation
062: or aircraft communications; or in the design, construction, operation or
063: maintenance of any nuclear facility. Licensee warrants that it will not
064: use or redistribute the Software for such purposes.
065:
066: c. Software is provided "AS IS," without a warranty
067: of any kind. ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES,
068: INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
069: PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
070:
071: d. This License is effective until terminated. Licensee may
072: terminate this License at any time by destroying all copies of Software.
073: This License will terminate immediately without notice from Sun if Licensee
074: fails to comply with any provision of this License. Upon such termination,
075: Licensee must destroy all copies of Software.
076:
077: e. Software, including technical data, is subject to U.S.
078: export control laws, including the U.S. Export Administration Act and its
079: associated regulations, and may be subject to export or import regulations
080: in other countries. Licensee agrees to comply strictly with all such
081: regulations and acknowledges that it has the responsibility to obtain
082: licenses to export, re-export, or import Software. Software may not be
083: downloaded, or otherwise exported or re-exported (i) into, or to a national
084: or resident of, Cuba, Iraq, Iran, North Korea, Libya, Sudan, Syria or any
085: country to which the U.S. has embargoed goods; or (ii) to anyone on the
086: U.S. Treasury Department's list of Specially Designated Nations or the U.S.
087: Commerce Department's Table of Denial Orders.
088:
089:
090: Feedback
091: ========
092:
093: We encourage your feedback and suggestions and want to use your feedback to
094: improve the Software. Send all such feedback to:
095: <feedback@tagtraum.com>
096:
097: For more information on tagtraum industries and jo!
098: please see <http://www.tagtraum.com/>.
099:
100:
101: */
102: package com.tagtraum.jo.jsp;
103:
104: import com.tagtraum.framework.bytecode.Klass;
105: import com.tagtraum.framework.compiler.Compiler;
106: import com.tagtraum.framework.compiler.CompilerException;
107: import com.tagtraum.framework.compiler.ErrorMessage;
108: import com.tagtraum.framework.compiler.JavaKeyword;
109: import com.tagtraum.framework.http.HttpException;
110: import com.tagtraum.framework.log.C_Log;
111: import com.tagtraum.framework.log.Log;
112: import com.tagtraum.framework.recycler.Recycler;
113: import com.tagtraum.framework.util.*;
114: import com.tagtraum.jo.I_JoServletContextPeer;
115: import com.tagtraum.jo.I_JoServletModel;
116: import com.tagtraum.jo.JoAbstractModel;
117: import com.tagtraum.jo.jsp.tagext.TagLibraryInfoImpl;
118: import com.tagtraum.jo.util.ServletClassLoader;
119: import org.w3c.dom.*;
120:
121: import javax.servlet.*;
122: import javax.servlet.http.HttpServletRequest;
123: import javax.servlet.http.HttpServletResponse;
124: import javax.servlet.http.HttpUtils;
125: import javax.servlet.jsp.tagext.PageData;
126: import javax.servlet.jsp.tagext.TagLibraryInfo;
127: import javax.servlet.jsp.tagext.TagLibraryValidator;
128: import javax.servlet.jsp.tagext.ValidationMessage;
129: import javax.xml.parsers.ParserConfigurationException;
130: import javax.xml.transform.OutputKeys;
131: import javax.xml.transform.Transformer;
132: import javax.xml.transform.TransformerException;
133: import javax.xml.transform.TransformerFactory;
134: import javax.xml.transform.dom.DOMSource;
135: import javax.xml.transform.stream.StreamResult;
136: import java.io.*;
137: import java.net.URL;
138: import java.util.*;
139:
140: /**
141: * Represents a jsp-file. Note: This object will be serialized to
142: * store page data persistently.
143: *
144: * @author <a href="mailto:hs@tagtraum.com">Hendrik Schreiber</a>
145: * @version 1.1beta1 $Id: JspModel.java,v 1.27 2005/07/06 18:31:01 hendriks73 Exp $
146: */
147: public class JspModel extends JoAbstractModel implements
148: I_JoServletModel, Serializable, C_Jsp {
149:
150: /**
151: * Source-Version
152: */
153: public static String vcid = "$Id: JspModel.java,v 1.27 2005/07/06 18:31:01 hendriks73 Exp $";
154:
155: private final static String C_Precompile = "jsp_precompile";
156:
157: private URL url;
158: private JspFile[] dependencies;
159: private String packageName;
160: private String partialClassName;
161: private Locale locale;
162: private SortedMap sourceNumberMapping;
163:
164: private transient Compiler compiler;
165: private transient boolean recompileOnLoad = true;
166: private transient URL[] scratchDirURL;
167: private transient File scratchDir;
168: private transient File sourceFile;
169: private transient JspEngine engine;
170: private transient JspDocument jspDocument;
171: private transient String recompileReason;
172: private transient Map tagLibInfos;
173:
174: public JspModel() {
175: super ();
176: sourceNumberMapping = new TreeMap();
177: tagLibInfos = new HashMap();
178: }
179:
180: public void setURL(URL url) {
181: this .url = url;
182: }
183:
184: public void setLocale(Locale locale) {
185: this .locale = locale;
186: }
187:
188: public void setServletContextPeer(I_JoServletContextPeer peer) {
189: // Note: This method is also called after deserialization
190: // to re-initialize a number of members that were not stored
191: // in a serialized way
192: super .setServletContextPeer(peer);
193: this .engine = peer.getJspEngine();
194: if (engine == null) {
195: setPermanentlyUnavailable(new UnavailableException(
196: "JspEngine not found"));
197: return;
198: }
199: this .compiler = engine.getCompiler();
200: this .scratchDir = engine.getScratchDir();
201: this .scratchDirURL = new URL[1];
202: try {
203: // make sure the URL points at a dir
204: this .scratchDirURL[0] = URLHelper.make(scratchDir
205: .toString()
206: + "/");
207: } catch (Exception e) {
208: e.printStackTrace(); // should not happen
209: }
210: addClassLocations(scratchDirURL);
211: // ensures that the jsp is recompiled, when the webapp is rebuilt
212: this .recompileOnLoad = true;
213: }
214:
215: /**
216: * Returns a servlet instance.
217: */
218: public Servlet getServletInstance() throws ServletException,
219: IOException {
220: // enter synchronized-Block only, if no instance exists yet.
221: boolean needsReload = needToRecompile();
222: if (servlet != null
223: && !(getServletContextPeer().isAutoReloadServlets() && needsReload)) {
224: return servlet;
225: }
226: synchronized (this ) {
227: try {
228: checkUnavailable();
229: if (needsReload)
230: reloadJsp();
231: // wait for instance to become available
232: waitForAvailableInstance();
233: Servlet jspInstance = instantiateJsp();
234: jspInstance.init(getServletConfig());
235: setServletClass(jspInstance.getClass());
236: if (jspInstance instanceof SingleThreadModel) {
237: setMaxActiveInstances(10);
238: setActiveInstances(getActiveInstances() + 1);
239: } else {
240: servlet = jspInstance;
241: setMaxActiveInstances(1);
242: }
243: setLoaded(true);
244: return jspInstance;
245: } catch (FactoryException fe) {
246: throw new ServletException(fe.toString());
247: }
248: }
249: }
250:
251: private Servlet instantiateJsp() throws IOException,
252: FactoryException {
253: Servlet aServletInstance;
254: try {
255: aServletInstance = (Servlet) Recycler.getRecycler().get(
256: getServletClassName(), getClassLoader());
257: } catch (FactoryException fe) {
258: boolean triedWorkaround = fe.getThrowable() instanceof VerifyError;
259: jikesWorkaround(fe);
260: compile();
261: try {
262: aServletInstance = (Servlet) Recycler.getRecycler()
263: .get(getServletClassName(), getClassLoader());
264: } catch (FactoryException fe2) {
265: if ((!triedWorkaround) && jikesWorkaround(fe2)) {
266: compile();
267: aServletInstance = (Servlet) Recycler.getRecycler()
268: .get(getServletClassName(),
269: getClassLoader());
270: } else
271: throw fe2;
272: }
273: }
274: return aServletInstance;
275: }
276:
277: private void reloadJsp() throws IOException {
278: // log
279: engine.log("JSP " + getJspFileName() + " (" + url
280: + ") needs to be recompiled: " + recompileReason,
281: C_Log.MODULE);
282: // destroy all jsp that were loaded with the old classloader.
283: // maybe this should signal to the webapp, that it needs to be restarted
284: // instead of just replacing the servletclassloader, maybe the whole
285: // webapp classloader should be replaced (rik)
286: try {
287: // trick the servicecall count
288: serviceMethodCalls--;
289: unload();
290: compile();
291: } finally {
292: serviceMethodCalls++;
293: }
294:
295: // remove the Classloader that is responsible for the JSP and only the JSP.
296: setClassLoader((ServletClassLoader) getClassLoader()
297: .getParent());
298: // add scratchdir again
299: addClassLocations(scratchDirURL);
300: }
301:
302: /**
303: * @return true if we really worked around jikes.
304: */
305: private boolean jikesWorkaround(FactoryException factoryException) {
306: boolean done = false;
307: if (factoryException.getThrowable() instanceof VerifyError) {
308: engine
309: .log(
310: "WARNING! Compilation and loading of "
311: + getJspFileName()
312: + " failed due to a VerifyError. It is likely that this error is caused by a bug in jikes. Will try to use a different compiler...",
313: C_Log.ERROR);
314: Compiler[] compilers = engine.getCompilers();
315: for (int i = 0; i < compilers.length && !done; i++) {
316: if (!compilers[i].getName().equals("jikes")) {
317: compiler = compilers[i];
318: engine.log("Set compiler for this JSP to: "
319: + compiler, C_Log.MODULE);
320: done = true;
321: // make sure that we have a fresh classloader with all caches cleared
322: setClassLoader((ServletClassLoader) getClassLoader()
323: .getParent());
324: // add scratchdir again
325: addClassLocations(scratchDirURL);
326: }
327: }
328: if (!done)
329: engine
330: .log(
331: "Sorry, no other compiler than jikes available.",
332: C_Log.MODULE);
333: }
334: return done;
335: }
336:
337: /**
338: * Executes the service method of the JSP.
339: *
340: * @see #isPrecompile(HttpServletRequest)
341: */
342: public void service(ServletRequest request, ServletResponse response)
343: throws IOException, ServletException {
344: if (!isPrecompile((HttpServletRequest) request)) {
345: super .service(request, response);
346: } else {
347: try {
348: preload();
349: } catch (FactoryException fe) {
350: throw new ServletException(fe);
351: }
352: }
353: }
354:
355: /**
356: * Determines whether this is just a precompile request
357: * or a real request.
358: *
359: * @param request the request
360: * @return true or false
361: * @see #service(ServletRequest, ServletResponse)
362: */
363: private boolean isPrecompile(HttpServletRequest request)
364: throws HttpException {
365: String query = request.getQueryString();
366: boolean precompile = false;
367: if (query != null) {
368: Hashtable paramTable = HttpUtils.parseQueryString(query);
369: String[] value = (String[]) paramTable.get(C_Precompile);
370: if (value != null) {
371: if ((value.length == 0) || value[0].equals("")
372: || value[0].equals("true")) {
373: precompile = true;
374: } else if (!value[0].equals("false")) {
375: throw new HttpException(
376: HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
377: "Illegal attempt to call jsp precompile: "
378: + query);
379: }
380: }
381: }
382: return precompile;
383: }
384:
385: /**
386: * Indicates whether this JSP needs to be recompiled. Possible reasons
387: * are:
388: * <ul>
389: * <li>Dependencies unknown
390: * <li>Dependent files have changed
391: * <li>Dependent files weren't found
392: * </ul>
393: * The reason is stored in the variable recompileReason.
394: *
395: * @return true or false
396: */
397: private boolean needToRecompile() {
398: if (dependencies == null) {
399: recompileReason = "Dependent files are unknown";
400: return true;
401: }
402: if (getLastModified() == 0) {
403: recompileReason = "LastModified was not set";
404: return true;
405: }
406: if (engine.getServletContextPeer().isAutoReloadServlets()
407: || recompileOnLoad) {
408: try {
409: recompileOnLoad = false;
410: for (int i = 0; i < dependencies.length; i++) {
411: if (!dependencies[i].isCurrent()) {
412: recompileReason = "Dependent file "
413: + dependencies[i] + " has changed.";
414: return true;
415: }
416: if (dependencies[i].getLastModified() > getLastModified()) {
417: recompileReason = "Dependent file "
418: + dependencies[i]
419: + " is newer than the previously compiled classfile.";
420: return true;
421: }
422: }
423: } catch (IOException ioe) {
424: recompileReason = "Dependent file wasn't found or could not be accessed - "
425: + ioe.toString();
426: return true;
427: }
428: }
429: if (Log.getLog(getServletContextPeer().getHost().getName())
430: .isLog(C_Log.FORTYTWO)) {
431: log("Method: needToRecompile: false", C_Log.FORTYTWO);
432: }
433: return false;
434: }
435:
436: /**
437: * Parses, generates, and compiles a jsp file.
438: */
439: private void compile() throws IOException {
440: parse();
441: dependencies = jspDocument.getDependencies();
442: if (Log.getLog(getServletContextPeer().getHost().getName())
443: .isLog(C_Log.METHOD)) {
444: for (int i = 0; i < dependencies.length; i++) {
445: log("Depends on " + dependencies[i], C_Log.METHOD);
446: }
447: }
448: // compute classname, sourcefilename etc.
449: setServletClassName(getClassName(getJspFileName()));
450: partialClassName = getServletClassName();
451: packageName = null;
452: int lastDot = getServletClassName().lastIndexOf('.');
453: // we have to hand this to the codegenerator
454: if (lastDot != -1) {
455: packageName = getServletClassName().substring(0, lastDot);
456: partialClassName = getServletClassName().substring(
457: lastDot + 1);
458: }
459: sourceFile = JspEngine.getFileName(scratchDir,
460: getServletClassName(), ".java");
461: generate();
462: if (Log.getLog(getServletContextPeer().getHost().getName())
463: .isLog(C_Log.METHOD)) {
464: log("Compiling " + sourceFile + " with compiler "
465: + compiler, C_Log.METHOD);
466: }
467: try {
468: compiler.compile(sourceFile, jspDocument.getCharEncoding());
469: } catch (CompilerException ce) {
470: // set URI
471: ce.setURI(getJspFileName());
472: //ce.printStackTrace();
473: throw ce;
474: }
475: addSmap();
476: setLastModified(System.currentTimeMillis());
477: if (Log.getLog(getServletContextPeer().getHost().getName())
478: .isLog(C_Log.METHOD)) {
479: log("No Errors.", C_Log.METHOD);
480: }
481: }
482:
483: private void addSmap() throws IOException {
484: String classFilename = compiler.getOutputDirectory() + "/"
485: + getServletClassName().replace('.', '/') + ".class";
486: FileInputStream in = new FileInputStream(classFilename);
487: Klass klass;
488: try {
489: klass = new Klass(in);
490: } finally {
491: in.close();
492: }
493: ByteArrayOutputStream bout = new ByteArrayOutputStream();
494: jspDocument.getSmap().write(bout);
495: klass.setSourceDebugExtension(new String(bout.toByteArray(),
496: "UTF-8"));
497: final DataOutputStream dataOutput = new DataOutputStream(
498: new BufferedOutputStream(new FileOutputStream(
499: classFilename)));
500: try {
501: klass.write(dataOutput);
502: } finally {
503: dataOutput.close();
504: }
505: }
506:
507: private void generate() throws IOException {
508: if (Log.getLog(getServletContextPeer().getHost().getName())
509: .isLog(C_Log.METHOD)) {
510: log("Writing source to " + sourceFile, C_Log.METHOD);
511: }
512: OutputStream smapOut = null;
513: try {
514: // the generator gets attributes through getter methods
515: engine.getJspGeneratorManager().generate(this );
516: // write smap file
517: smapOut = new FileOutputStream(sourceFile + ".smap");
518: jspDocument.getSmap()
519: .setGeneratedFile(sourceFile.getName());
520: jspDocument.getSmap().write(smapOut);
521: } finally {
522: if (smapOut != null) {
523: try {
524: smapOut.close();
525: } catch (IOException ignore) {
526: }
527: }
528: }
529: }
530:
531: private void parse() throws IOException {
532: if (Log.getLog(getServletContextPeer().getHost().getName())
533: .isLog(C_Log.METHOD)) {
534: log("Parsing " + getJspFileName(), C_Log.METHOD);
535: }
536: try {
537: jspDocument = engine.createParser(getJspFileName()).parse(
538: getJspFileName(), locale);
539: instantiateTagLibraryInfos();
540: } catch (ParserConfigurationException e) {
541: engine.log(
542: "Failed to create DOM document while parsing JSP.",
543: e, Log.ERROR);
544: throw new IOException(
545: "Failed to create DOM document while parsing JSP: "
546: + e);
547: }
548: }
549:
550: /**
551: * Instantiates the TagLibraryInfoImpls.
552: */
553: private void instantiateTagLibraryInfos() throws IOException {
554: // get root element
555: Element root = (Element) this .getDocument()
556: .getElementsByTagNameNS(XMLNS_JSP_VALUE, JSP_ROOT)
557: .item(0);
558: NamedNodeMap attributes = root.getAttributes();
559: if (tagLibInfos == null)
560: tagLibInfos = new HashMap();
561:
562: byte[] xmlDocument = null;
563: for (int i = 0; i < attributes.getLength(); i++) {
564: Attr attribute = (Attr) attributes.item(i);
565: String localName = attribute.getLocalName();
566:
567: // in the future implement jsp as taglib! (rik)
568: if ("xmlns".equals(attribute.getPrefix())
569: && !localName.equals("jsp")) {
570: String uri = attribute.getValue();
571: final TagLibraryInfoImpl tagLibraryInfo = (TagLibraryInfoImpl) engine
572: .getServletContextPeer().getTagLibraryInfo(uri,
573: localName, this );
574: tagLibInfos.put(localName, tagLibraryInfo);
575: TagLibraryValidator tagLibraryValidator = tagLibraryInfo
576: .getTagLibraryValidator();
577: if (tagLibraryValidator != null) {
578: if (xmlDocument == null) {
579: try {
580: xmlDocument = createXMLDocument();
581: Log log = Log.getLog(engine
582: .getServletContextPeer().getHost()
583: .getName());
584: if (log.isLog(Log.FORTYTWO))
585: engine.log("XMLView of "
586: + getJspFileName()
587: + ": "
588: + new String(xmlDocument,
589: "UTF-8"), Log.FORTYTWO);
590: } catch (TransformerException e) {
591: engine.log(e, Log.ERROR);
592: throw new IOException(
593: "Failed to create XML view of "
594: + getJspFileName() + ": "
595: + e);
596: }
597: }
598: final byte[] bytes = xmlDocument;
599: PageData pageData = new PageData() {
600: private InputStream in = new ByteArrayInputStream(
601: bytes);
602:
603: public InputStream getInputStream() {
604: return in;
605: }
606: };
607: ValidationMessage[] messages = tagLibraryValidator
608: .validate(localName, uri, pageData);
609: if (messages != null && messages.length > 0) {
610: StringBuffer sb = new StringBuffer();
611: sb
612: .append("The following errors occurred while validating "
613: + getJspFileName() + ": ");
614: for (int j = 0; j < messages.length; j++) {
615: final String id = messages[j].getId();
616: Element element = jspDocument
617: .getElement(id);
618: FileLocation location = jspDocument
619: .getFileLocation(element);
620: final String message = getJspFileName()
621: + ": Taglib validation error <"
622: + localName
623: + ":"
624: + (element == null ? "???"
625: : element.getLocalName())
626: + " ...> "
627: + (location != null ? location
628: .toString()
629: + " " : "")
630: + messages[j].getMessage();
631: engine.log(message, Log.ERROR);
632: sb.append(System
633: .getProperty("line.separator")
634: + message);
635: }
636: throw new IOException(sb.toString());
637: } else {
638: engine
639: .log(
640: getJspFileName()
641: + ": Successfully validated the usage of taglib with localName/namespace "
642: + localName
643: + " and uri " + uri,
644: Log.METHOD);
645: }
646: }
647: }
648: }
649: }
650:
651: private byte[] createXMLDocument() throws TransformerException {
652: Transformer transformer = TransformerFactory.newInstance()
653: .newTransformer();
654: transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
655: "no");
656: transformer.setOutputProperty(OutputKeys.METHOD, "xml");
657: transformer.setOutputProperty(OutputKeys.INDENT, "no");
658: transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
659: final ByteArrayOutputStream baos = new ByteArrayOutputStream();
660: transformer.transform(new DOMSource(jspDocument.getDocument()),
661: new StreamResult(baos));
662: return baos.toByteArray();
663: }
664:
665: public TagLibraryInfo getTagLibraryInfo(String prefix) {
666: return (TagLibraryInfo) tagLibInfos.get(prefix);
667: }
668:
669: /**
670: * Returns the {@link Document}.
671: */
672: public Document getDocument() {
673: return jspDocument.getDocument();
674: }
675:
676: /**
677: * Returns the {@link JspDocument}.
678: */
679: public JspDocument getJspDocument() {
680: return jspDocument;
681: }
682:
683: /**
684: * Returns the package name.
685: */
686: public String getPackageName() {
687: return packageName;
688: }
689:
690: /**
691: * Returns the name of the class without packagename.
692: */
693: public String getPartialClassName() {
694: return partialClassName;
695: }
696:
697: /**
698: * Returns the engine.
699: *
700: * @return engine
701: */
702: public JspEngine getEngine() {
703: return engine;
704: }
705:
706: /**
707: * Logs a message.
708: */
709: public void log(String message, int level) {
710: engine.log(message, level);
711: }
712:
713: /**
714: * String representation of this jsp.
715: */
716: public String toString() {
717: return getJspFileName() + " [" + url + "]";
718: }
719:
720: /**
721: * Returns the classname for a URI.
722: *
723: * @param uri URI to convert
724: *
725: * @return classname
726: */
727: private String getClassName(String uri) {
728: // prevent classnames that only represent the extension .jsp
729: int idx = uri.lastIndexOf('.');
730: if (idx != -1) {
731: String temp = uri.substring(0, idx) + "_";
732: if (idx + 1 < uri.length()) {
733: uri = temp + uri.substring(idx + 1);
734: }
735: }
736:
737: StringBuffer sb = new StringBuffer(uri.length());
738: sb.append('.');
739: for (StringTokenizer st = new StringTokenizer(uri, "/"); st
740: .hasMoreTokens();) {
741: String token = st.nextToken();
742: sb.append(token);
743: if (JavaKeyword.isKeyword(token)) {
744: sb.append("_");
745: if (Log.isLog(Log.ERROR, getServletContextPeer()
746: .getName()))
747: Log
748: .getLog(getServletContextPeer().getName())
749: .log(
750: "JSP path of "
751: + uri
752: + " contains a Java keyword. This can lead to problems while debugging the JSP: "
753: + token, Log.ERROR);
754: }
755: if (st.hasMoreTokens())
756: sb.append('.');
757: }
758: return engine.getClassNamePrefix()
759: + StringHelper.toJavaIdentifier(sb.toString());
760: }
761:
762: /**
763: * Returns the name of the source file.
764: */
765: public File getSourceFile() {
766: return sourceFile;
767: }
768:
769: /**
770: * Maps a linenumber in the generated java source file to the file and linenumber it was generated from.
771: *
772: * @param generatedLineNumber the linenumber in the generated source file
773: * @param node the node in the DOM
774: */
775: public void registerLineNumberMapping(int generatedLineNumber,
776: Node node) {
777: FileLocation location = jspDocument.getFileLocation(node);
778: if (location != null)
779: sourceNumberMapping.put(new Integer(generatedLineNumber),
780: location);
781: }
782:
783: /**
784: * Returns the location of the original source code in a jsp that was compiled to
785: * a given line number in the generated java file.
786: *
787: * @param generatedLineNumber the linenumber in the generated source file
788: */
789: public FileLocation getFileLocation(int generatedLineNumber) {
790: Integer last = (Integer) sourceNumberMapping.lastKey();
791: if (generatedLineNumber > last.intValue())
792: return (FileLocation) sourceNumberMapping.get(last);
793: for (int i = generatedLineNumber; i >= 0; i--) {
794: FileLocation location = (FileLocation) sourceNumberMapping
795: .get(new Integer(i));
796: if (location != null)
797: return location;
798: }
799: return null;
800: }
801:
802: /**
803: * Returns the next location of the original source code in a jsp that was compiled to
804: * a given line number in the generated java file.
805: *
806: * @param generatedLineNumber the linenumber in the generated source file
807: */
808: public FileLocation getNextFileLocation(int generatedLineNumber) {
809: Integer last = (Integer) sourceNumberMapping.lastKey();
810: if (generatedLineNumber > last.intValue())
811: return null;
812: for (int i = generatedLineNumber + 1; i < last.intValue(); i++) {
813: FileLocation location = (FileLocation) sourceNumberMapping
814: .get(new Integer(i));
815: if (location != null)
816: return location;
817: }
818: return null;
819: }
820:
821: public String getLogMessage(ErrorMessage message) {
822: FileLocation location = getFileLocation(message.getLine());
823: FileLocation nextLocation = getNextFileLocation(message
824: .getLine());
825: int lastLine = Integer.MAX_VALUE;
826: if (nextLocation != null)
827: lastLine = nextLocation.getLineNumber();
828: StringBuffer sb = new StringBuffer();
829: sb.append(message.toString());
830: if (location != null) {
831: sb.append(System.getProperty("line.separator"));
832: sb
833: .append("The location of this error probably corresponds to ");
834: sb.append(location.getReal());
835: sb.append(", line ");
836: sb.append(location.getLineNumber());
837: sb.append(':');
838: sb.append(System.getProperty("line.separator"));
839: appendFileExcerpt(location, lastLine, sb);
840: }
841: return sb.toString();
842: }
843:
844: void appendFileExcerpt(FileLocation location, int lastLine,
845: StringBuffer sb) {
846: LineNumberReader reader = null;
847: try {
848: String fileName = URLDecoder.decode(location.getReal()
849: .getFile());
850: reader = new LineNumberReader(new InputStreamReader(
851: new FileInputStream(fileName)));
852: String line = null;
853: while ((line = reader.readLine()) != null
854: && reader.getLineNumber() <= (lastLine + 1)) {
855: if (reader.getLineNumber() + 1 >= location
856: .getLineNumber()) {
857: sb.append(reader.getLineNumber());
858: sb.append(". ");
859: sb.append(line);
860: sb.append(System.getProperty("line.separator"));
861: }
862: }
863: } catch (Exception e) {
864: e.printStackTrace();
865: sb.append("Failed to access sourcefile "
866: + location.getReal() + ": " + e);
867: } finally {
868: try {
869: if (reader != null)
870: reader.close();
871: } catch (IOException ioe) {
872: }
873: }
874: }
875:
876: }
|