blob: e7a7f1376e494908b598f98d4485a0eba3796fa6 [file] [log] [blame]
/*
* 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.myfaces.extensions.scripting.loader;
import org.apache.myfaces.extensions.scripting.loader.dependencies.registry.DefaultDependencyRegistry;
import org.apache.myfaces.extensions.scripting.loader.dependencies.registry.DependencyRegistry;
import org.apache.myfaces.extensions.scripting.loader.dependencies.scanner.DependencyScanner;
import java.io.File;
/**
*
*/
public class DependencyAwareReloadingClassLoader extends ReloadingClassLoader {
/** The dependency scanner that determines dependencies for a class. */
private DependencyScanner scanner;
/** The dependency registry that keeps track of the dependencies determine by the scanner. */
private DependencyRegistry registry;
// ------------------------------------------ Constructors
/**
* <p>Constructs a new dependency aware reloading class loader for the specified
* compilation directory using the default delegation parent class loader. Note
* that this class loader will only delegate to the parent class loader if
* there's no dynamically compiled class available.</p>
*
* <p>The given dependency scanner will be used to determine dependencies for each
* class that this class loader has to load. If you then reload one of those
* dependencies, the dependent class will be reloaded automatically as well.</p>
*
* @param scanner the dependency scanner that will be used to determine dependencies
* @param compilationDirectory the compilation directory
*/
public DependencyAwareReloadingClassLoader(DependencyScanner scanner, File compilationDirectory) {
this(scanner, new DefaultDependencyRegistry(), compilationDirectory);
}
/**
* <p>Constructs a new reloading class loader for the specified compilation
* directory using the given delegation parent class loader. Note that this
* class loader will only delegate to the parent class loader if there's no
* dynamically compiled class available.</p>
*
* <p>The given dependency scanner will be used to determine dependencies for each
* class that this class loader has to load. If you then reload one of those
* dependencies, the dependent class will be reloaded automatically as well.</p>
*
* @param scanner the dependency scanner that will be used to determine dependencies
* @param parentClassLoader the parent class loader
* @param compilationDirectory the compilation directory
*/
public DependencyAwareReloadingClassLoader(DependencyScanner scanner, ClassLoader parentClassLoader, File compilationDirectory) {
this(scanner, new DefaultDependencyRegistry(), parentClassLoader, compilationDirectory);
}
public DependencyAwareReloadingClassLoader(DependencyScanner scanner, DependencyRegistry registry, File compilationDirectory) {
super(compilationDirectory);
if (scanner == null) {
throw new IllegalArgumentException("The given dependency scanner must not be null.");
}
this.scanner = scanner;
if (registry != null) {
this.registry = registry;
} else {
this.registry = new DefaultDependencyRegistry();
}
}
public DependencyAwareReloadingClassLoader(DependencyScanner scanner, DependencyRegistry registry,
ClassLoader parentClassLoader, File compilationDirectory) {
super(parentClassLoader, compilationDirectory);
if (scanner == null) {
throw new IllegalArgumentException("The given dependency scanner must not be null.");
}
this.scanner = scanner;
if (registry != null) {
this.registry = registry;
} else {
this.registry = new DefaultDependencyRegistry();
}
}
// ------------------------------------------ ReloadingClassLoader methods
/**
* <p>Reloads the given class internally explicitly. Note that this class loader usually
* reloads classes automatically, i.e. this class loader detects if there is a newer
* version of a class file available in the compilation directory. However, by using
* this method you tell this class loader to forcefully reload the given class. For
* example, if you've got a newer version of a dynamically recompiled class and a
* statically compiled class depending on this one, you can tell this class loader to
* reload the statically compiled class as well so that it references the correct
* version of the Class object.</p>
*
* <p>This method also automatically reloads all dependent classes of the given class.</p>
*
* @param className the class you want to reload
*/
@Override
public void reloadClass(String className) {
super.reloadClass(className);
// First of all, update the dependencies of this class
// as they could have changed due to the reload.
registry.unregisterDependencies(className);
scanner.scan(registry, this, className);
// Reload all dependent classes as well, in doing so we're automatically updating
// dependencies, even transitive ones.
for (String dependentClassName : registry.getDependentClasses(className)) {
reloadClass(dependentClassName);
}
}
}