Esempio n. 1
0
 /** Remove generated files */
 public void removeGeneratedFiles() {
   try {
     String classFileName = ctxt.getClassFileName();
     if (classFileName != null) {
       File classFile = new File(classFileName);
       if (log.isDebugEnabled()) log.debug("Deleting " + classFile);
       if (classFile.exists()) {
         if (!classFile.delete()) {
           log.warn(
               Localizer.getMessage(
                   "jsp.warning.compiler.classfile.delete.fail", classFile.getAbsolutePath()));
         }
       }
     }
   } catch (Exception e) {
     // Remove as much as possible, ignore possible exceptions
   }
   try {
     String javaFileName = ctxt.getServletJavaFileName();
     if (javaFileName != null) {
       File javaFile = new File(javaFileName);
       if (log.isDebugEnabled()) log.debug("Deleting " + javaFile);
       if (javaFile.exists()) {
         if (!javaFile.delete()) {
           log.warn(
               Localizer.getMessage(
                   "jsp.warning.compiler.javafile.delete.fail", javaFile.getAbsolutePath()));
         }
       }
     }
   } catch (Exception e) {
     // Remove as much as possible, ignore possible exceptions
   }
 }
Esempio n. 2
0
 public void removeGeneratedClassFiles() {
   try {
     String classFileName = ctxt.getClassFileName();
     if (classFileName != null) {
       File classFile = new File(classFileName);
       if (log.isLoggable(Level.FINE)) log.fine("Deleting " + classFile);
       classFile.delete();
     }
   } catch (Exception e) {
     // Remove as much as possible, ignore possible exceptions
   }
 }
Esempio n. 3
0
  /**
   * Compile the jsp file from the current engine context. As an side- effect, tag files that are
   * referenced by this page are also compiled.
   *
   * @param compileClass If true, generate both .java and .class file If false, generate only .java
   *     file
   * @param jspcMode true if invoked from JspC, false otherwise
   */
  public void compile(boolean compileClass, boolean jspcMode)
      throws FileNotFoundException, JasperException, Exception {
    if (errDispatcher == null) {
      this.errDispatcher = new ErrorDispatcher(jspcMode);
    }

    try {
      String[] smap = generateJava();
      if (compileClass) {
        generateClass(smap);
        // Fix for bugzilla 41606
        // Set JspServletWrapper.servletClassLastModifiedTime after successful compile
        String targetFileName = ctxt.getClassFileName();
        if (targetFileName != null) {
          File targetFile = new File(targetFileName);
          if (targetFile.exists() && jsw != null) {
            jsw.setServletClassLastModifiedTime(targetFile.lastModified());
          }
        }
      }
    } finally {
      if (tfp != null && ctxt.isPrototypeMode()) {
        tfp.removeProtoTypeFiles(null);
      }
      // Make sure these object which are only used during the
      // generation and compilation of the JSP page get
      // dereferenced so that they can be GC'd and reduce the
      // memory footprint.
      tfp = null;
      errDispatcher = null;
      pageInfo = null;

      // Only get rid of the pageNodes if in production.
      // In development mode, they are used for detailed
      // error messages.
      // http://issues.apache.org/bugzilla/show_bug.cgi?id=37062
      if (!this.options.getDevelopment()) {
        pageNodes = null;
      }

      if (ctxt.getWriter() != null) {
        ctxt.getWriter().close();
        ctxt.setWriter(null);
      }
    }
  }
Esempio n. 4
0
  /**
   * Determine if a compilation is necessary by checking the time stamp of the JSP page with that of
   * the corresponding .class or .java file. If the page has dependencies, the check is also
   * extended to its dependeants, and so on. This method can by overidden by a subclasses of
   * Compiler.
   *
   * @param checkClass If true, check against .class file, if false, check against .java file.
   */
  public boolean isOutDated(boolean checkClass) {

    String jsp = ctxt.getJspFile();

    if (jsw != null && (ctxt.getOptions().getModificationTestInterval() > 0)) {

      if (jsw.getLastModificationTest() + (ctxt.getOptions().getModificationTestInterval() * 1000)
          > System.currentTimeMillis()) {
        return false;
      } else {
        jsw.setLastModificationTest(System.currentTimeMillis());
      }
    }

    long jspRealLastModified = 0;
    // START PWC 6468930
    File targetFile;

    if (checkClass) {
      targetFile = new File(ctxt.getClassFileName());
    } else {
      targetFile = new File(ctxt.getServletJavaFileName());
    }

    // Get the target file's last modified time. File.lastModified()
    // returns 0 if the file does not exist.
    long targetLastModified = targetFile.lastModified();

    // Check cached class file
    if (checkClass) {
      JspRuntimeContext rtctxt = ctxt.getRuntimeContext();
      String className = ctxt.getFullClassName();
      long cachedTime = rtctxt.getBytecodeBirthTime(className);
      if (cachedTime > targetLastModified) {
        targetLastModified = cachedTime;
      } else {
        // Remove from cache, since the bytecodes from the file is more
        // current, so that JasperLoader won't load the cached version
        rtctxt.setBytecode(className, null);
      }
    }

    if (targetLastModified == 0L) return true;

    // Check if the jsp exists in the filesystem (instead of a jar
    // or a remote location). If yes, then do a File.lastModified()
    // to determine its last modified time. This is more performant
    // (fewer stat calls) than the ctxt.getResource() followed by
    // openConnection(). However, it only works for file system jsps.
    // If the file has indeed changed, then need to call URL.OpenConnection()
    // so that the cache loads the latest jsp file
    if (jsw != null) {
      File jspFile = jsw.getJspFile();
      if (jspFile != null) {
        jspRealLastModified = jspFile.lastModified();
      }
    }
    if (jspRealLastModified == 0 || targetLastModified < jspRealLastModified) {
      // END PWC 6468930
      try {
        URL jspUrl = ctxt.getResource(jsp);
        if (jspUrl == null) {
          ctxt.incrementRemoved();
          return false;
        }
        URLConnection uc = jspUrl.openConnection();
        jspRealLastModified = uc.getLastModified();
        uc.getInputStream().close();
      } catch (Exception e) {
        e.printStackTrace();
        return true;
      }
      // START PWC 6468930
    }
    // END PWC 6468930
    /* PWC 6468930
    long targetLastModified = 0;
    File targetFile;

    if( checkClass ) {
        targetFile = new File(ctxt.getClassFileName());
    } else {
        targetFile = new File(ctxt.getServletJavaFileName());
    }

    if (!targetFile.exists()) {
        return true;
    }

    targetLastModified = targetFile.lastModified();
    */
    if (checkClass && jsw != null) {
      jsw.setServletClassLastModifiedTime(targetLastModified);
    }

    if (targetLastModified < jspRealLastModified) {
      // Remember JSP mod time
      jspModTime = jspRealLastModified;
      if (log.isLoggable(Level.FINE)) {
        log.fine("Compiler: outdated: " + targetFile + " " + targetLastModified);
      }
      return true;
    }

    // determine if source dependent files (e.g. includes using include
    // directives) have been changed.
    if (jsw == null) {
      return false;
    }

    List depends = jsw.getDependants();
    if (depends == null) {
      return false;
    }

    Iterator it = depends.iterator();
    while (it.hasNext()) {
      String include = (String) it.next();
      try {
        URL includeUrl = ctxt.getResource(include);
        if (includeUrl == null) {
          return true;
        }

        URLConnection includeUconn = includeUrl.openConnection();
        long includeLastModified = includeUconn.getLastModified();
        includeUconn.getInputStream().close();

        if (includeLastModified > targetLastModified) {
          // START GlassFish 750
          if (include.endsWith(".tld")) {
            ctxt.clearTaglibs();
            ctxt.clearTagFileJarUrls();
          }
          // END GlassFish 750
          return true;
        }
      } catch (Exception e) {
        e.printStackTrace();
        return true;
      }
    }

    return false;
  }
Esempio n. 5
0
  /** Compile the servlet from .java file to .class file */
  private void generateClass() throws FileNotFoundException, JasperException, Exception {

    long t1 = 0;
    if (log.isLoggable(Level.FINE)) {
      t1 = System.currentTimeMillis();
    }

    String javaFileName = ctxt.getServletJavaFileName();
    String classpath = ctxt.getClassPath();
    String sep = System.getProperty("path.separator");

    // Initializing classpath
    ArrayList<File> cpath = new ArrayList<File>();
    HashSet<String> paths = new HashSet<String>();

    // Process classpath, which includes system classpath from compiler
    // options, plus the context classpath from the classloader
    String sysClassPath = options.getSystemClassPath();
    if (sysClassPath != null) {
      StringTokenizer tokenizer = new StringTokenizer(sysClassPath, sep);
      while (tokenizer.hasMoreElements()) {
        String path = tokenizer.nextToken();
        if (!paths.contains(path) && !systemJarInWebinf(path)) {
          paths.add(path);
          cpath.add(new File(path));
        }
      }
    }
    if (classpath != null) {
      StringTokenizer tokenizer = new StringTokenizer(classpath, sep);
      while (tokenizer.hasMoreElements()) {
        String path = tokenizer.nextToken();
        if (!paths.contains(path) && !systemJarInWebinf(path)) {
          paths.add(path);
          cpath.add(new File(path));
        }
      }
    }
    if (log.isLoggable(Level.FINE)) {
      log.fine("Using classpath: " + sysClassPath + sep + classpath);
    }
    javaCompiler.setClassPath(cpath);

    // Set debug info
    javaCompiler.setDebug(options.getClassDebugInfo());

    // Initialize and set java extensions
    String exts = System.getProperty("java.ext.dirs");
    if (exts != null) {
      javaCompiler.setExtdirs(exts);
    }

    if (options.getCompilerTargetVM() != null) {
      javaCompiler.setTargetVM(options.getCompilerTargetVM());
    }

    if (options.getCompilerSourceVM() != null) {
      javaCompiler.setSourceVM(options.getCompilerSourceVM());
    }

    // Start java compilation
    JavacErrorDetail[] javacErrors = javaCompiler.compile(ctxt.getFullClassName(), pageNodes);

    if (javacErrors != null) {
      // If there are errors, always generate java files to disk.
      javaCompiler.doJavaFile(true);

      log.severe("Error compiling file: " + javaFileName);
      errDispatcher.javacError(javacErrors);
    }

    if (log.isLoggable(Level.FINE)) {
      long t2 = System.currentTimeMillis();
      log.fine("Compiled " + javaFileName + " " + (t2 - t1) + "ms");
    }

    // Save or delete the generated Java files, depending on the
    // value of "keepgenerated" attribute
    javaCompiler.doJavaFile(ctxt.keepGenerated());

    // JSR45 Support
    if (!ctxt.isPrototypeMode() && !options.isSmapSuppressed()) {
      smapUtil.installSmap();
    }

    // START CR 6373479
    if (jsw != null && jsw.getServletClassLastModifiedTime() <= 0) {
      jsw.setServletClassLastModifiedTime(javaCompiler.getClassLastModified());
    }
    // END CR 6373479

    if (options.getSaveBytecode()) {
      javaCompiler.saveClassFile(ctxt.getFullClassName(), ctxt.getClassFileName());
    }

    // On some systems, due to file caching, the time stamp for the updated
    // JSP file may actually be greater than that of the newly created byte
    // codes in the cache.  In such cases, adjust the cache time stamp to
    // JSP page time, to avoid unnecessary recompilations.
    ctxt.getRuntimeContext().adjustBytecodeTime(ctxt.getFullClassName(), jspModTime);
  }
Esempio n. 6
0
  /** Compile the jsp file into equivalent servlet in java source */
  private void generateJava() throws Exception {

    long t1, t2, t3, t4;
    t1 = t2 = t3 = t4 = 0;

    if (log.isLoggable(Level.FINE)) {
      t1 = System.currentTimeMillis();
    }

    // Setup page info area
    pageInfo =
        new PageInfo(new BeanRepository(ctxt.getClassLoader(), errDispatcher), ctxt.getJspFile());

    JspConfig jspConfig = options.getJspConfig();
    JspProperty jspProperty = jspConfig.findJspProperty(ctxt.getJspFile());

    /*
     * If the current uri is matched by a pattern specified in
     * a jsp-property-group in web.xml, initialize pageInfo with
     * those properties.
     */
    pageInfo.setELIgnored(JspUtil.booleanValue(jspProperty.isELIgnored()));
    pageInfo.setScriptingInvalid(JspUtil.booleanValue(jspProperty.isScriptingInvalid()));
    pageInfo.setTrimDirectiveWhitespaces(JspUtil.booleanValue(jspProperty.getTrimSpaces()));
    pageInfo.setDeferredSyntaxAllowedAsLiteral(JspUtil.booleanValue(jspProperty.getPoundAllowed()));
    pageInfo.setErrorOnUndeclaredNamespace(
        JspUtil.booleanValue(jspProperty.errorOnUndeclaredNamespace()));

    if (jspProperty.getIncludePrelude() != null) {
      pageInfo.setIncludePrelude(jspProperty.getIncludePrelude());
    }
    if (jspProperty.getIncludeCoda() != null) {
      pageInfo.setIncludeCoda(jspProperty.getIncludeCoda());
    }
    if (options.isDefaultBufferNone() && pageInfo.getBufferValue() == null) {
      // Set to unbuffered if not specified explicitly
      pageInfo.setBuffer(0);
    }

    String javaFileName = ctxt.getServletJavaFileName();
    ServletWriter writer = null;

    try {
      // Setup the ServletWriter
      Writer javaWriter =
          javaCompiler.getJavaWriter(javaFileName, ctxt.getOptions().getJavaEncoding());
      writer = new ServletWriter(new PrintWriter(javaWriter));
      ctxt.setWriter(writer);

      // Reset the temporary variable counter for the generator.
      JspUtil.resetTemporaryVariableName();

      // Parse the file
      ParserController parserCtl = new ParserController(ctxt, this);
      pageNodes = parserCtl.parse(ctxt.getJspFile());

      if (ctxt.isPrototypeMode()) {
        // generate prototype .java file for the tag file
        Generator.generate(writer, this, pageNodes);
        writer.close();
        writer = null;
        return;
      }

      // Validate and process attributes
      Validator.validate(this, pageNodes);

      if (log.isLoggable(Level.FINE)) {
        t2 = System.currentTimeMillis();
      }

      // Collect page info
      Collector.collect(this, pageNodes);

      // Compile (if necessary) and load the tag files referenced in
      // this compilation unit.
      tfp = new TagFileProcessor();
      tfp.loadTagFiles(this, pageNodes);

      if (log.isLoggable(Level.FINE)) {
        t3 = System.currentTimeMillis();
      }

      // Determine which custom tag needs to declare which scripting vars
      ScriptingVariabler.set(pageNodes, errDispatcher);

      // Optimizations by Tag Plugins
      TagPluginManager tagPluginManager = options.getTagPluginManager();
      tagPluginManager.apply(pageNodes, errDispatcher, pageInfo);

      // Optimization: concatenate contiguous template texts.
      TextOptimizer.concatenate(this, pageNodes);

      // Generate static function mapper codes.
      ELFunctionMapper.map(this, pageNodes);

      // generate servlet .java file
      Generator.generate(writer, this, pageNodes);
      writer.close();
      writer = null;

      // The writer is only used during the compile, dereference
      // it in the JspCompilationContext when done to allow it
      // to be GC'd and save memory.
      ctxt.setWriter(null);

      if (log.isLoggable(Level.FINE)) {
        t4 = System.currentTimeMillis();
        log.fine(
            "Generated "
                + javaFileName
                + " total="
                + (t4 - t1)
                + " generate="
                + (t4 - t3)
                + " validate="
                + (t2 - t1));
      }

    } catch (Exception e) {
      if (writer != null) {
        try {
          writer.close();
          writer = null;
        } catch (Exception e1) {
          // do nothing
        }
      }
      // Remove the generated .java file
      javaCompiler.doJavaFile(false);
      throw e;
    } finally {
      if (writer != null) {
        try {
          writer.close();
        } catch (Exception e2) {
          // do nothing
        }
      }
    }

    // JSR45 Support
    if (!options.isSmapSuppressed()) {
      smapUtil.generateSmap(pageNodes);
    }

    // If any proto type .java and .class files was generated,
    // the prototype .java may have been replaced by the current
    // compilation (if the tag file is self referencing), but the
    // .class file need to be removed, to make sure that javac would
    // generate .class again from the new .java file just generated.
    tfp.removeProtoTypeFiles(ctxt.getClassFileName());
  }
Esempio n. 7
0
  /**
   * Compile the jsp file into equivalent servlet in .java file
   *
   * @return a smap for the current JSP page, if one is generated, null otherwise
   */
  protected String[] generateJava() throws Exception {

    String[] smapStr = null;

    long t1, t2, t3, t4;

    t1 = t2 = t3 = t4 = 0;

    if (log.isDebugEnabled()) {
      t1 = System.currentTimeMillis();
    }

    // Setup page info area
    pageInfo =
        new PageInfo(new BeanRepository(ctxt.getClassLoader(), errDispatcher), ctxt.getJspFile());

    JspConfig jspConfig = options.getJspConfig();
    JspConfig.JspProperty jspProperty = jspConfig.findJspProperty(ctxt.getJspFile());

    /*
     * If the current uri is matched by a pattern specified in a
     * jsp-property-group in web.xml, initialize pageInfo with those
     * properties.
     */
    if (jspProperty.isELIgnored() != null) {
      pageInfo.setELIgnored(JspUtil.booleanValue(jspProperty.isELIgnored()));
    }
    if (jspProperty.isScriptingInvalid() != null) {
      pageInfo.setScriptingInvalid(JspUtil.booleanValue(jspProperty.isScriptingInvalid()));
    }
    if (jspProperty.getIncludePrelude() != null) {
      pageInfo.setIncludePrelude(jspProperty.getIncludePrelude());
    }
    if (jspProperty.getIncludeCoda() != null) {
      pageInfo.setIncludeCoda(jspProperty.getIncludeCoda());
    }
    if (jspProperty.isDeferedSyntaxAllowedAsLiteral() != null) {
      pageInfo.setDeferredSyntaxAllowedAsLiteral(
          JspUtil.booleanValue(jspProperty.isDeferedSyntaxAllowedAsLiteral()));
    }
    if (jspProperty.isTrimDirectiveWhitespaces() != null) {
      pageInfo.setTrimDirectiveWhitespaces(
          JspUtil.booleanValue(jspProperty.isTrimDirectiveWhitespaces()));
    }
    // Default ContentType processing is deferred until after the page has
    // been parsed
    if (jspProperty.getBuffer() != null) {
      pageInfo.setBufferValue(jspProperty.getBuffer(), null, errDispatcher);
    }
    if (jspProperty.isErrorOnUndeclaredNamespace() != null) {
      pageInfo.setErrorOnUndeclaredNamespace(
          JspUtil.booleanValue(jspProperty.isErrorOnUndeclaredNamespace()));
    }
    if (ctxt.isTagFile()) {
      try {
        double libraryVersion =
            Double.parseDouble(ctxt.getTagInfo().getTagLibrary().getRequiredVersion());
        if (libraryVersion < 2.0) {
          pageInfo.setIsELIgnored("true", null, errDispatcher, true);
        }
        if (libraryVersion < 2.1) {
          pageInfo.setDeferredSyntaxAllowedAsLiteral("true", null, errDispatcher, true);
        }
      } catch (NumberFormatException ex) {
        errDispatcher.jspError(ex);
      }
    }

    ctxt.checkOutputDir();
    String javaFileName = ctxt.getServletJavaFileName();

    ServletWriter writer = null;
    try {
      /*
       * The setting of isELIgnored changes the behaviour of the parser
       * in subtle ways. To add to the 'fun', isELIgnored can be set in
       * any file that forms part of the translation unit so setting it
       * in a file included towards the end of the translation unit can
       * change how the parser should have behaved when parsing content
       * up to the point where isELIgnored was set. Arghh!
       * Previous attempts to hack around this have only provided partial
       * solutions. We now use two passes to parse the translation unit.
       * The first just parses the directives and the second parses the
       * whole translation unit once we know how isELIgnored has been set.
       * TODO There are some possible optimisations of this process.
       */
      // Parse the file
      ParserController parserCtl = new ParserController(ctxt, this);

      // Pass 1 - the directives
      Node.Nodes directives = parserCtl.parseDirectives(ctxt.getJspFile());
      Validator.validateDirectives(this, directives);

      // Pass 2 - the whole translation unit
      pageNodes = parserCtl.parse(ctxt.getJspFile());

      // Leave this until now since it can only be set once - bug 49726
      if (pageInfo.getContentType() == null && jspProperty.getDefaultContentType() != null) {
        pageInfo.setContentType(jspProperty.getDefaultContentType());
      }

      if (ctxt.isPrototypeMode()) {
        // generate prototype .java file for the tag file
        writer = setupContextWriter(javaFileName);
        Generator.generate(writer, this, pageNodes);
        writer.close();
        writer = null;
        return null;
      }

      // Validate and process attributes - don't re-validate the
      // directives we validated in pass 1
      Validator.validateExDirectives(this, pageNodes);

      if (log.isDebugEnabled()) {
        t2 = System.currentTimeMillis();
      }

      // Collect page info
      Collector.collect(this, pageNodes);

      // Compile (if necessary) and load the tag files referenced in
      // this compilation unit.
      tfp = new TagFileProcessor();
      tfp.loadTagFiles(this, pageNodes);

      if (log.isDebugEnabled()) {
        t3 = System.currentTimeMillis();
      }

      // Determine which custom tag needs to declare which scripting vars
      ScriptingVariabler.set(pageNodes, errDispatcher);

      // Optimizations by Tag Plugins
      TagPluginManager tagPluginManager = options.getTagPluginManager();
      tagPluginManager.apply(pageNodes, errDispatcher, pageInfo);

      // Optimization: concatenate contiguous template texts.
      TextOptimizer.concatenate(this, pageNodes);

      // Generate static function mapper codes.
      ELFunctionMapper.map(pageNodes);

      // generate servlet .java file
      writer = setupContextWriter(javaFileName);
      Generator.generate(writer, this, pageNodes);
      writer.close();
      writer = null;

      // The writer is only used during the compile, dereference
      // it in the JspCompilationContext when done to allow it
      // to be GC'd and save memory.
      ctxt.setWriter(null);

      if (log.isDebugEnabled()) {
        t4 = System.currentTimeMillis();
        log.debug(
            "Generated "
                + javaFileName
                + " total="
                + (t4 - t1)
                + " generate="
                + (t4 - t3)
                + " validate="
                + (t2 - t1));
      }

    } catch (Exception e) {
      if (writer != null) {
        try {
          writer.close();
          writer = null;
        } catch (Exception e1) {
          // do nothing
        }
      }
      // Remove the generated .java file
      File file = new File(javaFileName);
      if (file.exists()) {
        if (!file.delete()) {
          log.warn(
              Localizer.getMessage(
                  "jsp.warning.compiler.javafile.delete.fail", file.getAbsolutePath()));
        }
      }
      throw e;
    } finally {
      if (writer != null) {
        try {
          writer.close();
        } catch (Exception e2) {
          // do nothing
        }
      }
    }

    // JSR45 Support
    if (!options.isSmapSuppressed()) {
      smapStr = SmapUtil.generateSmap(ctxt, pageNodes);
    }

    // If any proto type .java and .class files was generated,
    // the prototype .java may have been replaced by the current
    // compilation (if the tag file is self referencing), but the
    // .class file need to be removed, to make sure that javac would
    // generate .class again from the new .java file just generated.
    tfp.removeProtoTypeFiles(ctxt.getClassFileName());

    return smapStr;
  }
Esempio n. 8
0
  /**
   * Determine if a compilation is necessary by checking the time stamp of the JSP page with that of
   * the corresponding .class or .java file. If the page has dependencies, the check is also
   * extended to its dependents, and so on. This method can by overridden by a subclasses of
   * Compiler.
   *
   * @param checkClass If true, check against .class file, if false, check against .java file.
   */
  public boolean isOutDated(boolean checkClass) {

    String jsp = ctxt.getJspFile();

    if (jsw != null && (ctxt.getOptions().getModificationTestInterval() > 0)) {

      if (jsw.getLastModificationTest() + (ctxt.getOptions().getModificationTestInterval() * 1000)
          > System.currentTimeMillis()) {
        return false;
      }
      jsw.setLastModificationTest(System.currentTimeMillis());
    }

    long jspRealLastModified = 0;
    try {
      URL jspUrl = ctxt.getResource(jsp);
      if (jspUrl == null) {
        ctxt.incrementRemoved();
        return true;
      }
      URLConnection uc = jspUrl.openConnection();
      if (uc instanceof JarURLConnection) {
        jspRealLastModified = ((JarURLConnection) uc).getJarEntry().getTime();
      } else {
        jspRealLastModified = uc.getLastModified();
      }
      uc.getInputStream().close();
    } catch (Exception e) {
      if (log.isDebugEnabled()) log.debug("Problem accessing resource. Treat as outdated.", e);
      return true;
    }

    long targetLastModified = 0;
    File targetFile;

    if (checkClass) {
      targetFile = new File(ctxt.getClassFileName());
    } else {
      targetFile = new File(ctxt.getServletJavaFileName());
    }

    if (!targetFile.exists()) {
      return true;
    }

    targetLastModified = targetFile.lastModified();
    if (checkClass && jsw != null) {
      jsw.setServletClassLastModifiedTime(targetLastModified);
    }
    if (targetLastModified < jspRealLastModified) {
      if (log.isDebugEnabled()) {
        log.debug("Compiler: outdated: " + targetFile + " " + targetLastModified);
      }
      return true;
    }

    // determine if source dependent files (e.g. includes using include
    // directives) have been changed.
    if (jsw == null) {
      return false;
    }

    List<String> depends = jsw.getDependants();
    if (depends == null) {
      return false;
    }

    Iterator<String> it = depends.iterator();
    while (it.hasNext()) {
      String include = it.next();
      try {
        URL includeUrl = ctxt.getResource(include);
        if (includeUrl == null) {
          return true;
        }

        URLConnection iuc = includeUrl.openConnection();
        long includeLastModified = 0;
        if (iuc instanceof JarURLConnection) {
          includeLastModified = ((JarURLConnection) iuc).getJarEntry().getTime();
        } else {
          includeLastModified = iuc.getLastModified();
        }
        iuc.getInputStream().close();

        if (includeLastModified > targetLastModified) {
          return true;
        }
      } catch (Exception e) {
        if (log.isDebugEnabled()) log.debug("Problem accessing resource. Treat as outdated.", e);
        return true;
      }
    }

    return false;
  }