All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.faces.config.ConfigureListener Maven / Gradle / Ivy

There is a newer version: 4.1.1
Show newest version
/*
 * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.faces.config;

import static com.sun.faces.RIConstants.ANNOTATED_CLASSES;
import static com.sun.faces.RIConstants.ERROR_PAGE_PRESENT_KEY_NAME;
import static com.sun.faces.RIConstants.FACES_SERVLET_MAPPINGS;
import static com.sun.faces.RIConstants.FACES_SERVLET_REGISTRATION;
import static com.sun.faces.config.InitFacesContext.getInitContextServletContextMap;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableLazyBeanValidation;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableThreading;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.EnableWebsocketEndpoint;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.ForceLoadFacesConfigFiles;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.VerifyFacesConfigObjects;
import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.JakartaFacesProjectStage;
import static com.sun.faces.push.WebsocketEndpoint.URI_TEMPLATE;
import static java.lang.Boolean.TRUE;
import static java.text.MessageFormat.format;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.FINEST;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.SEVERE;
import static java.util.logging.Level.WARNING;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.application.WebappLifecycleListener;
import com.sun.faces.el.ELContextImpl;
import com.sun.faces.push.WebsocketEndpoint;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.MojarraThreadFactory;
import com.sun.faces.util.ReflectionUtils;
import com.sun.faces.util.Timer;
import com.sun.faces.util.Util;

import jakarta.el.ELManager;
import jakarta.faces.FactoryFinder;
import jakarta.faces.application.Application;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.PreDestroyApplicationEvent;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletContextEvent;
import jakarta.servlet.ServletContextListener;
import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpSessionEvent;
import jakarta.servlet.http.HttpSessionListener;
import jakarta.websocket.server.ServerContainer;
import jakarta.websocket.server.ServerEndpointConfig;

/**
 * Parse all relevant Faces configuration resources, and configure the Mojarra runtime
 * environment.
 */
public class ConfigureListener implements ServletRequestListener, HttpSessionListener, ServletContextListener {

    private static final Logger LOGGER = FacesLogger.CONFIG.getLogger();

    private ScheduledThreadPoolExecutor webResourcePool;

    protected WebappLifecycleListener webAppListener;
    protected WebConfiguration webConfig;

    // ------------------------------------------ ServletContextListener Methods

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ServletContext servletContext = servletContextEvent.getServletContext();

        Timer timer = Timer.getInstance();
        if (timer != null) {
            timer.startTiming();
        }

        ConfigManager configManager = ConfigManager.getInstance(servletContext);
        if (configManager == null) {
            configManager = ConfigManager.createInstance(servletContext);
        }

        if (configManager.hasBeenInitialized(servletContext)) {
            return;
        }

        InitFacesContext initFacesContext = new InitFacesContext(servletContext);

        LOGGER.log(FINE, () -> format("ConfigureListener.contextInitialized({0})", servletContext.getContextPath()));

        webConfig = WebConfiguration.getInstance(servletContext);

        // Check to see if the FacesServlet is present in the
        // web.xml. If it is, perform faces configuration as normal,
        // otherwise, simply return.
        Object facesServletRegistration = servletContext.getAttribute(FACES_SERVLET_REGISTRATION); // If found by FacesInitializer.

        WebXmlProcessor webXmlProcessor = new WebXmlProcessor(servletContext);
        if (facesServletRegistration == null) {
            if (!webXmlProcessor.isFacesServletPresent()) {
                if (!webConfig.isOptionEnabled(ForceLoadFacesConfigFiles)) {
                    LOGGER.log(FINE, "No FacesServlet found in deployment descriptor - bypassing configuration");

                    WebConfiguration.clear(servletContext);
                    configManager.destroy(servletContext, initFacesContext);
                    ConfigManager.removeInstance(servletContext);
                    InitFacesContext.cleanupInitMaps(servletContext);

                    return;
                }
            } else {
                LOGGER.log(FINE, "FacesServlet found in deployment descriptor - processing configuration.");
            }
        }

        if (webXmlProcessor.isDistributablePresent()) {
            webConfig.setOptionEnabled(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable, true);
            servletContext.setAttribute(WebConfiguration.BooleanWebContextInitParameter.EnableDistributable.getQualifiedName(), TRUE);
        }

        // Bootstrap of faces required
        webAppListener = new WebappLifecycleListener(servletContext);
        webAppListener.contextInitialized(servletContextEvent);
        ReflectionUtils.initCache(Thread.currentThread().getContextClassLoader());
        Throwable caughtThrowable = null;

        try {
            if (LOGGER.isLoggable(INFO)) {
                LOGGER.log(INFO, "faces.config.listener.version", servletContext.getContextPath());
            }

            if (webConfig.isOptionEnabled(VerifyFacesConfigObjects)) {
                LOGGER.warning(
                    "JSF1059: WARNING!  The com.sun.faces.verifyObjects feature is to aid developers not using tools.  " +
                    "It shouldn't be enabled if using an IDE, or if this application is being deployed for production as it " +
                    "will impact application start times.");

                // If we're verifying, force bean validation to occur at startup as well
                webConfig.overrideContextInitParameter(EnableLazyBeanValidation, false);
                Verifier.setCurrentInstance(new Verifier());
            }

            configManager.initialize(servletContext, initFacesContext);

            if (shouldInitConfigMonitoring()) {
                initConfigMonitoring(servletContext);
            }

            // Step 7, verify that all the configured factories are available
            // and optionally that configured objects can be created.
            Verifier verifier = Verifier.getCurrentInstance();
            if (verifier != null && !verifier.isApplicationValid() && LOGGER.isLoggable(SEVERE)) {
                LOGGER.severe("faces.config.verifyobjects.failures_detected");
                StringBuilder sb = new StringBuilder(128);
                for (String msg : verifier.getMessages()) {
                    sb.append(msg).append('\n');
                }
                LOGGER.severe(sb.toString());
            }

            ApplicationAssociate associate = ApplicationAssociate.getInstance(servletContext);
            if (associate != null) {
                associate.setExpressionFactory(ELManager.getExpressionFactory());
            }

            initFacesContext.setELContext(new ELContextImpl(initFacesContext));

            if (associate != null) {
                associate.setContextName(servletContext.getContextPath());

                boolean isErrorPagePresent = webXmlProcessor.isErrorPagePresent();
                associate.setErrorPagePresent(isErrorPagePresent);
                servletContext.setAttribute(ERROR_PAGE_PRESENT_KEY_NAME, isErrorPagePresent);
            }

            // Register websocket endpoint if explicitly enabled.
            // Note: websocket channel filter is registered in FacesInitializer.
            if (webConfig.isOptionEnabled(EnableWebsocketEndpoint)) {
                ServerContainer serverContainer = (ServerContainer) servletContext.getAttribute(ServerContainer.class.getName());

                if (serverContainer == null) {
                    throw new UnsupportedOperationException(
                        "Cannot enable f:websocket." +
                        " The current websocket container implementation does not support programmatically registering a container-provided endpoint.");
                }

                serverContainer.addEndpoint(ServerEndpointConfig.Builder.create(WebsocketEndpoint.class, URI_TEMPLATE).build());
            }

            webConfig.doPostBringupActions();
            configManager.publishPostConfigEvent();

        } catch (Throwable t) {
            LOGGER.log(SEVERE, "Critical error during deployment: ", t);
            caughtThrowable = t;

        } finally {
            servletContextEvent.getServletContext().removeAttribute(ANNOTATED_CLASSES);
            servletContextEvent.getServletContext().removeAttribute(FACES_SERVLET_MAPPINGS);

            Verifier.setCurrentInstance(null);

            LOGGER.log(FINE, "faces.config.listener.version.complete");

            if (timer != null) {
                timer.stopTiming();
                timer.logResult("Initialization of context " + servletContext.getContextPath());
            }

            if (caughtThrowable != null) {
                throw new RuntimeException(caughtThrowable);
            }

            // Bug 20458755: The InitFacesContext was not being cleaned up, resulting in
            // a partially constructed FacesContext being made available
            // to other code that re-uses this Thread at init time.
            initFacesContext.releaseCurrentInstance();
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();

        ConfigManager configManager = ConfigManager.getInstance(context);

        // The additional check for a WebConfiguration instance was added at the request of JBoss
        if (configManager == null && WebConfiguration.getInstanceWithoutCreating(context) != null) {
            LOGGER.log(WARNING,
               "Unexpected state during contextDestroyed: no ConfigManager instance in current ServletContext but one is expected to exist.");
        }

        InitFacesContext initContext = null;
        try {
            initContext = getInitFacesContext(context);
            if (initContext == null) {
                initContext = new InitFacesContext(context);
            } else {
                InitFacesContext.getThreadInitContextMap().put(Thread.currentThread(), initContext);
            }

            if (webAppListener != null) {
                webAppListener.contextDestroyed(sce);
                webAppListener = null;
            }

            if (webResourcePool != null) {
                webResourcePool.shutdownNow();
            }

            if (LOGGER.isLoggable(FINE)) {
                LOGGER.log(FINE, "ConfigureListener.contextDestroyed({0})", context.getServletContextName());
            }

            if (configManager == null || !configManager.hasBeenInitialized(context)) {
                return;
            }

            ApplicationAssociate associate = ApplicationAssociate.getInstance(context);
            if (associate != null) {
                associate.setExpressionFactory(ELManager.getExpressionFactory());
            }

            initContext.setELContext(new ELContextImpl(initContext));
            Application application = initContext.getApplication();

            application.publishEvent(initContext, PreDestroyApplicationEvent.class, Application.class, application);

        } catch (Exception e) {
            LOGGER.log(SEVERE, "Unexpected exception when attempting to tear down the Mojarra runtime", e);
        } finally {
            ApplicationAssociate.clearInstance(context);
            ApplicationAssociate.setCurrentInstance(null);

            // Release the initialization mark on this web application
            if (configManager != null) {
                configManager.destroy(context, initContext);
                ConfigManager.removeInstance(context);
            } else if (WebConfiguration.getInstanceWithoutCreating(context) != null && LOGGER.isLoggable(WARNING)) {
                LOGGER.log(WARNING, "Unexpected state during contextDestroyed: no ConfigManager instance in current ServletContext but one is expected to exist.");
            }

            FactoryFinder.releaseFactories();
            ReflectionUtils.clearCache(Thread.currentThread().getContextClassLoader());
            WebConfiguration.clear(context);
            InitFacesContext.cleanupInitMaps(context);
        }

    }


    // ------------------------------------- Methods from ServletRequestListener

    @Override
    public void requestDestroyed(ServletRequestEvent event) {
        if (webAppListener != null) {
            webAppListener.requestDestroyed(event);
        }
    }

    @Override
    public void requestInitialized(ServletRequestEvent event) {
        if (webAppListener != null) {
            webAppListener.requestInitialized(event);
        }
    }


    // ----------------------------------------- Methods from HttpSessionListener

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        if (webAppListener != null) {
            webAppListener.sessionCreated(event);
        }
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        if (webAppListener != null) {
            webAppListener.sessionDestroyed(event);
        }
    }



    // --------------------------------------------------------- Private Methods

    private boolean shouldInitConfigMonitoring() {

        boolean development = isDevModeEnabled();
        boolean threadingOptionSpecified = webConfig.isSet(EnableThreading);

        if (development && !threadingOptionSpecified) {
            return true;
        }

        return development && threadingOptionSpecified && webConfig.isOptionEnabled(EnableThreading);
    }

    private void initConfigMonitoring(ServletContext context) {

        @SuppressWarnings("unchecked")
        Collection webURIs = (Collection) context.getAttribute("com.sun.faces.webresources");

        if (isDevModeEnabled() && webURIs != null && !webURIs.isEmpty()) {
            webResourcePool = new ScheduledThreadPoolExecutor(1, new MojarraThreadFactory("WebResourceMonitor"));
            webResourcePool.scheduleAtFixedRate(new WebConfigResourceMonitor(context, webURIs), 2000, 2000, TimeUnit.MILLISECONDS);
        }

        context.removeAttribute("com.sun.faces.webresources");
    }

    private boolean isDevModeEnabled() {
        // interrogate the init parameter directly vs looking up the application
        return "Development".equals(webConfig.getOptionValue(JakartaFacesProjectStage));
    }

    /**
     * This method will be invoked {@link WebConfigResourceMonitor} when changes to any of the faces-config.xml files
     * included in WEB-INF are modified.
     */
    private void reload(ServletContext servletContext) {
        LOGGER.log(INFO, () -> format("Reloading Faces configuration for context {0}", servletContext.getContextPath()));

        // tear down the application
        try {
            // this will only be true in the automated test usage scenario
            if (webAppListener != null) {
                List sessions = webAppListener.getActiveSessions();
                if (sessions != null) {
                    for (HttpSession session : sessions) {
                        if (LOGGER.isLoggable(INFO)) {
                            LOGGER.log(INFO, "Invalidating Session {0}", session.getId());
                        }
                        session.invalidate();
                    }
                }
            }

            // Release any allocated application resources
            FactoryFinder.releaseFactories();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FacesContext initContext = new InitFacesContext(servletContext);
            ApplicationAssociate.clearInstance(initContext.getExternalContext());
            ApplicationAssociate.setCurrentInstance(null);

            // Release the initialization mark on this web application
            ConfigManager configManager = ConfigManager.getInstance(servletContext);

            if (configManager != null) {
                configManager.destroy(servletContext, initContext);
                ConfigManager.removeInstance(servletContext);
            } else {
                LOGGER.log(SEVERE, "Unexpected state during reload: no ConfigManager instance in current ServletContext but one is expected to exist.");
            }

            initContext.release();
            ReflectionUtils.clearCache(Thread.currentThread().getContextClassLoader());
            WebConfiguration.clear(servletContext);
        }

        // Bring the application back up.
        // No verification will be performed either to make this light weight.

        // init a new WebAppLifecycleListener so that the cached ApplicationAssociate
        // is removed.
        webAppListener = new WebappLifecycleListener(servletContext);

        InitFacesContext initContext = new InitFacesContext(servletContext);
        ReflectionUtils.initCache(Thread.currentThread().getContextClassLoader());

        try {
            ConfigManager configManager = ConfigManager.createInstance(servletContext);
            if (configManager != null) {
                configManager.initialize(servletContext, initContext);
            } else {
                LOGGER.log(SEVERE, "Unexpected state during reload: no ConfigManager instance in current ServletContext but one is expected to exist.");
            }

            ApplicationAssociate associate = ApplicationAssociate.getInstance(servletContext);

            if (associate != null) {
                Boolean errorPagePresent = (Boolean) servletContext.getAttribute(ERROR_PAGE_PRESENT_KEY_NAME);
                if (errorPagePresent != null) {
                    associate.setErrorPagePresent(errorPagePresent);
                    associate.setContextName(servletContext.getContextPath());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            initContext.release();
        }

        LOGGER.log(INFO, () -> format("Reload complete. ({0})", servletContext.getContextPath()));
    }

    private InitFacesContext getInitFacesContext(ServletContext context) {
        for (Entry entry : getInitContextServletContextMap().entrySet()) {
            if (context == entry.getValue()) {
                return entry.getKey();
            }
        }

        return null;
    }


    // ----------------------------------------------------------- Inner classes

    /**
     * 

* Processes a web application's deployment descriptor looking for a reference to * jakarta.faces.webapp.FacesServlet. *

*/ private static class WebXmlProcessor { private static final String WEB_XML_PATH = "/WEB-INF/web.xml"; private static final String WEB_FRAGMENT_PATH = "META-INF/web-fragment.xml"; private boolean facesServletPresent; private boolean errorPagePresent; private boolean distributablePresent; /** *

* When instantiated, the web.xml of the current application will be scanned looking for a references to the * FacesServlet. isFacesServletPresent() will return the appropriate value based on the scan. *

* * @param context the ServletContext for the application of interest */ WebXmlProcessor(ServletContext context) { if (context != null) { scanForFacesServlet(context); } } /** * @return true if the WebXmlProcessor detected a FacesServlet entry, otherwise * return false. *

*/ boolean isFacesServletPresent() { return facesServletPresent; } /** * @return true if WEB-INF/web.xml contains a <error-page> element. */ boolean isErrorPagePresent() { return errorPagePresent; } /* * return true if is present in the web.xml or a fragment. * */ public boolean isDistributablePresent() { return distributablePresent; } /** *

* Parse the web.xml for the current application and scan for a FacesServlet entry, if found, set the * facesServletPresent property to true. * * @param context the ServletContext instance for this application */ private void scanForFacesServlet(ServletContext context) { InputStream in = context.getResourceAsStream(WEB_XML_PATH); SAXParserFactory factory = getConfiguredFactory(); if (in != null) { try { SAXParser parser = factory.newSAXParser(); parser.parse(in, new WebXmlHandler()); } catch (ParserConfigurationException | SAXException | IOException e) { warnProcessingError(e, context); facesServletPresent = true; return; } finally { if (in != null) { try { in.close(); } catch (Exception ioe) { LOGGER.log(FINEST, "Closing stream", ioe); } } } } if (!facesServletPresent && context.getMajorVersion() >= 3) { ClassLoader cl = Util.getCurrentLoader(this); Enumeration urls; try { urls = cl.getResources(WEB_FRAGMENT_PATH); } catch (IOException ioe) { throw new ConfigurationException(ioe); } if (urls != null) { while (urls.hasMoreElements() && !facesServletPresent) { InputStream fragmentStream = null; try { URL url = urls.nextElement(); URLConnection conn = url.openConnection(); conn.setUseCaches(false); fragmentStream = conn.getInputStream(); factory.newSAXParser().parse(fragmentStream, new WebXmlHandler()); } catch (IOException | ParserConfigurationException | SAXException e) { warnProcessingError(e, context); facesServletPresent = true; return; } finally { if (fragmentStream != null) { try { fragmentStream.close(); } catch (IOException ioe) { LOGGER.log(WARNING, "Exception whil scanning for FacesServlet", ioe); } } } } } } } // END scanForFacesServlet /** *

* Return a SAXParserFactory instance that is non-validating and is namespace aware. *

* * @return configured SAXParserFactory */ private SAXParserFactory getConfiguredFactory() { SAXParserFactory factory = Util.createSAXParserFactory(); factory.setValidating(false); factory.setNamespaceAware(true); return factory; } private void warnProcessingError(Exception e, ServletContext servletContext) { LOGGER.log(WARNING,format("JSF1078: Unable to process deployment descriptor for context ({0}).", servletContext.getContextPath() ), e); } /** *

* A simple SAX handler to process the elements of interested within a web application's deployment descriptor. *

*/ private class WebXmlHandler extends DefaultHandler { private static final String ERROR_PAGE = "error-page"; private static final String SERVLET_CLASS = "servlet-class"; private static final String FACES_SERVLET = "jakarta.faces.webapp.FacesServlet"; private boolean servletClassFound; private StringBuffer content; @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException { return new InputSource(new StringReader("")); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (!errorPagePresent && ERROR_PAGE.equals(localName)) { errorPagePresent = true; return; } if (!facesServletPresent) { if (SERVLET_CLASS.equals(localName)) { servletClassFound = true; content = new StringBuffer(); } else { servletClassFound = false; } } if ("distributable".equals(localName)) { distributablePresent = true; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (servletClassFound && !facesServletPresent) { content.append(ch, start, length); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (servletClassFound && !facesServletPresent && FACES_SERVLET.equals(content.toString().trim())) { facesServletPresent = true; } } } // END WebXmlHandler } // END WebXmlProcessor private class WebConfigResourceMonitor implements Runnable { private List monitors; private ServletContext servletContext; // -------------------------------------------------------- Constructors public WebConfigResourceMonitor(ServletContext servletContext, Collection uris) { this.servletContext = servletContext; for (URI uri : uris) { if (monitors == null) { monitors = new ArrayList<>(uris.size()); } try { monitors.add(new Monitor(uri)); } catch (IOException ioe) { LOGGER.log(SEVERE, () -> "Unable to setup resource monitor for " + uri.toString() + ". Resource will not be monitored for changes."); LOGGER.log(FINE, ioe, () -> ioe.toString()); } } } // ----------------------------------------------- Methods from Runnable /** * PENDING javadocs */ @Override public void run() { boolean reloaded = false; for (Iterator i = monitors.iterator(); i.hasNext();) { Monitor m = i.next(); try { if (m.hasBeenModified()) { if (!reloaded) { reloaded = true; } } } catch (IOException ioe) { if (LOGGER.isLoggable(SEVERE)) { LOGGER.severe("Unable to access url " + m.uri.toString() + ". Monitoring for this resource will no longer occur."); } if (LOGGER.isLoggable(FINE)) { LOGGER.log(FINE, ioe.toString(), ioe); } i.remove(); } } if (reloaded) { reload(servletContext); } } // ------------------------------------------------------- Inner Classes private class Monitor { private URI uri; private long timestamp = -1; // ---------------------------------------------------- Constructors Monitor(URI uri) throws IOException { this.uri = uri; timestamp = getLastModified(); if (LOGGER.isLoggable(INFO)) { LOGGER.log(INFO, "Monitoring {0} for modifications", uri.toURL().toExternalForm()); } } // ----------------------------------------- Package Private Methods boolean hasBeenModified() throws IOException { long temp = getLastModified(); if (timestamp < temp) { timestamp = temp; if (LOGGER.isLoggable(INFO)) { LOGGER.log(INFO, "{0} changed!", uri.toURL().toExternalForm()); } return true; } return false; } // ------------------------------------------------- Private Methods private long getLastModified() throws IOException { InputStream in = null; try { URLConnection connection = uri.toURL().openConnection(); connection.connect(); in = connection.getInputStream(); return connection.getLastModified(); } finally { if (in != null) { try { in.close(); } catch (IOException ignored) { LOGGER.log(FINEST, "Exception while closing stream", ignored); } } } } } // END Monitor } // END WebConfigResourceMonitor }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy