| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.wicket.protocol.http; |
| |
| import java.net.URL; |
| |
| import jakarta.servlet.FilterConfig; |
| import jakarta.servlet.ServletException; |
| |
| import org.apache.wicket.Session; |
| import org.apache.wicket.application.ReloadingClassLoader; |
| import org.apache.wicket.util.listener.IChangeListener; |
| |
| |
| /** |
| * Custom {@link WicketFilter} that reloads the web applications when classes are modified. In order |
| * to monitor changes to your own classes, subclass {@link ReloadingWicketFilter} and use include |
| * and exclude patterns using wildcards. And in web.xml, point to your custom |
| * {@link ReloadingWicketFilter} instead of the original {@link WicketFilter}. |
| * |
| * <p> |
| * The built-in patterns are: |
| * </p> |
| * |
| * <pre> |
| * ReloadingClassLoader.excludePattern("org.apache.wicket.*"); |
| * ReloadingClassLoader.includePattern("org.apache.wicket.examples.*"); |
| * </pre> |
| * |
| * <p> |
| * <b>Example. </b> Defining custom patterns |
| * </p> |
| * |
| * <pre> |
| * public class MyReloadingFilter extends ReloadingWicketFilter |
| * { |
| * static |
| * { |
| * ReloadingClassLoader.includePattern("com.company.*"); |
| * ReloadingClassLoader.excludePattern("com.company.spring.beans.*"); |
| * ReloadingClassLoader.includePattern("some.other.package.with.wicket.components.*"); |
| * } |
| * } |
| * </pre> |
| * |
| * <p> |
| * <b>NOTE. </b> If you wish to reload <tt>com.company.search.Form</tt>, you have to make sure to |
| * include all classes that <b>reference</b> <tt>com.company.search.Form</tt>. In particular, if it |
| * is referenced in com.company.Application, you will also have to include the latter. And this is |
| * viral, as for every class you include, you must check that all classes that reference it are also |
| * included. |
| * </p> |
| * |
| * <p> |
| * It is also possible to add an extra URL to watch for changes using |
| * <tt>ReloadingClassLoader.addLocation()</tt> . By default, all the URLs returned by the parent |
| * class loader (ie all {@link URL} returned by {@link ClassLoader#getResources(String)} with empty |
| * {@link String}) are registered. |
| * </p> |
| * <hr> |
| * <p> |
| * <b>Important. </b> It can be quite tricky to setup the reloading mechanism correctly. Here are |
| * the general guidelines: |
| * </p> |
| * <ul> |
| * <li>The order of include and exclude patterns is significant, the patterns are processed |
| * sequentially in the order they are defined</li> |
| * <li>Don't forget that inner classes are named after YourClass$1, so take that into account when |
| * setting up the patterns, eg include <tt>YourClass*</tt>, not just <tt>YourClass</tt></li> |
| * <li>To enable back-button support for the reloading mechanism, be sure to put |
| * <tt>Objects.setObjectStreamFactory(new WicketObjectStreamFactory());</tt> in your application's |
| * {@link WebApplication#init()} method. <b>Native JDK object serialization will break the reloading |
| * mechanism when navigating in the browser's history.</b></li> |
| * <li>It is advisable to <b>exclude</b> subclasses of {@link Session} from the the reloading |
| * classloader, because this is the only object that remains across application restarts</li> |
| * <li>Last but not least, make sure to clear your session cookie before reloading the application |
| * home page (or any other bookmarkable page) to get rid of old pages stored in session</li> |
| * </ul> |
| * |
| * <p> |
| * <b>Be sure to carefully read the following information if you also use Spring:</b> |
| * </p> |
| * |
| * <p> |
| * When using org.apache.wicket.spring.SpringWebApplicationFactory, the application must be a bean |
| * with "prototype" scope and the "applicationBean" init parameter must be explicitly set, otherwise |
| * the reloading mechanism won't be able to recreate the application. |
| * </p> |
| * |
| * <p> |
| * <b>WARNING. </b> Be careful that when using Spring or other component managers, you will get |
| * <tt>ClassCastException</tt> if a given class is loaded two times, one time by the normal |
| * classloader, and another time by the reloading classloader. You need to ensure that your Spring |
| * beans are properly excluded from the reloading class loader and that only the Wicket components |
| * are included. When getting a cryptic error with regard to class loading, class instantiation or |
| * class comparison, first <b>disable the reloading class loader</b> to rule out the possibility of |
| * a classloader conflict. Please keep in mind that two classes are equal if and only if they have |
| * the same name <b>and are loaded in the same classloader</b>. Same goes for errors like |
| * <tt>LinkageError: Class FooBar violates loader constraints</tt>, better be safe and disable the |
| * reloading feature. |
| * </p> |
| * |
| * @see WicketFilter |
| * |
| * @author <a href="mailto:jbq@apache.org">Jean-Baptiste Quenot</a> |
| */ |
| public class ReloadingWicketFilter extends WicketFilter |
| { |
| private ReloadingClassLoader reloadingClassLoader; |
| |
| /** |
| * Instantiate the reloading class loader |
| */ |
| public ReloadingWicketFilter() |
| { |
| // Create a reloading classloader |
| reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader()); |
| } |
| |
| /** |
| * @see org.apache.wicket.protocol.http.WicketFilter#getClassLoader() |
| */ |
| @Override |
| protected ClassLoader getClassLoader() |
| { |
| return reloadingClassLoader; |
| } |
| |
| /** |
| * @see org.apache.wicket.protocol.http.WicketFilter#init(boolean, jakarta.servlet.FilterConfig) |
| */ |
| @Override |
| public void init(final boolean isServlet, final FilterConfig filterConfig) |
| throws ServletException |
| { |
| reloadingClassLoader.setListener(new IChangeListener<Class<?>>() |
| { |
| @Override |
| public void onChange(Class<?> clz) |
| { |
| destroy(); |
| |
| // Remove the ModificationWatcher from the current reloading class loader |
| reloadingClassLoader.destroy(); |
| |
| /* |
| * Create a new classloader, as there is no way to clear a ClassLoader's cache. This |
| * supposes that we don't share objects across application instances, this is almost |
| * true, except for Wicket's Session object. |
| */ |
| reloadingClassLoader = new ReloadingClassLoader(getClass().getClassLoader()); |
| try |
| { |
| init(filterConfig); |
| } |
| catch (ServletException e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| |
| super.init(isServlet, filterConfig); |
| } |
| } |