Author: musachy
Date: Sun Dec 21 10:30:18 2008
New Revision: 728469
URL: http://svn.apache.org/viewvc?rev=728469&view=rev
Log:
WW-2931 Make convention plugin reload the configuration when a class that
contains actions changes in the file system
Added:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathPackageProvider.java
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/FileResourceStore.java
(with props)
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ReloadingClassLoader.java
(with props)
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStore.java
(with props)
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStoreClassLoader.java
(with props)
Modified:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ActionConfigBuilder.java
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathConfigurationProvider.java
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
struts/struts2/trunk/plugins/convention/src/main/resources/struts-plugin.xml
Modified:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ActionConfigBuilder.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ActionConfigBuilder.java?rev=728469&r1=728468&r2=728469&view=diff
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ActionConfigBuilder.java
(original)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ActionConfigBuilder.java
Sun Dec 21 10:30:18 2008
@@ -35,4 +35,8 @@
* via XWork dependency injetion.
*/
void buildActionConfigs();
+
+ boolean needsReload();
+
+ void destroy();
}
\ No newline at end of file
Modified:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathConfigurationProvider.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathConfigurationProvider.java?rev=728469&r1=728468&r2=728469&view=diff
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathConfigurationProvider.java
(original)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathConfigurationProvider.java
Sun Dec 21 10:30:18 2008
@@ -26,56 +26,83 @@
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.location.LocatableProperties;
+import org.apache.struts2.dispatcher.DispatcherListener;
+import org.apache.struts2.dispatcher.Dispatcher;
+import org.apache.struts2.StrutsConstants;
/**
* <p>
- * This class is a configuration provider for the XWork configuration
- * system. This is really the only way to truly handle loading of the
- * packages, actions and results correctly. This doesn't contain any
- * logic and instead delegates to the configured instance of the
- * {...@link ActionConfigBuilder} interface.
+ * Xwork will only reload configurations, if one ContainerProvider needs
reloading, that's all this class does
* </p>
*/
-public class ClasspathConfigurationProvider implements ConfigurationProvider {
+public class ClasspathConfigurationProvider implements ConfigurationProvider,
DispatcherListener {
private ActionConfigBuilder actionConfigBuilder;
+ private boolean devMode;
+ private boolean reload;
+ private boolean listeningToDispatcher;
@Inject
public ClasspathConfigurationProvider(ActionConfigBuilder
actionConfigBuilder) {
this.actionConfigBuilder = actionConfigBuilder;
}
+ @Inject(StrutsConstants.STRUTS_DEVMODE)
+ public void setDevMode(String mode) {
+ this.devMode = "true".equals(mode);
+ }
+
+ @Inject("struts.convention.classes.reload")
+ public void setReload(String reload) {
+ this.reload = "true".equals(reload);
+ }
+
/**
* Not used.
*/
public void destroy() {
+ if (this.listeningToDispatcher)
+ Dispatcher.removeDispatcherListener(this);
+ actionConfigBuilder.destroy();
}
/**
* Not used.
*/
public void init(Configuration configuration) {
+ if (devMode && reload && !listeningToDispatcher) {
+ //this is the only way I found to be able to get added to to
ConfigurationProvider list
+ //listening to events in Dispatcher
+ listeningToDispatcher = true;
+ Dispatcher.addDispatcherListener(this);
+ }
}
/**
* Does nothing.
*/
public void register(ContainerBuilder containerBuilder,
LocatableProperties locatableProperties)
- throws ConfigurationException {
+ throws ConfigurationException {
}
/**
* Loads the packages using the {...@link ActionConfigBuilder}.
*
- * @throws ConfigurationException
+ * @throws ConfigurationException
*/
public void loadPackages() throws ConfigurationException {
- actionConfigBuilder.buildActionConfigs();
}
/**
- * @return Always false.
+ * @return Always false.
*/
public boolean needsReload() {
- return false;
+ return devMode && reload ? actionConfigBuilder.needsReload() : false;
+ }
+
+ public void dispatcherInitialized(Dispatcher du) {
+ du.getConfigurationManager().addContainerProvider(this);
+ }
+
+ public void dispatcherDestroyed(Dispatcher du) {
}
}
\ No newline at end of file
Added:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathPackageProvider.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathPackageProvider.java?rev=728469&view=auto
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathPackageProvider.java
(added)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/ClasspathPackageProvider.java
Sun Dec 21 10:30:18 2008
@@ -0,0 +1,54 @@
+/*
+ * $Id: ClasspathConfigurationProvider.java 655902 2008-05-13 15:15:12Z
bpontarelli $
+ *
+ * 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.struts2.convention;
+
+import com.opensymphony.xwork2.config.PackageProvider;
+import com.opensymphony.xwork2.config.Configuration;
+import com.opensymphony.xwork2.config.ConfigurationException;
+import com.opensymphony.xwork2.inject.Inject;
+
+/**
+ * <p>
+ * This class is a configuration provider for the XWork configuration
+ * system. This is really the only way to truly handle loading of the
+ * packages, actions and results correctly. This doesn't contain any
+ * logic and instead delegates to the configured instance of the
+ * {...@link ActionConfigBuilder} interface.
+ * </p>
+ */
+public class ClasspathPackageProvider implements PackageProvider {
+ private ActionConfigBuilder actionConfigBuilder;
+
+ @Inject
+ public ClasspathPackageProvider(ActionConfigBuilder actionConfigBuilder) {
+ this.actionConfigBuilder = actionConfigBuilder;
+ }
+
+ public void init(Configuration configuration) throws
ConfigurationException {
+ }
+
+ public boolean needsReload() {
+ return actionConfigBuilder.needsReload();
+ }
+
+ public void loadPackages() throws ConfigurationException {
+ actionConfigBuilder.buildActionConfigs();
+ }
+}
Modified:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java?rev=728469&r1=728468&r2=728469&view=diff
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
(original)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/PackageBasedActionConfigBuilder.java
Sun Dec 21 10:30:18 2008
@@ -31,6 +31,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.net.URL;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
@@ -41,6 +42,8 @@
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.Namespaces;
import org.apache.struts2.convention.annotation.ParentPackage;
+import org.apache.struts2.convention.classloader.ReloadingClassLoader;
+import org.apache.struts2.StrutsConstants;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.config.Configuration;
@@ -56,6 +59,7 @@
import com.opensymphony.xwork2.util.finder.UrlSet;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.FileManager;
/**
* <p>
@@ -82,30 +86,34 @@
private String actionSuffix = "Action";
private boolean checkImplementsAction = true;
private boolean mapAllMatches = false;
+ private Set<String> loadedFileUrls = new HashSet<String>();
+ private boolean devMode;
+ private ReloadingClassLoader reloadingClassLoader;
+ private boolean reload;
/**
* Constructs actions based on a list of packages.
*
- * @param configuration The XWork configuration that the new package
configs and action configs
- * are added to.
- * @param actionNameBuilder The action name builder used to convert
action class names to action
- * names.
- * @param resultMapBuilder The result map builder used to create
ResultConfig mappings for each
- * action.
- * @param interceptorMapBuilder The interceptor map builder used to
create InterceptorConfig mappings for each
- * action.
- * @param objectFactory The ObjectFactory used to create the actions and
such.
- * @param redirectToSlash A boolean parameter that controls whether or
not this will create an
- * action for indexes. If this is set to true, index actions are
not created because
- * the unknown handler will redirect from /foo to /foo/. The only
action that is created
- * is to the empty action in the namespace (e.g. the namespace
/foo and the action "").
- * @param defaultParentPackage The default parent package for all the
configuration.
+ * @param configuration The XWork configuration that the new
package configs and action configs
+ * are added to.
+ * @param actionNameBuilder The action name builder used to convert
action class names to action
+ * names.
+ * @param resultMapBuilder The result map builder used to create
ResultConfig mappings for each
+ * action.
+ * @param interceptorMapBuilder The interceptor map builder used to create
InterceptorConfig mappings for each
+ * action.
+ * @param objectFactory The ObjectFactory used to create the
actions and such.
+ * @param redirectToSlash A boolean parameter that controls whether
or not this will create an
+ * action for indexes. If this is set to
true, index actions are not created because
+ * the unknown handler will redirect from
/foo to /foo/. The only action that is created
+ * is to the empty action in the namespace
(e.g. the namespace /foo and the action "").
+ * @param defaultParentPackage The default parent package for all the
configuration.
*/
@Inject
public PackageBasedActionConfigBuilder(Configuration configuration,
ActionNameBuilder actionNameBuilder,
- ResultMapBuilder resultMapBuilder, InterceptorMapBuilder
interceptorMapBuilder, ObjectFactory objectFactory,
- @Inject("struts.convention.redirect.to.slash") String
redirectToSlash,
- @Inject("struts.convention.default.parent.package") String
defaultParentPackage) {
+ ResultMapBuilder resultMapBuilder,
InterceptorMapBuilder interceptorMapBuilder, ObjectFactory objectFactory,
+
@Inject("struts.convention.redirect.to.slash") String redirectToSlash,
+
@Inject("struts.convention.default.parent.package") String
defaultParentPackage) {
// Validate that the parameters are okay
this.configuration = configuration;
@@ -122,6 +130,16 @@
this.defaultParentPackage = defaultParentPackage;
}
+ @Inject(StrutsConstants.STRUTS_DEVMODE)
+ public void setDevMode(String mode) {
+ this.devMode = "true".equals(mode);
+ }
+
+ @Inject("struts.convention.classes.reload")
+ public void setReload(String reload) {
+ this.reload = "true".equals(reload);
+ }
+
/**
* @param disableActionScanning Disable scanning for actions
*/
@@ -136,7 +154,7 @@
*/
@Inject(value = "struts.convention.action.excludeJars", required = false)
public void setExcludeJars(String excludeJars) {
- this.excludeJars = excludeJars.split("\\s*[,]\\s*");;
+ this.excludeJars = excludeJars.split("\\s*[,]\\s*");
}
/**
@@ -156,8 +174,8 @@
}
/**
- * @param actionPackages (Optional) An optional list of action packages
that this should create
- * configuration for.
+ * @param actionPackages (Optional) An optional list of action packages
that this should create
+ * configuration for.
*/
@Inject(value = "struts.convention.action.packages", required = false)
public void setActionPackages(String actionPackages) {
@@ -167,8 +185,8 @@
}
/**
- * @param actionPackages (Optional) Map classes that implement
com.opensymphony.xwork2.Action
- * as actions
+ * @param actionPackages (Optional) Map classes that implement
com.opensymphony.xwork2.Action
+ * as actions
*/
@Inject(value = "struts.convention.action.checkImplementsAction", required
= false)
public void setCheckImplementsAction(String checkImplementsAction) {
@@ -176,8 +194,8 @@
}
/**
- * @param actionSuffix (Optional) Classes that end with these value will
be mapped as actions
- * (defaults to "Action")
+ * @param actionSuffix (Optional) Classes that end with these value will
be mapped as actions
+ * (defaults to "Action")
*/
@Inject(value = "struts.convention.action.suffix", required = false)
public void setActionSuffix(String actionSuffix) {
@@ -187,8 +205,8 @@
}
/**
- * @param excludePackages (Optional) A list of packages that should be
skipped when building
- * configuration.
+ * @param excludePackages (Optional) A list of packages that should be
skipped when building
+ * configuration.
*/
@Inject(value = "struts.convention.exclude.packages", required = false)
public void setExcludePackages(String excludePackages) {
@@ -198,7 +216,7 @@
}
/**
- * @param packageLocators (Optional) A list of names used to find action
packages.
+ * @param packageLocators (Optional) A list of names used to find action
packages.
*/
@Inject(value = "struts.convention.package.locators", required = false)
public void setPackageLocators(String packageLocators) {
@@ -206,24 +224,29 @@
}
/**
- * @param packageLocatorsBasePackage (Optional) If set, only packages
that start with this
- * name will be scanned for actions.
+ * @param packageLocatorsBasePackage (Optional) If set, only packages that
start with this
+ * name will be scanned for actions.
*/
@Inject(value = "struts.convention.package.locators.basePackage", required
= false)
public void setPackageLocatorsBase(String packageLocatorsBasePackage) {
this.packageLocatorsBasePackage = packageLocatorsBasePackage;
- }
+ }
/**
- * @param mapAllMatches (Optional) Map actions that match the
"*${Suffix}" pattern
- * even if they don't have a default method. The
mapping from
- * the url to the action will be delegated the
action mapper.
+ * @param mapAllMatches (Optional) Map actions that match the "*${Suffix}"
pattern
+ * even if they don't have a default method. The
mapping from
+ * the url to the action will be delegated the action
mapper.
*/
@Inject(value = "struts.convention.action.mapAllMatches", required = false)
public void setMapAllMatches(String mapAllMatches) {
- this.mapAllMatches = "true".equals(mapAllMatches);
+ this.mapAllMatches = "true".equals(mapAllMatches);
}
+ protected void initReloadClassLoader() {
+ //when the configuration is reloaded, a new classloader will be setup
+ if (isReloadEnabled() && reloadingClassLoader == null)
+ reloadingClassLoader = new ReloadingClassLoader(getClassLoader());
+ }
/**
* Builds the action configurations by loading all classes in the packages
specified by the
* property <b>struts.convention.action.packages</b> and then figuring out
which classes implement Action
@@ -235,11 +258,14 @@
* {...@link ResultMapBuilder} is used to create ResultConfig instances of
the action.
*/
public void buildActionConfigs() {
- if (!disableActionScanning ) {
+ //setup reload class loader based on dev settings
+ initReloadClassLoader();
+
+ if (!disableActionScanning) {
if (actionPackages == null && packageLocators == null) {
throw new ConfigurationException("At least a list of action
packages or action package locators " +
- "must be given using one of the properties
[struts.convention.action.packages] or " +
- "[struts.convention.package.locators]");
+ "must be given using one of the properties
[struts.convention.action.packages] or " +
+ "[struts.convention.package.locators]");
}
if (LOG.isTraceEnabled()) {
@@ -260,12 +286,20 @@
}
}
+ protected ClassLoader getClassLoaderForFinder() {
+ return isReloadEnabled() ? this.reloadingClassLoader :
getClassLoader();
+ }
+
+ protected boolean isReloadEnabled() {
+ return devMode && reload;
+ }
+
@SuppressWarnings("unchecked")
protected Set<Class> findActions() {
Set<Class> classes = new HashSet<Class>();
try {
if (actionPackages != null || (packageLocators != null &&
!disablePackageLocatorsScanning)) {
- ClassFinder finder = new ClassFinder(getClassLoader(),
buildUrlSet().getUrls(), true);
+ ClassFinder finder = new
ClassFinder(getClassLoaderForFinder(), buildUrlSet().getUrls(), true);
// named packages
if (actionPackages != null) {
@@ -326,7 +360,7 @@
boolean nameMatches =
classInfo.getName().endsWith(actionSuffix);
try {
- return inPackage && (nameMatches ||
(checkImplementsAction &&
com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));
+ return inPackage && (nameMatches || (checkImplementsAction
&& com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));
} catch (ClassNotFoundException ex) {
if (LOG.isErrorEnabled())
LOG.error("Unable to load class [#0]", ex,
classInfo.getName());
@@ -347,7 +381,7 @@
boolean nameMatches =
classInfo.getName().endsWith(actionSuffix);
try {
- return packageMatches && (nameMatches ||
(checkImplementsAction &&
com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));
+ return packageMatches && (nameMatches ||
(checkImplementsAction &&
com.opensymphony.xwork2.Action.class.isAssignableFrom(classInfo.get())));
} catch (ClassNotFoundException ex) {
if (LOG.isErrorEnabled())
LOG.error("Unable to load class [#0]", ex,
classInfo.getName());
@@ -391,7 +425,7 @@
String defaultActionName = determineActionName(actionClass);
String defaultActionMethod = "execute";
PackageConfig.Builder defaultPackageConfig =
getPackageConfig(packageConfigs, namespace,
- actionPackage, actionClass, null);
+ actionPackage, actionClass, null);
// Verify that the annotations have no errors and also
determine if the default action
// configuration should still be built or not.
@@ -407,8 +441,8 @@
String actionName =
action.value().equals(Action.DEFAULT_VALUE) ? defaultActionName :
action.value();
if (actionNames.contains(actionName)) {
throw new ConfigurationException("The action
class [" + actionClass +
- "] contains two methods with an action
name annotation whose value " +
- "is the same (they both might be empty as
well).");
+ "] contains two methods with an action
name annotation whose value " +
+ "is the same (they both might be empty
as well).");
} else {
actionNames.add(actionName);
}
@@ -433,7 +467,7 @@
PackageConfig.Builder pkgCfg = defaultPackageConfig;
if (action.value().contains("/")) {
pkgCfg = getPackageConfig(packageConfigs,
namespace, actionPackage,
- actionClass, action);
+ actionClass, action);
}
createActionConfig(pkgCfg, actionClass,
defaultActionName, method, action);
@@ -466,8 +500,8 @@
* configuration values. These are used to determine which part of the
Java package name should
* be converted into the namespace for the XWork PackageConfig.
*
- * @param actionClass The action class.
- * @return The namespace or an empty string.
+ * @param actionClass The action class.
+ * @return The namespace or an empty string.
*/
protected List<String> determineActionNamespace(Class<?> actionClass) {
List<String> namespaces = new ArrayList<String>();
@@ -540,8 +574,8 @@
/**
* Converts the class name into an action name using the ActionNameBuilder.
*
- * @param actionClass The action class.
- * @return The action name.
+ * @param actionClass The action class.
+ * @return The action name.
*/
protected String determineActionName(Class<?> actionClass) {
String actionName =
actionNameBuilder.build(actionClass.getSimpleName());
@@ -556,8 +590,8 @@
* Locates all of the {...@link Actions} and {...@link Action} annotations
on methods within the Action
* class and its parent classes.
*
- * @param actionClass The action class.
- * @return The list of annotations or an empty list if there are none.
+ * @param actionClass The action class.
+ * @return The list of annotations or an empty list if there are none.
*/
protected Map<String, List<Action>> getActionAnnotations(Class<?>
actionClass) {
Method[] methods = actionClass.getMethods();
@@ -573,7 +607,7 @@
valuelessSeen = true;
} else if (ann.value().equals(Action.DEFAULT_VALUE)) {
throw new ConfigurationException("You may only add a
single Action " +
- "annotation that has no value parameter.");
+ "annotation that has no value parameter.");
}
actions.add(ann);
@@ -594,23 +628,23 @@
/**
* Creates a single ActionConfig object.
*
- * @param pkgCfg The package the action configuration instance will
belong to.
- * @param actionClass The action class.
- * @param actionName The name of the action.
- * @param actionMethod The method that the annotation was on (if the
annotation is not null) or
- * the default method (execute).
- * @param annotation The ActionName annotation that might override the
action name and possibly
+ * @param pkgCfg The package the action configuration instance will
belong to.
+ * @param actionClass The action class.
+ * @param actionName The name of the action.
+ * @param actionMethod The method that the annotation was on (if the
annotation is not null) or
+ * the default method (execute).
+ * @param annotation The ActionName annotation that might override the
action name and possibly
*/
protected void createActionConfig(PackageConfig.Builder pkgCfg, Class<?>
actionClass, String actionName,
- String actionMethod, Action annotation) {
+ String actionMethod, Action annotation) {
if (annotation != null) {
actionName = annotation.value() != null &&
annotation.value().equals(Action.DEFAULT_VALUE) ?
- actionName : annotation.value();
+ actionName : annotation.value();
actionName = StringTools.lastToken(actionName, "/");
}
ActionConfig.Builder actionConfig = new
ActionConfig.Builder(pkgCfg.getName(),
- actionName, actionClass.getName());
+ actionName, actionClass.getName());
actionConfig.methodName(actionMethod);
if (LOG.isDebugEnabled()) {
@@ -648,7 +682,14 @@
// there is a package already with that name, check action
ActionConfig existingActionConfig =
existingPkg.getActionConfigs().get(actionName);
if (existingActionConfig != null && LOG.isWarnEnabled())
- LOG.warn("Duplicated action definition in package [#0] with
name [#1]. First definition was loaded from [#3]", pkgCfg.getName(),
actionName, existingActionConfig.getLocation().toString());
+ LOG.warn("Duplicated action definition in package [#0] with
name [#1].", pkgCfg.getName(), actionName);
+ }
+
+ //watch class file
+ if (isReloadEnabled()) {
+ URL classFile =
actionClass.getResource(actionClass.getSimpleName() + ".class");
+ FileManager.loadFile(classFile, false);
+ loadedFileUrls.add(classFile.toString());
}
}
@@ -670,8 +711,8 @@
}
private PackageConfig.Builder getPackageConfig(final Map<String,
PackageConfig.Builder> packageConfigs,
- String actionNamespace, final String actionPackage, final Class<?>
actionClass,
- Action action) {
+ String actionNamespace,
final String actionPackage, final Class<?> actionClass,
+ Action action) {
if (action != null && !action.value().equals(Action.DEFAULT_VALUE)) {
if (LOG.isTraceEnabled()) {
LOG.trace("Using non-default action namespace from the Action
annotation of [#0]", action.value());
@@ -697,7 +738,7 @@
if (parentName == null) {
throw new ConfigurationException("Unable to determine the parent
XWork package for the action class [" +
- actionClass.getName() + "]");
+ actionClass.getName() + "]");
}
PackageConfig parentPkg = configuration.getPackageConfig(parentName);
@@ -735,12 +776,12 @@
*
* 1. Loop over all the namespaces such as /foo and see if it has an
action named index
* 2. If an action doesn't exists in the parent namespace of the same
name, create an action
- * in the parent namespace of the same name as the namespace that
points to the index
- * action in the namespace. e.g. /foo -> /foo/index
+ * in the parent namespace of the same name as the namespace that points
to the index
+ * action in the namespace. e.g. /foo -> /foo/index
* 3. Create the action in the namespace for empty string if it doesn't
exist. e.g. /foo/
- * the action is "" and the namespace is /foo
+ * the action is "" and the namespace is /foo
*
- * @param packageConfigs Used to store the actions.
+ * @param packageConfigs Used to store the actions.
*/
protected void buildIndexActions(Map<String, PackageConfig.Builder>
packageConfigs) {
Map<String, PackageConfig.Builder> byNamespace = new HashMap<String,
PackageConfig.Builder>();
@@ -769,7 +810,7 @@
if (parent == null ||
parent.build().getAllActionConfigs().get(parentAction) == null) {
if (parent == null) {
parent = new
PackageConfig.Builder(parentNamespace).namespace(parentNamespace).
- addParents(pkgConfig.build().getParents());
+ addParents(pkgConfig.build().getParents());
packageConfigs.put(parentNamespace, parent);
}
@@ -778,7 +819,7 @@
}
} else if (LOG.isTraceEnabled()) {
LOG.trace("The parent namespace [#0] already contains
" +
- "an action [#1]", parentNamespace, parentAction);
+ "an action [#1]", parentNamespace,
parentAction);
}
}
}
@@ -787,11 +828,30 @@
if (pkgConfig.build().getAllActionConfigs().get("") == null) {
if (LOG.isTraceEnabled()) {
LOG.trace("Creating index ActionConfig with an action name
of [] for the action " +
- "class [#0]", indexActionConfig.getClassName());
+ "class [#0]", indexActionConfig.getClassName());
}
pkgConfig.addActionConfig("", indexActionConfig);
}
}
}
+
+ public void destroy() {
+ loadedFileUrls.clear();
+ }
+
+ public boolean needsReload() {
+ if (devMode && reload) {
+ for (String url : loadedFileUrls) {
+ if (FileManager.fileNeedsReloading(url)) {
+ if (LOG.isDebugEnabled())
+ LOG.debug("File [#0] changed, configuration will be
reloaded", url);
+ return true;
+ }
+ }
+
+ return false;
+ } else
+ return false;
+ }
}
Added:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/FileResourceStore.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/FileResourceStore.java?rev=728469&view=auto
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/FileResourceStore.java
(added)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/FileResourceStore.java
Sun Dec 21 10:30:18 2008
@@ -0,0 +1,83 @@
+/*
+ * $Id$
+ *
+ * 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.struts2.convention.classloader;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+
+/**
+ * Reads a class from disk
+ * class taken from Apache JCI
+ */
+public final class FileResourceStore implements ResourceStore {
+
+ private final File root;
+
+ public FileResourceStore(final File pFile) {
+ root = pFile;
+ }
+
+ public byte[] read(final String pResourceName) {
+ FileInputStream fis = null;
+ try {
+ File file = getFile(pResourceName);
+ byte[] data = new byte[(int) file.length()];
+ fis = new FileInputStream(file);
+ fis.read(data);
+
+ return data;
+ } catch (Exception e) {
+ return null;
+ } finally {
+ closeQuietly(fis);
+ }
+ }
+
+ public void write(final String pResourceName, final byte[] pData) {
+
+ }
+
+ private void closeQuietly(InputStream is) {
+ try {
+ if (is != null)
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+
+ public void remove(final String pResourceName) {
+ getFile(pResourceName).delete();
+ }
+
+ private File getFile(final String pResourceName) {
+ final String fileName = pResourceName.replace('/', File.separatorChar);
+ return new File(root, fileName);
+ }
+
+ public String toString() {
+ return this.getClass().getName() + root.toString();
+ }
+
+
+}
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/FileResourceStore.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/FileResourceStore.java
------------------------------------------------------------------------------
svn:keywords = Id
Added:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ReloadingClassLoader.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ReloadingClassLoader.java?rev=728469&view=auto
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ReloadingClassLoader.java
(added)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ReloadingClassLoader.java
Sun Dec 21 10:30:18 2008
@@ -0,0 +1,140 @@
+/*
+ * $Id$
+ *
+ * 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.struts2.convention.classloader;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+import java.io.InputStream;
+import java.io.File;
+import java.net.URL;
+import java.net.URISyntaxException;
+
+import org.apache.struts2.StrutsException;
+
+
+/**
+ * The ReloadingClassLoader uses a delegation mechansim to allow
+ * classes to be reloaded. That means that loadClass calls may
+ * return different results if the class was change in the underlying
+ * ResoruceStore.
+ * <p/>
+ * class taken from Apache JCI
+ */
+public class ReloadingClassLoader extends ClassLoader {
+ private static final Logger LOG =
LoggerFactory.getLogger(ReloadingClassLoader.class);
+ private final ClassLoader parent;
+ private ResourceStore[] stores;
+ private ClassLoader delegate;
+
+ public ReloadingClassLoader(final ClassLoader pParent) {
+ super(pParent);
+ parent = pParent;
+ URL root = pParent.getResource("/");
+ try {
+ stores = new ResourceStore[]{new FileResourceStore(new
File(root.toURI()))};
+ } catch (URISyntaxException e) {
+ throw new StrutsException("Unable to start the reloadable class
loader, consider setting 'struts.convention.classes.reload' to false", e);
+ }
+
+ delegate = new ResourceStoreClassLoader(parent, stores);
+ }
+
+ public boolean addResourceStore(final ResourceStore pStore) {
+ try {
+ final int n = stores.length;
+ final ResourceStore[] newStores = new ResourceStore[n + 1];
+ System.arraycopy(stores, 0, newStores, 1, n);
+ newStores[0] = pStore;
+ stores = newStores;
+ delegate = new ResourceStoreClassLoader(parent, stores);
+ return true;
+ } catch (final RuntimeException e) {
+ LOG.error("could not add resource store " + pStore);
+ }
+ return false;
+ }
+
+ public boolean removeResourceStore(final ResourceStore pStore) {
+
+ final int n = stores.length;
+ int i = 0;
+
+ // FIXME: this should be improved with a Map
+ // find the pStore and index position with var i
+ while ((i < n) && (stores[i] != pStore)) {
+ i++;
+ }
+
+ // pStore was not found
+ if (i == n) {
+ return false;
+ }
+
+ // if stores length > 1 then array copy old values, else create new
empty store
+ final ResourceStore[] newStores = new ResourceStore[n - 1];
+ if (i > 0) {
+ System.arraycopy(stores, 0, newStores, 0, i);
+ }
+ if (i < n - 1) {
+ System.arraycopy(stores, i + 1, newStores, i, (n - i - 1));
+ }
+
+ stores = newStores;
+ delegate = new ResourceStoreClassLoader(parent, stores);
+ return true;
+ }
+
+ public void reload() {
+ if (LOG.isTraceEnabled())
+ LOG.trace("Reloading class loader");
+ delegate = new ResourceStoreClassLoader(parent, stores);
+ }
+
+ public void clearAssertionStatus() {
+ delegate.clearAssertionStatus();
+ }
+
+ public URL getResource(String name) {
+ return delegate.getResource(name);
+ }
+
+ public InputStream getResourceAsStream(String name) {
+ return delegate.getResourceAsStream(name);
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return delegate.loadClass(name);
+ }
+
+ public void setClassAssertionStatus(String className, boolean enabled) {
+ delegate.setClassAssertionStatus(className, enabled);
+ }
+
+ public void setDefaultAssertionStatus(boolean enabled) {
+ delegate.setDefaultAssertionStatus(enabled);
+ }
+
+ public void setPackageAssertionStatus(String packageName, boolean enabled)
{
+ delegate.setPackageAssertionStatus(packageName, enabled);
+ }
+}
+
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ReloadingClassLoader.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ReloadingClassLoader.java
------------------------------------------------------------------------------
svn:keywords = Id
Added:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStore.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStore.java?rev=728469&view=auto
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStore.java
(added)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStore.java
Sun Dec 21 10:30:18 2008
@@ -0,0 +1,35 @@
+/*
+ * $Id$
+ *
+ * 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.struts2.convention.classloader;
+
+/**
+ * *interface taken from Apache JCI
+ */
+public interface ResourceStore {
+
+ void write(final String pResourceName, final byte[] pResourceData);
+
+ byte[] read(final String pResourceName);
+
+ //FIXME: return the result of the remove
+ void remove(final String pResourceName);
+}
+
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStore.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStore.java
------------------------------------------------------------------------------
svn:keywords = Id
Added:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStoreClassLoader.java
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStoreClassLoader.java?rev=728469&view=auto
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStoreClassLoader.java
(added)
+++
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStoreClassLoader.java
Sun Dec 21 10:30:18 2008
@@ -0,0 +1,88 @@
+/*
+ * $Id$
+ *
+ * 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.struts2.convention.classloader;
+
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+/**
+ * class taken from Apache JCI
+ */
+public final class ResourceStoreClassLoader extends ClassLoader {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(ResourceStoreClassLoader.class);
+
+ private final ResourceStore[] stores;
+
+ public ResourceStoreClassLoader(final ClassLoader pParent, final
ResourceStore[] pStores) {
+ super(pParent);
+
+ stores = new ResourceStore[pStores.length];
+ System.arraycopy(pStores, 0, stores, 0, stores.length);
+ }
+
+ private Class fastFindClass(final String name) {
+
+ if (stores != null) {
+ for (int i = 0; i < stores.length; i++) {
+ final ResourceStore store = stores[i];
+ final byte[] clazzBytes = store.read(name.replace('.', '/') +
".class");
+ if (clazzBytes != null) {
+ return defineClass(name, clazzBytes, 0, clazzBytes.length);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ protected synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
+ Class clazz = findLoadedClass(name);
+
+ if (clazz == null) {
+ clazz = fastFindClass(name);
+
+ if (clazz == null) {
+ final ClassLoader parent = getParent();
+ if (parent != null) {
+ clazz = parent.loadClass(name);
+ } else {
+ throw new ClassNotFoundException(name);
+ }
+
+ }
+ }
+
+ if (resolve) {
+ resolveClass(clazz);
+ }
+
+ return clazz;
+ }
+
+ protected Class findClass(final String name) throws ClassNotFoundException
{
+ final Class clazz = fastFindClass(name);
+ if (clazz == null) {
+ throw new ClassNotFoundException(name);
+ }
+ return clazz;
+ }
+}
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStoreClassLoader.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
struts/struts2/trunk/plugins/convention/src/main/java/org/apache/struts2/convention/classloader/ResourceStoreClassLoader.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified:
struts/struts2/trunk/plugins/convention/src/main/resources/struts-plugin.xml
URL:
http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/convention/src/main/resources/struts-plugin.xml?rev=728469&r1=728468&r2=728469&view=diff
==============================================================================
---
struts/struts2/trunk/plugins/convention/src/main/resources/struts-plugin.xml
(original)
+++
struts/struts2/trunk/plugins/convention/src/main/resources/struts-plugin.xml
Sun Dec 21 10:30:18 2008
@@ -35,7 +35,8 @@
<bean type="org.apache.struts2.convention.InterceptorMapBuilder"
class="org.apache.struts2.convention.DefaultInterceptorMapBuilder"/>
<bean type="org.apache.struts2.convention.ConventionsService"
class="org.apache.struts2.convention.ConventionsServiceImpl"/>
- <bean type="com.opensymphony.xwork2.config.PackageProvider"
class="org.apache.struts2.convention.ClasspathConfigurationProvider"/>
+ <bean type="com.opensymphony.xwork2.config.PackageProvider"
name="convention.packageProvider"
class="org.apache.struts2.convention.ClasspathPackageProvider"/>
+ <bean type="com.opensymphony.xwork2.config.PackageProvider"
name="convention.containerProvider"
class="org.apache.struts2.convention.ClasspathConfigurationProvider"/>
<constant name="struts.convention.result.path" value="/WEB-INF/content/"/>
<constant name="struts.convention.result.flatLayout" value="true"/>
@@ -55,6 +56,8 @@
<constant name="struts.convention.redirect.to.slash" value="true"/>
<constant name="struts.mapper.alwaysSelectFullNamespace" value="true"/>
+ <constant name="struts.convention.classes.reload" value="false" />
+
<package name="convention-default" extends="struts-default">
</package>
</struts>