diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContext.java
new file mode 100644
index 00000000000..16e9f36b6cd
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContext.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import org.springframework.beans.factory.HierarchicalBeanFactory;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.core.io.support.ResourcePatternResolver;
+
+/**
+ * Central interface to provide configuration for an application.
+ * This is read-only while the application is running, but may be
+ * reloaded if the implementation supports this.
+ *
+ *
An ApplicationContext provides:
+ *
+ *
Bean factory methods for accessing application components.
+ * Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
+ *
The ability to load file resources in a generic fashion.
+ * Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
+ *
The ability to publish events to registered listeners.
+ * Inherited from the {@link ApplicationEventPublisher} interface.
+ *
The ability to resolve messages, supporting internationalization.
+ * Inherited from the {@link MessageSource} interface.
+ *
Inheritance from a parent context. Definitions in a descendant context
+ * will always take priority. This means, for example, that a single parent
+ * context can be used by an entire web application, while each servlet has
+ * its own child context that is independent of that of any other servlet.
+ *
+ *
+ *
In addition to standard {@link org.springframework.beans.factory.BeanFactory}
+ * lifecycle capabilities, ApplicationContext implementations detect and invoke
+ * {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
+ * {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see ConfigurableApplicationContext
+ * @see org.springframework.beans.factory.BeanFactory
+ * @see org.springframework.core.io.ResourceLoader
+ */
+public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory,
+ MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
+
+ /**
+ * Return the unique id of this application context.
+ * @return the unique id of the context
+ */
+ String getId();
+
+ /**
+ * Return a friendly name for this context.
+ * @return a display name for this context
+ */
+ String getDisplayName();
+
+ /**
+ * Return the timestamp when this context was first loaded.
+ * @return the timestamp (ms) when this context was first loaded
+ */
+ long getStartupDate();
+
+ /**
+ * Return the parent context, or null if there is no parent
+ * and this is the root of the context hierarchy.
+ * @return the parent context, or null if there is no parent
+ */
+ ApplicationContext getParent();
+
+ /**
+ * Expose AutowireCapableBeanFactory functionality for this context.
+ *
This is not typically used by application code, except for the purpose
+ * of initializing bean instances that live outside the application context,
+ * applying the Spring bean lifecycle (fully or partly) to them.
+ *
Alternatively, the internal BeanFactory exposed by the
+ * {@link ConfigurableApplicationContext} interface offers access to the
+ * AutowireCapableBeanFactory interface too. The present method mainly
+ * serves as convenient, specific facility on the ApplicationContext
+ * interface itself.
+ * @return the AutowireCapableBeanFactory for this context
+ * @throws IllegalStateException if the context does not support
+ * the AutowireCapableBeanFactory interface or does not hold an autowire-capable
+ * bean factory yet (usually if refresh() has never been called)
+ * @see ConfigurableApplicationContext#refresh()
+ * @see ConfigurableApplicationContext#getBeanFactory()
+ */
+ AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextAware.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextAware.java
new file mode 100644
index 00000000000..f26919e81a9
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextAware.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import org.springframework.beans.BeansException;
+
+/**
+ * Interface to be implemented by any object that wishes to be notified
+ * of the {@link ApplicationContext} that it runs in.
+ *
+ *
Implementing this interface makes sense for example when an object
+ * requires access to a set of collaborating beans. Note that configuration
+ * via bean references is preferable to implementing this interface just
+ * for bean lookup purposes.
+ *
+ *
This interface can also be implemented if an object needs access to file
+ * resources, i.e. wants to call getResource, wants to publish
+ * an application event, or requires access to the MessageSource. However,
+ * it is preferable to implement the more specific {@link ResourceLoaderAware},
+ * {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
+ * in such a specific scenario.
+ *
+ *
Note that file resource dependencies can also be exposed as bean properties
+ * of type {@link org.springframework.core.io.Resource}, populated via Strings
+ * with automatic type conversion by the bean factory. This removes the need
+ * for implementing any callback interface just for the purpose of accessing
+ * a specific file resource.
+ *
+ *
{@link org.springframework.context.support.ApplicationObjectSupport} is a
+ * convenience base class for application objects, implementing this interface.
+ *
+ *
For a list of all bean lifecycle methods, see the
+ * {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see ResourceLoaderAware
+ * @see ApplicationEventPublisherAware
+ * @see MessageSourceAware
+ * @see org.springframework.context.support.ApplicationObjectSupport
+ * @see org.springframework.beans.factory.BeanFactoryAware
+ */
+public interface ApplicationContextAware {
+
+ /**
+ * Set the ApplicationContext that this object runs in.
+ * Normally this call will be used to initialize the object.
+ *
Invoked after population of normal bean properties but before an init callback such
+ * as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
+ * or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
+ * {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
+ * {@link MessageSourceAware}, if applicable.
+ * @param applicationContext the ApplicationContext object to be used by this object
+ * @throws ApplicationContextException in case of context initialization errors
+ * @throws BeansException if thrown by application context methods
+ * @see org.springframework.beans.factory.BeanInitializationException
+ */
+ void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextException.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextException.java
new file mode 100644
index 00000000000..9c9f4066a4d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationContextException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import org.springframework.beans.FatalBeanException;
+
+/**
+ * Exception thrown during application context initialization.
+ *
+ * @author Rod Johnson
+ */
+public class ApplicationContextException extends FatalBeanException {
+
+ /**
+ * Create a new ApplicationContextException
+ * with the specified detail message and no root cause.
+ * @param msg the detail message
+ */
+ public ApplicationContextException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Create a new ApplicationContextException
+ * with the specified detail message and the given root cause.
+ * @param msg the detail message
+ * @param cause the root cause
+ */
+ public ApplicationContextException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationEvent.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationEvent.java
new file mode 100644
index 00000000000..66bddfcfd28
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationEvent.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import java.util.EventObject;
+
+/**
+ * Class to be extended by all application events. Abstract as it
+ * doesn't make sense for generic events to be published directly.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ */
+public abstract class ApplicationEvent extends EventObject {
+
+ /** use serialVersionUID from Spring 1.2 for interoperability */
+ private static final long serialVersionUID = 7099057708183571937L;
+
+ /** System time when the event happened */
+ private final long timestamp;
+
+
+ /**
+ * Create a new ApplicationEvent.
+ * @param source the component that published the event (never null)
+ */
+ public ApplicationEvent(Object source) {
+ super(source);
+ this.timestamp = System.currentTimeMillis();
+ }
+
+
+ /**
+ * Return the system time in milliseconds when the event happened.
+ */
+ public final long getTimestamp() {
+ return this.timestamp;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationEventPublisher.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
new file mode 100644
index 00000000000..f92a26ef7a1
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationEventPublisher.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+/**
+ * Interface that encapsulates event publication functionality.
+ * Serves as super-interface for ApplicationContext.
+ *
+ * @author Juergen Hoeller
+ * @since 1.1.1
+ * @see ApplicationContext
+ * @see ApplicationEventPublisherAware
+ * @see org.springframework.context.ApplicationEvent
+ * @see org.springframework.context.event.EventPublicationInterceptor
+ */
+public interface ApplicationEventPublisher {
+
+ /**
+ * Notify all listeners registered with this application of an application
+ * event. Events may be framework events (such as RequestHandledEvent)
+ * or application-specific events.
+ * @param event the event to publish
+ * @see org.springframework.web.context.support.RequestHandledEvent
+ */
+ void publishEvent(ApplicationEvent event);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationEventPublisherAware.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationEventPublisherAware.java
new file mode 100644
index 00000000000..e0a0c5a4e8e
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationEventPublisherAware.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+/**
+ * Interface to be implemented by any object that wishes to be notified
+ * of the ApplicationEventPublisher (typically the ApplicationContext)
+ * that it runs in.
+ *
+ * @author Juergen Hoeller
+ * @since 1.1.1
+ * @see ApplicationContextAware
+ */
+public interface ApplicationEventPublisherAware {
+
+ /**
+ * Set the ApplicationEventPublisher that this object runs in.
+ *
Invoked after population of normal bean properties but before an init
+ * callback like InitializingBean's afterPropertiesSet or a custom init-method.
+ * Invoked before ApplicationContextAware's setApplicationContext.
+ * @param applicationEventPublisher event publisher to be used by this object
+ */
+ void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ApplicationListener.java b/org.springframework.context/src/main/java/org/springframework/context/ApplicationListener.java
new file mode 100644
index 00000000000..5446d75246e
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ApplicationListener.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import java.util.EventListener;
+
+/**
+ * Interface to be implemented by application event listeners.
+ * Based on the standard java.util.EventListener interface
+ * for the Observer design pattern.
+ *
+ * @author Rod Johnson
+ * @see org.springframework.context.event.ApplicationEventMulticaster
+ */
+public interface ApplicationListener extends EventListener {
+
+ /**
+ * Handle an application event.
+ * @param event the event to respond to
+ */
+ void onApplicationEvent(ApplicationEvent event);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
new file mode 100644
index 00000000000..e5266d0bdf9
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ConfigurableApplicationContext.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+
+/**
+ * SPI interface to be implemented by most if not all application contexts.
+ * Provides facilities to configure an application context in addition
+ * to the application context client methods in the
+ * {@link org.springframework.context.ApplicationContext} interface.
+ *
+ *
Configuration and lifecycle methods are encapsulated here to avoid
+ * making them obvious to ApplicationContext client code. The present
+ * methods should only be used by startup and shutdown code.
+ *
+ * @author Juergen Hoeller
+ * @since 03.11.2003
+ */
+public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle {
+
+ /**
+ * Any number of these characters are considered delimiters between
+ * multiple context config paths in a single String value.
+ * @see org.springframework.context.support.AbstractXmlApplicationContext#setConfigLocation
+ * @see org.springframework.web.context.ContextLoader#CONFIG_LOCATION_PARAM
+ * @see org.springframework.web.servlet.FrameworkServlet#setContextConfigLocation
+ */
+ String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
+
+ /**
+ * Name of the LoadTimeWeaver bean in the factory. If such a bean is supplied,
+ * the context will use a temporary ClassLoader for type matching, in order
+ * to allow the LoadTimeWeaver to process all actual bean classes.
+ * @see org.springframework.instrument.classloading.LoadTimeWeaver
+ */
+ String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
+
+
+ /**
+ * Set the parent of this application context.
+ *
Note that the parent shouldn't be changed: It should only be set outside
+ * a constructor if it isn't available when an object of this class is created,
+ * for example in case of WebApplicationContext setup.
+ * @param parent the parent context
+ * @see org.springframework.web.context.ConfigurableWebApplicationContext
+ */
+ void setParent(ApplicationContext parent);
+
+ /**
+ * Add a new BeanFactoryPostProcessor that will get applied to the internal
+ * bean factory of this application context on refresh, before any of the
+ * bean definitions get evaluated. To be invoked during context configuration.
+ * @param beanFactoryPostProcessor the factory processor to register
+ */
+ void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);
+
+ /**
+ * Add a new ApplicationListener that will be notified on context events
+ * such as context refresh and context shutdown.
+ *
Note that any ApplicationListener registered here will be applied
+ * on refresh of this context. If a listener is added after the initial
+ * refresh, it will be applied on next refresh of the context.
+ * @param listener the ApplicationListener to register
+ * @see org.springframework.context.event.ContextRefreshedEvent
+ * @see org.springframework.context.event.ContextClosedEvent
+ */
+ void addApplicationListener(ApplicationListener listener);
+
+ /**
+ * Load or refresh the persistent representation of the configuration,
+ * which might an XML file, properties file, or relational database schema.
+ *
As this is a startup method, it should destroy already created singletons
+ * if it fails, to avoid dangling resources. In other words, after invocation
+ * of that method, either all or no singletons at all should be instantiated.
+ * @throws BeansException if the bean factory could not be initialized
+ * @throws IllegalStateException if already initialized and multiple refresh
+ * attempts are not supported
+ */
+ void refresh() throws BeansException, IllegalStateException;
+
+ /**
+ * Register a shutdown hook with the JVM runtime, closing this context
+ * on JVM shutdown unless it has already been closed at that time.
+ *
This method can be called multiple times. Only one shutdown hook
+ * (at max) will be registered for each context instance.
+ * @see java.lang.Runtime#addShutdownHook
+ * @see #close()
+ */
+ void registerShutdownHook();
+
+ /**
+ * Close this application context, releasing all resources and locks that the
+ * implementation might hold. This includes destroying all cached singleton beans.
+ *
Note: Does not invoke close on a parent context;
+ * parent contexts have their own, independent lifecycle.
+ *
This method can be called multiple times without side effects: Subsequent
+ * close calls on an already closed context will be ignored.
+ */
+ void close();
+
+ /**
+ * Determine whether this application context is active, that is,
+ * whether it has been refreshed at least once and has not been closed yet.
+ * @return whether the context is still active
+ * @see #refresh()
+ * @see #close()
+ * @see #getBeanFactory()
+ */
+ boolean isActive();
+
+ /**
+ * Return the internal bean factory of this application context.
+ * Can be used to access specific functionality of the underlying factory.
+ *
Note: Do not use this to post-process the bean factory; singletons
+ * will already have been instantiated before. Use a BeanFactoryPostProcessor
+ * to intercept the BeanFactory setup process before beans get touched.
+ *
Generally, this internal factory will only be accessible while the context
+ * is active, that is, inbetween {@link #refresh()} and {@link #close()}.
+ * The {@link #isActive()} flag can be used to check whether the context
+ * is in an appropriate state.
+ * @return the underlying bean factory
+ * @throws IllegalStateException if the context does not hold an internal
+ * bean factory (usually if {@link #refresh()} hasn't been called yet or
+ * if {@link #close()} has already been called)
+ * @see #isActive()
+ * @see #refresh()
+ * @see #close()
+ * @see #addBeanFactoryPostProcessor
+ */
+ ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/HierarchicalMessageSource.java b/org.springframework.context/src/main/java/org/springframework/context/HierarchicalMessageSource.java
new file mode 100644
index 00000000000..45ef4243c76
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/HierarchicalMessageSource.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+/**
+ * Sub-interface of MessageSource to be implemented by objects that
+ * can resolve messages hierarchically.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ */
+public interface HierarchicalMessageSource extends MessageSource {
+
+ /**
+ * Set the parent that will be used to try to resolve messages
+ * that this object can't resolve.
+ * @param parent the parent MessageSource that will be used to
+ * resolve messages that this object can't resolve.
+ * May be null, in which case no further resolution is possible.
+ */
+ void setParentMessageSource(MessageSource parent);
+
+ /**
+ * Return the parent of this MessageSource, or null if none.
+ */
+ MessageSource getParentMessageSource();
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java b/org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java
new file mode 100644
index 00000000000..112aebcea5f
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/Lifecycle.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+/**
+ * Interface defining methods for start/stop lifecycle control.
+ * The typical use case for this is to control asynchronous processing.
+ *
+ *
Can be implemented by both components (typically a Spring bean defined in
+ * a Spring {@link org.springframework.beans.factory.BeanFactory}) and containers
+ * (typically a Spring {@link ApplicationContext}). Containers will propagate
+ * start/stop signals to all components that apply.
+ *
+ *
Can be used for direct invocations or for management operations via JMX.
+ * In the latter case, the {@link org.springframework.jmx.export.MBeanExporter}
+ * will typically be defined with an
+ * {@link org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler},
+ * restricting the visibility of activity-controlled components to the Lifecycle
+ * interface.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see ConfigurableApplicationContext
+ * @see org.springframework.jms.listener.AbstractMessageListenerContainer
+ * @see org.springframework.scheduling.quartz.SchedulerFactoryBean
+ */
+public interface Lifecycle {
+
+ /**
+ * Start this component.
+ * Should not throw an exception if the component is already running.
+ *
In the case of a container, this will propagate the start signal
+ * to all components that apply.
+ */
+ void start();
+
+ /**
+ * Stop this component.
+ * Should not throw an exception if the component isn't started yet.
+ *
In the case of a container, this will propagate the stop signal
+ * to all components that apply.
+ */
+ void stop();
+
+ /**
+ * Check whether this component is currently running.
+ *
In the case of a container, this will return true
+ * only if all components that apply are currently running.
+ * @return whether the component is currently running
+ */
+ boolean isRunning();
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/MessageSource.java b/org.springframework.context/src/main/java/org/springframework/context/MessageSource.java
new file mode 100644
index 00000000000..b83ec2a014d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/MessageSource.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import java.util.Locale;
+
+/**
+ * Strategy interface for resolving messages, with support for the parameterization
+ * and internationalization of such messages.
+ *
+ *
Spring provides two out-of-the-box implementations for production:
+ *
+ *
{@link org.springframework.context.support.ResourceBundleMessageSource},
+ * built on top of the standard {@link java.util.ResourceBundle}
+ *
{@link org.springframework.context.support.ReloadableResourceBundleMessageSource},
+ * being able to reload message definitions without restarting the VM
+ *
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see org.springframework.context.support.ResourceBundleMessageSource
+ * @see org.springframework.context.support.ReloadableResourceBundleMessageSource
+ */
+public interface MessageSource {
+
+ /**
+ * Try to resolve the message. Return default message if no message was found.
+ * @param code the code to lookup up, such as 'calculator.noRateSet'. Users of
+ * this class are encouraged to base message names on the relevant fully
+ * qualified class name, thus avoiding conflict and ensuring maximum clarity.
+ * @param args array of arguments that will be filled in for params within
+ * the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
+ * or null if none.
+ * @param defaultMessage String to return if the lookup fails
+ * @param locale the Locale in which to do the lookup
+ * @return the resolved message if the lookup was successful;
+ * otherwise the default message passed as a parameter
+ * @see java.text.MessageFormat
+ */
+ String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
+
+ /**
+ * Try to resolve the message. Treat as an error if the message can't be found.
+ * @param code the code to lookup up, such as 'calculator.noRateSet'
+ * @param args Array of arguments that will be filled in for params within
+ * the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
+ * or null if none.
+ * @param locale the Locale in which to do the lookup
+ * @return the resolved message
+ * @throws NoSuchMessageException if the message wasn't found
+ * @see java.text.MessageFormat
+ */
+ String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
+
+ /**
+ * Try to resolve the message using all the attributes contained within the
+ * MessageSourceResolvable argument that was passed in.
+ *
NOTE: We must throw a NoSuchMessageException on this method
+ * since at the time of calling this method we aren't able to determine if the
+ * defaultMessage property of the resolvable is null or not.
+ * @param resolvable value object storing attributes required to properly resolve a message
+ * @param locale the Locale in which to do the lookup
+ * @return the resolved message
+ * @throws NoSuchMessageException if the message wasn't found
+ * @see java.text.MessageFormat
+ */
+ String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/MessageSourceAware.java b/org.springframework.context/src/main/java/org/springframework/context/MessageSourceAware.java
new file mode 100644
index 00000000000..3007e274189
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/MessageSourceAware.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+/**
+ * Interface to be implemented by any object that wishes to be notified
+ * of the MessageSource (typically the ApplicationContext) that it runs in.
+ *
+ *
Note that the MessageSource can usually also be passed on as bean
+ * reference (to arbitrary bean properties or constructor arguments), because
+ * it is defined as bean with name "messageSource" in the application context.
+ *
+ * @author Juergen Hoeller
+ * @since 1.1.1
+ * @see ApplicationContextAware
+ */
+public interface MessageSourceAware {
+
+ /**
+ * Set the MessageSource that this object runs in.
+ *
Invoked after population of normal bean properties but before an init
+ * callback like InitializingBean's afterPropertiesSet or a custom init-method.
+ * Invoked before ApplicationContextAware's setApplicationContext.
+ * @param messageSource message sourceto be used by this object
+ */
+ void setMessageSource(MessageSource messageSource);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/MessageSourceResolvable.java b/org.springframework.context/src/main/java/org/springframework/context/MessageSourceResolvable.java
new file mode 100644
index 00000000000..d1b1f388d06
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/MessageSourceResolvable.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+/**
+ * Interface for objects that are suitable for message resolution in a
+ * {@link MessageSource}.
+ *
+ *
Spring's own validation error classes implement this interface.
+ *
+ * @author Juergen Hoeller
+ * @see MessageSource#getMessage(MessageSourceResolvable, java.util.Locale)
+ * @see org.springframework.validation.ObjectError
+ * @see org.springframework.validation.FieldError
+ */
+public interface MessageSourceResolvable {
+
+ /**
+ * Return the codes to be used to resolve this message, in the order that
+ * they should get tried. The last code will therefore be the default one.
+ * @return a String array of codes which are associated with this message
+ */
+ public String[] getCodes();
+
+ /**
+ * Return the array of arguments to be used to resolve this message.
+ * @return an array of objects to be used as parameters to replace
+ * placeholders within the message text
+ * @see java.text.MessageFormat
+ */
+ public Object[] getArguments();
+
+ /**
+ * Return the default message to be used to resolve this message.
+ * @return the default message, or null if no default
+ */
+ public String getDefaultMessage();
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/NoSuchMessageException.java b/org.springframework.context/src/main/java/org/springframework/context/NoSuchMessageException.java
new file mode 100644
index 00000000000..b2ab999a3f5
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/NoSuchMessageException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import java.util.Locale;
+
+/**
+ * Exception thrown when a message can't be resolved.
+ *
+ * @author Rod Johnson
+ */
+public class NoSuchMessageException extends RuntimeException {
+
+ /**
+ * Create a new exception.
+ * @param code code that could not be resolved for given locale
+ * @param locale locale that was used to search for the code within
+ */
+ public NoSuchMessageException(String code, Locale locale) {
+ super("No message found under code '" + code + "' for locale '" + locale + "'.");
+ }
+
+ /**
+ * Create a new exception.
+ * @param code code that could not be resolved for given locale
+ */
+ public NoSuchMessageException(String code) {
+ super("No message found under code '" + code + "' for locale '" + Locale.getDefault() + "'.");
+ }
+
+}
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/ResourceLoaderAware.java b/org.springframework.context/src/main/java/org/springframework/context/ResourceLoaderAware.java
new file mode 100644
index 00000000000..c95f8b964bf
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/ResourceLoaderAware.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed 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.springframework.context;
+
+import org.springframework.core.io.ResourceLoader;
+
+/**
+ * Interface to be implemented by any object that wishes to be notified of
+ * the ResourceLoader (typically the ApplicationContext) that it runs in.
+ * This is an alternative to a full ApplicationContext dependency via the
+ * ApplicationContextAware interface.
+ *
+ *
Note that Resource dependencies can also be exposed as bean properties
+ * of type Resource, populated via Strings with automatic type conversion by
+ * the bean factory. This removes the need for implementing any callback
+ * interface just for the purpose of accessing a specific file resource.
+ *
+ *
You typically need a ResourceLoader when your application object has
+ * to access a variety of file resources whose names are calculated. A good
+ * strategy is to make the object use a DefaultResourceLoader but still
+ * implement ResourceLoaderAware to allow for overriding when running in an
+ * ApplicationContext. See ReloadableResourceBundleMessageSource for an example.
+ *
+ *
A passed-in ResourceLoader can also be checked for the
+ * ResourcePatternResolver interface and cast accordingly, to be able
+ * to resolve resource patterns into arrays of Resource objects. This will always
+ * work when running in an ApplicationContext (the context interface extends
+ * ResourcePatternResolver). Use a PathMatchingResourcePatternResolver as default.
+ * See also the ResourcePatternUtils.getResourcePatternResolver method.
+ *
+ *
As alternative to a ResourcePatternResolver dependency, consider exposing
+ * bean properties of type Resource array, populated via pattern Strings with
+ * automatic type conversion by the bean factory.
+ *
+ * @author Juergen Hoeller
+ * @since 10.03.2004
+ * @see ApplicationContextAware
+ * @see org.springframework.beans.factory.InitializingBean
+ * @see org.springframework.core.io.Resource
+ * @see org.springframework.core.io.support.ResourcePatternResolver
+ * @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver
+ * @see org.springframework.core.io.DefaultResourceLoader
+ * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
+ * @see org.springframework.context.support.ReloadableResourceBundleMessageSource
+ */
+public interface ResourceLoaderAware {
+
+ /**
+ * Set the ResourceLoader that this object runs in.
+ *
This might be a ResourcePatternResolver, which can be checked
+ * through instanceof ResourcePatternResolver. See also the
+ * ResourcePatternUtils.getResourcePatternResolver method.
+ *
Invoked after population of normal bean properties but before an init callback
+ * like InitializingBean's afterPropertiesSet or a custom init-method.
+ * Invoked before ApplicationContextAware's setApplicationContext.
+ * @param resourceLoader ResourceLoader object to be used by this object
+ * @see org.springframework.core.io.support.ResourcePatternResolver
+ * @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver
+ */
+ void setResourceLoader(ResourceLoader resourceLoader);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/access/ContextBeanFactoryReference.java b/org.springframework.context/src/main/java/org/springframework/context/access/ContextBeanFactoryReference.java
new file mode 100644
index 00000000000..fe56c165ae1
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/access/ContextBeanFactoryReference.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context.access;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.access.BeanFactoryReference;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
+
+/**
+ * ApplicationContext-specific implementation of BeanFactoryReference,
+ * wrapping a newly created ApplicationContext, closing it on release.
+ *
+ *
As per BeanFactoryReference contract, release may be called
+ * more than once, with subsequent calls not doing anything. However, calling
+ * getFactory after a release call will cause an exception.
+ *
+ * @author Juergen Hoeller
+ * @author Colin Sampaleanu
+ * @since 13.02.2004
+ * @see org.springframework.context.ConfigurableApplicationContext#close
+ */
+public class ContextBeanFactoryReference implements BeanFactoryReference {
+
+ private ApplicationContext applicationContext;
+
+
+ /**
+ * Create a new ContextBeanFactoryReference for the given context.
+ * @param applicationContext the ApplicationContext to wrap
+ */
+ public ContextBeanFactoryReference(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+
+ public BeanFactory getFactory() {
+ if (this.applicationContext == null) {
+ throw new IllegalStateException(
+ "ApplicationContext owned by this BeanFactoryReference has been released");
+ }
+ return this.applicationContext;
+ }
+
+ public void release() {
+ if (this.applicationContext != null) {
+ ApplicationContext savedCtx;
+
+ // We don't actually guarantee thread-safety, but it's not a lot of extra work.
+ synchronized (this) {
+ savedCtx = this.applicationContext;
+ this.applicationContext = null;
+ }
+
+ if (savedCtx != null && savedCtx instanceof ConfigurableApplicationContext) {
+ ((ConfigurableApplicationContext) savedCtx).close();
+ }
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/access/ContextJndiBeanFactoryLocator.java b/org.springframework.context/src/main/java/org/springframework/context/access/ContextJndiBeanFactoryLocator.java
new file mode 100644
index 00000000000..3a1f46347f0
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/access/ContextJndiBeanFactoryLocator.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.access;
+
+import javax.naming.NamingException;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.access.BeanFactoryLocator;
+import org.springframework.beans.factory.access.BeanFactoryReference;
+import org.springframework.beans.factory.access.BootstrapException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.jndi.JndiLocatorSupport;
+import org.springframework.util.StringUtils;
+
+/**
+ * BeanFactoryLocator implementation that creates the BeanFactory from one or
+ * more classpath locations specified in a JNDI environment variable.
+ *
+ *
This default implementation creates a
+ * {@link org.springframework.context.support.ClassPathXmlApplicationContext}.
+ * Subclasses may override {@link #createBeanFactory} for custom instantiation.
+ *
+ * @author Colin Sampaleanu
+ * @author Juergen Hoeller
+ * @see #createBeanFactory
+ */
+public class ContextJndiBeanFactoryLocator extends JndiLocatorSupport implements BeanFactoryLocator {
+
+ /**
+ * Any number of these characters are considered delimiters between
+ * multiple bean factory config paths in a single String value.
+ */
+ public static final String BEAN_FACTORY_PATH_DELIMITERS = ",; \t\n";
+
+
+ /**
+ * Load/use a bean factory, as specified by a factory key which is a JNDI
+ * address, of the form java:comp/env/ejb/BeanFactoryPath. The
+ * contents of this JNDI location must be a string containing one or more
+ * classpath resource names (separated by any of the delimiters ',; \t\n'
+ * if there is more than one. The resulting BeanFactory (or ApplicationContext)
+ * will be created from the combined resources.
+ * @see #createBeanFactory
+ */
+ public BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException {
+ try {
+ String beanFactoryPath = (String) lookup(factoryKey, String.class);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Bean factory path from JNDI environment variable [" + factoryKey +
+ "] is: " + beanFactoryPath);
+ }
+ String[] paths = StringUtils.tokenizeToStringArray(beanFactoryPath, BEAN_FACTORY_PATH_DELIMITERS);
+ return createBeanFactory(paths);
+ }
+ catch (NamingException ex) {
+ throw new BootstrapException("Define an environment variable [" + factoryKey + "] containing " +
+ "the class path locations of XML bean definition files", ex);
+ }
+ }
+
+ /**
+ * Create the BeanFactory instance, given an array of class path resource Strings
+ * which should be combined. This is split out as a separate method so that
+ * subclasses can override the actual BeanFactory implementation class.
+ *
Delegates to createApplicationContext by default,
+ * wrapping the result in a ContextBeanFactoryReference.
+ * @param resources an array of Strings representing classpath resource names
+ * @return the created BeanFactory, wrapped in a BeanFactoryReference
+ * (for example, a ContextBeanFactoryReference wrapping an ApplicationContext)
+ * @throws BeansException if factory creation failed
+ * @see #createApplicationContext
+ * @see ContextBeanFactoryReference
+ */
+ protected BeanFactoryReference createBeanFactory(String[] resources) throws BeansException {
+ ApplicationContext ctx = createApplicationContext(resources);
+ return new ContextBeanFactoryReference(ctx);
+ }
+
+ /**
+ * Create the ApplicationContext instance, given an array of class path resource
+ * Strings which should be combined
+ * @param resources an array of Strings representing classpath resource names
+ * @return the created ApplicationContext
+ * @throws BeansException if context creation failed
+ */
+ protected ApplicationContext createApplicationContext(String[] resources) throws BeansException {
+ return new ClassPathXmlApplicationContext(resources);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/access/ContextSingletonBeanFactoryLocator.java b/org.springframework.context/src/main/java/org/springframework/context/access/ContextSingletonBeanFactoryLocator.java
new file mode 100644
index 00000000000..a86916a6f8a
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/access/ContextSingletonBeanFactoryLocator.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.access;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.access.BeanFactoryLocator;
+import org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternUtils;
+
+/**
+ *
Variant of {@link org.springframework.beans.factory.access.SingletonBeanFactoryLocator}
+ * which creates its internal bean factory reference as an
+ * {@link org.springframework.context.ApplicationContext} instead of
+ * SingletonBeanFactoryLocator's simple BeanFactory. For almost all usage scenarios,
+ * this will not make a difference, since within that ApplicationContext or BeanFactory
+ * you are still free to define either BeanFactory or ApplicationContext instances.
+ * The main reason one would need to use this class is if bean post-processing
+ * (or other ApplicationContext specific features are needed in the bean reference
+ * definition itself).
+ *
+ *
Note: This class uses classpath*:beanRefContext.xml
+ * as the default resource location for the bean factory reference definition files.
+ * It is not possible nor legal to share definitions with SingletonBeanFactoryLocator
+ * at the same time.
+ *
+ * @author Colin Sampaleanu
+ * @author Juergen Hoeller
+ * @see org.springframework.beans.factory.access.SingletonBeanFactoryLocator
+ * @see org.springframework.context.access.DefaultLocatorFactory
+ */
+public class ContextSingletonBeanFactoryLocator extends SingletonBeanFactoryLocator {
+
+ private static final String DEFAULT_RESOURCE_LOCATION = "classpath*:beanRefContext.xml";
+
+ /** The keyed singleton instances */
+ private static final Map instances = new HashMap();
+
+
+ /**
+ * Returns an instance which uses the default "classpath*:beanRefContext.xml", as
+ * the name of the definition file(s). All resources returned by the current
+ * thread's context class loader's getResources method with this
+ * name will be combined to create a definition, which is just a BeanFactory.
+ * @return the corresponding BeanFactoryLocator instance
+ * @throws BeansException in case of factory loading failure
+ */
+ public static BeanFactoryLocator getInstance() throws BeansException {
+ return getInstance(null);
+ }
+
+ /**
+ * Returns an instance which uses the the specified selector, as the name of the
+ * definition file(s). In the case of a name with a Spring "classpath*:" prefix,
+ * or with no prefix, which is treated the same, the current thread's context class
+ * loader's getResources method will be called with this value to get
+ * all resources having that name. These resources will then be combined to form a
+ * definition. In the case where the name uses a Spring "classpath:" prefix, or
+ * a standard URL prefix, then only one resource file will be loaded as the
+ * definition.
+ * @param selector the location of the resource(s) which will be read and
+ * combined to form the definition for the BeanFactoryLocator instance.
+ * Any such files must form a valid ApplicationContext definition.
+ * @return the corresponding BeanFactoryLocator instance
+ * @throws BeansException in case of factory loading failure
+ */
+ public static BeanFactoryLocator getInstance(String selector) throws BeansException {
+ String resourceLocation = selector;
+ if (resourceLocation == null) {
+ resourceLocation = DEFAULT_RESOURCE_LOCATION;
+ }
+
+ // For backwards compatibility, we prepend "classpath*:" to the selector name if there
+ // is no other prefix (i.e. "classpath*:", "classpath:", or some URL prefix).
+ if (!ResourcePatternUtils.isUrl(resourceLocation)) {
+ resourceLocation = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceLocation;
+ }
+
+ synchronized (instances) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("ContextSingletonBeanFactoryLocator.getInstance(): instances.hashCode=" +
+ instances.hashCode() + ", instances=" + instances);
+ }
+ BeanFactoryLocator bfl = (BeanFactoryLocator) instances.get(resourceLocation);
+ if (bfl == null) {
+ bfl = new ContextSingletonBeanFactoryLocator(resourceLocation);
+ instances.put(resourceLocation, bfl);
+ }
+ return bfl;
+ }
+ }
+
+
+ /**
+ * Constructor which uses the the specified name as the resource name
+ * of the definition file(s).
+ * @param resourceLocation the Spring resource location to use
+ * (either a URL or a "classpath:" / "classpath*:" pseudo URL)
+ */
+ protected ContextSingletonBeanFactoryLocator(String resourceLocation) {
+ super(resourceLocation);
+ }
+
+ /**
+ * Overrides the default method to create definition object as an ApplicationContext
+ * instead of the default BeanFactory. This does not affect what can actually
+ * be loaded by that definition.
+ *
The default implementation simply builds a
+ * {@link org.springframework.context.support.ClassPathXmlApplicationContext}.
+ */
+ protected BeanFactory createDefinition(String resourceLocation, String factoryKey) {
+ return new ClassPathXmlApplicationContext(new String[] {resourceLocation}, false);
+ }
+
+ /**
+ * Overrides the default method to refresh the ApplicationContext, invoking
+ * {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.refresh()}.
+ */
+ protected void initializeDefinition(BeanFactory groupDef) {
+ if (groupDef instanceof ConfigurableApplicationContext) {
+ ((ConfigurableApplicationContext) groupDef).refresh();
+ }
+ }
+
+ /**
+ * Overrides the default method to operate on an ApplicationContext, invoking
+ * {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.close()}.
+ */
+ protected void destroyDefinition(BeanFactory groupDef, String selector) {
+ if (groupDef instanceof ConfigurableApplicationContext) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Context group with selector '" + selector +
+ "' being released, as there are no more references to it");
+ }
+ ((ConfigurableApplicationContext) groupDef).close();
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/access/DefaultLocatorFactory.java b/org.springframework.context/src/main/java/org/springframework/context/access/DefaultLocatorFactory.java
new file mode 100644
index 00000000000..66e88b6bfc8
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/access/DefaultLocatorFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context.access;
+
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.access.BeanFactoryLocator;
+
+/**
+ * A factory class to get a default ContextSingletonBeanFactoryLocator instance.
+ *
+ * @author Colin Sampaleanu
+ * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
+ */
+public class DefaultLocatorFactory {
+
+ /**
+ * Return an instance object implementing BeanFactoryLocator. This will normally
+ * be a singleton instance of the specific ContextSingletonBeanFactoryLocator class,
+ * using the default resource selector.
+ */
+ public static BeanFactoryLocator getInstance() throws FatalBeanException {
+ return ContextSingletonBeanFactoryLocator.getInstance();
+ }
+
+ /**
+ * Return an instance object implementing BeanFactoryLocator. This will normally
+ * be a singleton instance of the specific ContextSingletonBeanFactoryLocator class,
+ * using the specified resource selector.
+ * @param selector a selector variable which provides a hint to the factory as to
+ * which instance to return.
+ */
+ public static BeanFactoryLocator getInstance(String selector) throws FatalBeanException {
+ return ContextSingletonBeanFactoryLocator.getInstance(selector);
+ }
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/access/package.html b/org.springframework.context/src/main/java/org/springframework/context/access/package.html
new file mode 100644
index 00000000000..161da1e9d02
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/access/package.html
@@ -0,0 +1,11 @@
+
+
+
+Helper infrastructure to locate and access shared application contexts.
+
+
Note: This package is only relevant for special sharing of application
+contexts, for example behind EJB facades. It is not used in a typical
+web application or standalone application.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
new file mode 100644
index 00000000000..235e7668716
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.beans.Introspector;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanNameGenerator;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link org.springframework.beans.factory.support.BeanNameGenerator}
+ * implementation for bean classes annotated with the
+ * {@link org.springframework.stereotype.Component @Component} annotation
+ * or with another annotation that is itself annotated with
+ * {@link org.springframework.stereotype.Component @Component} as a
+ * meta-annotation. For example, Spring's stereotype annotations (such as
+ * {@link org.springframework.stereotype.Repository @Repository}) are
+ * themselves annotated with
+ * {@link org.springframework.stereotype.Component @Component}.
+ *
+ *
If the annotation's value doesn't indicate a bean name, an appropriate
+ * name will be built based on the short name of the class (with the first
+ * letter lower-cased). For example:
+ *
+ *
com.xyz.FooServiceImpl -> fooServiceImpl
+ *
+ * @author Juergen Hoeller
+ * @author Mark Fisher
+ * @since 2.5
+ * @see org.springframework.stereotype.Component#value()
+ * @see org.springframework.stereotype.Repository#value()
+ * @see org.springframework.stereotype.Service#value()
+ * @see org.springframework.stereotype.Controller#value()
+ */
+public class AnnotationBeanNameGenerator implements BeanNameGenerator {
+
+ private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
+
+
+ public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
+ if (definition instanceof AnnotatedBeanDefinition) {
+ String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
+ if (StringUtils.hasText(beanName)) {
+ // Explicit bean name found.
+ return beanName;
+ }
+ }
+ // Fallback: generate a unique default bean name.
+ return buildDefaultBeanName(definition);
+ }
+
+ /**
+ * Derive a bean name from one of the annotations on the class.
+ * @param annotatedDef the annotation-aware bean definition
+ * @return the bean name, or null if none is found
+ */
+ protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
+ AnnotationMetadata amd = annotatedDef.getMetadata();
+ Set types = amd.getAnnotationTypes();
+ String beanName = null;
+ for (String type : types) {
+ Map attributes = amd.getAnnotationAttributes(type);
+ if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
+ String value = (String) attributes.get("value");
+ if (StringUtils.hasLength(value)) {
+ if (beanName != null && !value.equals(beanName)) {
+ throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
+ "component names: '" + beanName + "' versus '" + value + "'");
+ }
+ beanName = value;
+ }
+ }
+ }
+ return beanName;
+ }
+
+ /**
+ * Check whether the given annotation is a stereotype that is allowed
+ * to suggest a component name through its annotation value().
+ * @param annotationType the name of the annotation class to check
+ * @param metaAnnotationTypes the names of meta-annotations on the given annotation
+ * @param attributes the map of attributes for the given annotation
+ * @return whether the annotation qualifies as a stereotype with component name
+ */
+ protected boolean isStereotypeWithNameValue(String annotationType,
+ Set metaAnnotationTypes, Map attributes) {
+
+ boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
+ (metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME));
+ return (isStereotype && attributes != null && attributes.containsKey("value"));
+ }
+
+ /**
+ * Derive a default bean name from the given bean definition.
+ *
The default implementation simply builds a decapitalized version
+ * of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".
+ *
Note that inner classes will thus have names of the form
+ * "outerClassName.innerClassName", which because of the period in the
+ * name may be an issue if you are autowiring by name.
+ * @param definition the bean definition to build a bean name for
+ * @return the default bean name (never null)
+ */
+ protected String buildDefaultBeanName(BeanDefinition definition) {
+ String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
+ return Introspector.decapitalize(shortClassName);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigBeanDefinitionParser.java
new file mode 100644
index 00000000000..c25b536f934
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigBeanDefinitionParser.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.util.Set;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+
+/**
+ * Parser for the <context:annotation-config/> element.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @author Christian Dupuis
+ * @since 2.5
+ * @see AnnotationConfigUtils
+ */
+public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ Object source = parserContext.extractSource(element);
+
+ // Obtain bean definitions for all relevant BeanPostProcessors.
+ Set processorDefinitions =
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
+
+ // Register component for the surrounding element.
+ CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
+ parserContext.pushContainingComponent(compDefinition);
+
+ // Nest the concrete beans in the surrounding component.
+ for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
+ parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
+ }
+
+ // Finally register the composite component.
+ parserContext.popAndRegisterContainingComponent();
+
+ return null;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java
new file mode 100644
index 00000000000..5c05e22dabe
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationConfigUtils.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
+import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.core.Ordered;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Utility class that allows for convenient registration of common
+ * {@link org.springframework.beans.factory.config.BeanPostProcessor}
+ * definitions for annotation-based configuration.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see CommonAnnotationBeanPostProcessor
+ * @see org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
+ * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
+ * @see org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
+ */
+public class AnnotationConfigUtils {
+
+ /**
+ * The bean name of the internally managed JPA annotation processor.
+ */
+ public static final String PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME =
+ "org.springframework.context.annotation.internalPersistenceAnnotationProcessor";
+
+ /**
+ * The bean name of the internally managed JSR-250 annotation processor.
+ */
+ public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
+ "org.springframework.context.annotation.internalCommonAnnotationProcessor";
+
+ /**
+ * The bean name of the internally managed Autowired annotation processor.
+ */
+ public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
+ "org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
+
+ /**
+ * The bean name of the internally managed Required annotation processor.
+ */
+ public static final String REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
+ "org.springframework.context.annotation.internalRequiredAnnotationProcessor";
+
+
+ private static final String PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME =
+ "org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor";
+
+
+ private static final boolean jsr250Present =
+ ClassUtils.isPresent("javax.annotation.Resource", AnnotationConfigUtils.class.getClassLoader());
+
+ private static final boolean jpaPresent =
+ ClassUtils.isPresent("javax.persistence.EntityManagerFactory", AnnotationConfigUtils.class.getClassLoader()) &&
+ ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader());
+
+
+ /**
+ * Register all relevant annotation post processors in the given registry.
+ * @param registry the registry to operate on
+ */
+ public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
+ registerAnnotationConfigProcessors(registry, null);
+ }
+
+ /**
+ * Register all relevant annotation post processors in the given registry.
+ * @param registry the registry to operate on
+ * @param source the configuration source element (already extracted)
+ * that this registration was triggered from. May be null.
+ * @return a Set of BeanDefinitionHolders, containing all bean definitions
+ * that have actually been registered by this call
+ */
+ public static Set registerAnnotationConfigProcessors(
+ BeanDefinitionRegistry registry, Object source) {
+
+ Set beanDefinitions = new LinkedHashSet(4);
+
+ // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
+ if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
+ RootBeanDefinition def = new RootBeanDefinition();
+ try {
+ ClassLoader cl = AnnotationConfigUtils.class.getClassLoader();
+ def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME));
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException(
+ "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
+ }
+ def.setSource(source);
+ def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ beanDefinitions.add(registerBeanPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
+ }
+
+ // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
+ if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
+ RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
+ def.setSource(source);
+ def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ beanDefinitions.add(registerBeanPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
+ }
+
+ if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
+ RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
+ def.setSource(source);
+ def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ beanDefinitions.add(registerBeanPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
+ }
+
+ if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
+ RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
+ def.setSource(source);
+ def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ beanDefinitions.add(registerBeanPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
+ }
+
+ return beanDefinitions;
+ }
+
+ private static BeanDefinitionHolder registerBeanPostProcessor(
+ BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
+
+ // Default infrastructure bean: lowest order value; role infrastructure.
+ definition.getPropertyValues().addPropertyValue("order", new Integer(Ordered.LOWEST_PRECEDENCE));
+ definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+
+ registry.registerBeanDefinition(beanName, definition);
+ return new BeanDefinitionHolder(definition, beanName);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java
new file mode 100644
index 00000000000..45104c5ed7f
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/AnnotationScopeMetadataResolver.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.lang.annotation.Annotation;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.util.Assert;
+
+/**
+ * A {@link ScopeMetadataResolver} implementation that (by default) checks for
+ * the presence of the {@link Scope} annotation on the bean class.
+ *
+ *
The exact type of annotation that is checked for is configurable via the
+ * {@link #setScopeAnnotationType(Class)} property.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see Scope
+ */
+public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
+
+ private Class extends Annotation> scopeAnnotationType = Scope.class;
+
+ private ScopedProxyMode scopedProxyMode;
+
+
+ /**
+ * Create a new instance of the AnnotationScopeMetadataResolver class.
+ * @see #AnnotationScopeMetadataResolver(ScopedProxyMode)
+ * @see ScopedProxyMode#NO
+ */
+ public AnnotationScopeMetadataResolver() {
+ this(ScopedProxyMode.NO);
+ }
+
+ /**
+ * Create a new instance of the AnnotationScopeMetadataResolver class.
+ * @param scopedProxyMode the desired scoped-proxy mode
+ */
+ public AnnotationScopeMetadataResolver(ScopedProxyMode scopedProxyMode) {
+ Assert.notNull(scopedProxyMode, "'scopedProxyMode' must not be null");
+ this.scopedProxyMode = scopedProxyMode;
+ }
+
+
+ /**
+ * Set the type of annotation that is checked for by this
+ * {@link AnnotationScopeMetadataResolver}.
+ * @param scopeAnnotationType the target annotation type
+ */
+ public void setScopeAnnotationType(Class extends Annotation> scopeAnnotationType) {
+ Assert.notNull(scopeAnnotationType, "'scopeAnnotationType' must not be null");
+ this.scopeAnnotationType = scopeAnnotationType;
+ }
+
+
+ public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
+ ScopeMetadata metadata = new ScopeMetadata();
+ if (definition instanceof AnnotatedBeanDefinition) {
+ AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
+ Map attributes =
+ annDef.getMetadata().getAnnotationAttributes(this.scopeAnnotationType.getName());
+ if (attributes != null) {
+ metadata.setScopeName((String) attributes.get("value"));
+ }
+ if (!metadata.getScopeName().equals(BeanDefinition.SCOPE_SINGLETON)) {
+ metadata.setScopedProxyMode(this.scopedProxyMode);
+ }
+ }
+ return metadata;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
new file mode 100644
index 00000000000..fdadb55ab9d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathBeanDefinitionScanner.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.springframework.aop.scope.ScopedProxyUtils;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionDefaults;
+import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanNameGenerator;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.util.Assert;
+import org.springframework.util.PatternMatchUtils;
+
+/**
+ * A bean definition scanner that detects bean candidates on the classpath,
+ * registering corresponding bean definitions with a given registry (BeanFactory
+ * or ApplicationContext).
+ *
+ *
Candidate classes are detected through configurable type filters. The
+ * default filters include classes that are annotated with Spring's
+ * {@link org.springframework.stereotype.Component @Component},
+ * {@link org.springframework.stereotype.Repository @Repository},
+ * {@link org.springframework.stereotype.Service @Service}, or
+ * {@link org.springframework.stereotype.Controller @Controller} stereotype.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see org.springframework.stereotype.Component
+ * @see org.springframework.stereotype.Repository
+ * @see org.springframework.stereotype.Service
+ * @see org.springframework.stereotype.Controller
+ */
+public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
+
+ private final BeanDefinitionRegistry registry;
+
+ private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();
+
+ private String[] autowireCandidatePatterns;
+
+ private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
+
+ private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
+
+ private boolean includeAnnotationConfig = true;
+
+
+ /**
+ * Create a new ClassPathBeanDefinitionScanner for the given bean factory.
+ * @param registry the BeanFactory to load bean definitions into,
+ * in the form of a BeanDefinitionRegistry
+ */
+ public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
+ this(registry, true);
+ }
+
+ /**
+ * Create a new ClassPathBeanDefinitionScanner for the given bean factory.
+ *
If the passed-in bean factory does not only implement the BeanDefinitionRegistry
+ * interface but also the ResourceLoader interface, it will be used as default
+ * ResourceLoader as well. This will usually be the case for
+ * {@link org.springframework.context.ApplicationContext} implementations.
+ *
If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
+ * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
+ * @param registry the BeanFactory to load bean definitions into,
+ * in the form of a BeanDefinitionRegistry
+ * @param useDefaultFilters whether to include the default filters for the
+ * {@link org.springframework.stereotype.Component @Component},
+ * {@link org.springframework.stereotype.Repository @Repository},
+ * {@link org.springframework.stereotype.Service @Service}, and
+ * {@link org.springframework.stereotype.Controller @Controller} stereotype
+ * annotations.
+ * @see #setResourceLoader
+ */
+ public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
+ super(useDefaultFilters);
+
+ Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
+ this.registry = registry;
+
+ // Determine ResourceLoader to use.
+ if (this.registry instanceof ResourceLoader) {
+ setResourceLoader((ResourceLoader) this.registry);
+ }
+ }
+
+
+ /**
+ * Return the BeanDefinitionRegistry that this scanner operates on.
+ */
+ public final BeanDefinitionRegistry getRegistry() {
+ return this.registry;
+ }
+
+ /**
+ * Set the defaults to use for detected beans.
+ * @see BeanDefinitionDefaults
+ */
+ public void setBeanDefinitionDefaults(BeanDefinitionDefaults beanDefinitionDefaults) {
+ this.beanDefinitionDefaults =
+ (beanDefinitionDefaults != null ? beanDefinitionDefaults : new BeanDefinitionDefaults());
+ }
+
+ /**
+ * Set the name-matching patterns for determining autowire candidates.
+ * @param autowireCandidatePatterns the patterns to match against
+ */
+ public void setAutowireCandidatePatterns(String[] autowireCandidatePatterns) {
+ this.autowireCandidatePatterns = autowireCandidatePatterns;
+ }
+
+ /**
+ * Set the BeanNameGenerator to use for detected bean classes.
+ *
Default is a {@link AnnotationBeanNameGenerator}.
+ */
+ public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
+ this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
+ }
+
+ /**
+ * Set the ScopeMetadataResolver to use for detected bean classes.
+ * Note that this will override any custom "scopedProxyMode" setting.
+ *
The default is an {@link AnnotationScopeMetadataResolver}.
+ * @see #setScopedProxyMode
+ */
+ public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
+ this.scopeMetadataResolver = scopeMetadataResolver;
+ }
+
+ /**
+ * Specify the proxy behavior for non-singleton scoped beans.
+ * Note that this will override any custom "scopeMetadataResolver" setting.
+ *
The default is {@link ScopedProxyMode#NO}.
+ * @see #setScopeMetadataResolver
+ */
+ public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
+ this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(scopedProxyMode);
+ }
+
+ /**
+ * Specify whether to register annotation config post-processors.
+ *
The default is to register the post-processors. Turn this off
+ * to be able to ignore the annotations or to process them differently.
+ */
+ public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) {
+ this.includeAnnotationConfig = includeAnnotationConfig;
+ }
+
+
+ /**
+ * Perform a scan within the specified base packages.
+ * @param basePackages the packages to check for annotated classes
+ * @return number of beans registered
+ */
+ public int scan(String... basePackages) {
+ int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
+
+ doScan(basePackages);
+
+ // Register annotation config processors, if necessary.
+ if (this.includeAnnotationConfig) {
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
+ }
+
+ return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
+ }
+
+ /**
+ * Perform a scan within the specified base packages,
+ * returning the registered bean definitions.
+ *
This method does not register an annotation config processor
+ * but rather leaves this up to the caller.
+ * @param basePackages the packages to check for annotated classes
+ * @return number of beans registered
+ */
+ protected Set doScan(String... basePackages) {
+ Set beanDefinitions = new LinkedHashSet();
+ for (int i = 0; i < basePackages.length; i++) {
+ Set candidates = findCandidateComponents(basePackages[i]);
+ for (BeanDefinition candidate : candidates) {
+ String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
+ if (candidate instanceof AbstractBeanDefinition) {
+ postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
+ }
+ ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
+ if (checkCandidate(beanName, candidate)) {
+ BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
+ definitionHolder = applyScope(definitionHolder, scopeMetadata);
+ beanDefinitions.add(definitionHolder);
+ registerBeanDefinition(definitionHolder, this.registry);
+ }
+ }
+ }
+ return beanDefinitions;
+ }
+
+ /**
+ * Apply further settings to the given bean definition,
+ * beyond the contents retrieved from scanning the component class.
+ * @param beanDefinition the scanned bean definition
+ * @param beanName the generated bean name for the given bean
+ */
+ protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
+ beanDefinition.applyDefaults(this.beanDefinitionDefaults);
+ if (this.autowireCandidatePatterns != null) {
+ beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
+ }
+ }
+
+ /**
+ * Register the specified bean with the given registry.
+ *
Can be overridden in subclasses, e.g. to adapt the registration
+ * process or to register further bean definitions for each scanned bean.
+ * @param definitionHolder the bean definition plus bean name for the bean
+ * @param registry the BeanDefinitionRegistry to register the bean with
+ */
+ protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
+ BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
+ }
+
+
+ /**
+ * Check the given candidate's bean name, determining whether the corresponding
+ * bean definition needs to be registered or conflicts with an existing definition.
+ * @param beanName the suggested name for the bean
+ * @param beanDefinition the corresponding bean definition
+ * @return true if the bean can be registered as-is;
+ * false if it should be skipped because there is an
+ * existing, compatible bean definition for the specified name
+ * @throws IllegalStateException if an existing, incompatible
+ * bean definition has been found for the specified name
+ */
+ protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
+ if (!this.registry.containsBeanDefinition(beanName)) {
+ return true;
+ }
+ BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
+ BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
+ if (originatingDef != null) {
+ existingDef = originatingDef;
+ }
+ if (isCompatible(beanDefinition, existingDef)) {
+ return false;
+ }
+ throw new IllegalStateException("Annotation-specified bean name '" + beanName +
+ "' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
+ "non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
+ }
+
+ /**
+ * Determine whether the given new bean definition is compatible with
+ * the given existing bean definition.
+ *
The default implementation simply considers them as compatible
+ * when the bean class name matches.
+ * @param newDefinition the new bean definition, originated from scanning
+ * @param existingDefinition the existing bean definition, potentially an
+ * explicitly defined one or a previously generated one from scanning
+ * @return whether the definitions are considered as compatible, with the
+ * new definition to be skipped in favor of the existing definition
+ */
+ protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
+ return (!(existingDefinition instanceof AnnotatedBeanDefinition) || // explicitly registered overriding bean
+ newDefinition.getSource().equals(existingDefinition.getSource()) || // scanned same file twice
+ newDefinition.equals(existingDefinition)); // scanned equivalent class twice
+ }
+
+ /**
+ * Apply the specified scope to the given bean definition.
+ * @param definitionHolder the bean definition to configure
+ * @param scopeMetadata the corresponding scope metadata
+ * @return the final bean definition to use (potentially a proxy)
+ */
+ private BeanDefinitionHolder applyScope(BeanDefinitionHolder definitionHolder, ScopeMetadata scopeMetadata) {
+ String scope = scopeMetadata.getScopeName();
+ ScopedProxyMode scopedProxyMode = scopeMetadata.getScopedProxyMode();
+ definitionHolder.getBeanDefinition().setScope(scope);
+ if (BeanDefinition.SCOPE_SINGLETON.equals(scope) || BeanDefinition.SCOPE_PROTOTYPE.equals(scope) ||
+ scopedProxyMode.equals(ScopedProxyMode.NO)) {
+ return definitionHolder;
+ }
+ boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
+ return ScopedProxyCreator.createScopedProxy(definitionHolder, this.registry, proxyTargetClass);
+ }
+
+
+ /**
+ * Inner factory class used to just introduce an AOP framework dependency
+ * when actually creating a scoped proxy.
+ */
+ private static class ScopedProxyCreator {
+
+ public static BeanDefinitionHolder createScopedProxy(
+ BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
+
+ return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
new file mode 100644
index 00000000000..e65ae74e295
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ClassPathScanningCandidateComponentProvider.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternUtils;
+import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.core.type.classreading.MetadataReaderFactory;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.core.type.filter.TypeFilter;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Controller;
+import org.springframework.stereotype.Repository;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.SystemPropertyUtils;
+
+/**
+ * A component provider that scans the classpath from a base package. It then
+ * applies exclude and include filters to the resulting classes to find candidates.
+ *
+ *
This implementation is based on Spring's
+ * {@link org.springframework.core.type.classreading.MetadataReader MetadataReader}
+ * facility, backed by an ASM {@link org.objectweb.asm.ClassReader ClassReader}.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @author Ramnivas Laddad
+ * @since 2.5
+ * @see org.springframework.core.type.classreading.MetadataReaderFactory
+ * @see org.springframework.core.type.AnnotationMetadata
+ * @see ScannedGenericBeanDefinition
+ */
+public class ClassPathScanningCandidateComponentProvider implements ResourceLoaderAware {
+
+ protected static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
+
+
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
+
+ private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
+
+ private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
+
+ private final List includeFilters = new LinkedList();
+
+ private final List excludeFilters = new LinkedList();
+
+
+ /**
+ * Create a ClassPathScanningCandidateComponentProvider.
+ * @param useDefaultFilters whether to register the default filters for the
+ * {@link Component @Component}, {@link Repository @Repository},
+ * {@link Service @Service}, and {@link Controller @Controller}
+ * stereotype annotations
+ * @see #registerDefaultFilters()
+ */
+ public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
+ if (useDefaultFilters) {
+ registerDefaultFilters();
+ }
+ }
+
+
+ /**
+ * Set the ResourceLoader to use for resource locations.
+ * This will typically be a ResourcePatternResolver implementation.
+ *
Default is PathMatchingResourcePatternResolver, also capable of
+ * resource pattern resolving through the ResourcePatternResolver interface.
+ * @see org.springframework.core.io.support.ResourcePatternResolver
+ * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
+ */
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
+ this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
+ }
+
+ /**
+ * Return the ResourceLoader that this component provider uses.
+ */
+ public final ResourceLoader getResourceLoader() {
+ return this.resourcePatternResolver;
+ }
+
+ /**
+ * Set the resource pattern to use when scanning the classpath.
+ * This value will be appended to each base package name.
+ * @see #findCandidateComponents(String)
+ * @see #DEFAULT_RESOURCE_PATTERN
+ */
+ public void setResourcePattern(String resourcePattern) {
+ Assert.notNull(resourcePattern, "'resourcePattern' must not be null");
+ this.resourcePattern = resourcePattern;
+ }
+
+ /**
+ * Add an include type filter to the end of the inclusion list.
+ */
+ public void addIncludeFilter(TypeFilter includeFilter) {
+ this.includeFilters.add(includeFilter);
+ }
+
+ /**
+ * Add an exclude type filter to the front of the exclusion list.
+ */
+ public void addExcludeFilter(TypeFilter excludeFilter) {
+ this.excludeFilters.add(0, excludeFilter);
+ }
+
+ /**
+ * Reset the configured type filters.
+ * @param useDefaultFilters whether to re-register the default filters for
+ * the {@link Component @Component}, {@link Repository @Repository},
+ * {@link Service @Service}, and {@link Controller @Controller}
+ * stereotype annotations
+ * @see #registerDefaultFilters()
+ */
+ public void resetFilters(boolean useDefaultFilters) {
+ this.includeFilters.clear();
+ this.excludeFilters.clear();
+ if (useDefaultFilters) {
+ registerDefaultFilters();
+ }
+ }
+
+ /**
+ * Register the default filter for {@link Component @Component}.
+ * This will implicitly register all annotations that have the
+ * {@link Component @Component} meta-annotation including the
+ * {@link Repository @Repository}, {@link Service @Service}, and
+ * {@link Controller @Controller} stereotype annotations.
+ */
+ protected void registerDefaultFilters() {
+ this.includeFilters.add(new AnnotationTypeFilter(Component.class));
+ }
+
+
+ /**
+ * Scan the class path for candidate components.
+ * @param basePackage the package to check for annotated classes
+ * @return a corresponding Set of autodetected bean definitions
+ */
+ public Set findCandidateComponents(String basePackage) {
+ Set candidates = new LinkedHashSet();
+ try {
+ String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
+ resolveBasePackage(basePackage) + "/" + this.resourcePattern;
+ Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
+ boolean traceEnabled = logger.isTraceEnabled();
+ boolean debugEnabled = logger.isDebugEnabled();
+ for (int i = 0; i < resources.length; i++) {
+ Resource resource = resources[i];
+ if (traceEnabled) {
+ logger.trace("Scanning " + resource);
+ }
+ if (resource.isReadable()) {
+ MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
+ if (isCandidateComponent(metadataReader)) {
+ ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
+ sbd.setResource(resource);
+ sbd.setSource(resource);
+ if (isCandidateComponent(sbd)) {
+ if (debugEnabled) {
+ logger.debug("Identified candidate component class: " + resource);
+ }
+ candidates.add(sbd);
+ }
+ else {
+ if (debugEnabled) {
+ logger.debug("Ignored because not a concrete top-level class: " + resource);
+ }
+ }
+ }
+ else {
+ if (traceEnabled) {
+ logger.trace("Ignored because not matching any filter: " + resource);
+ }
+ }
+ }
+ else {
+ if (traceEnabled) {
+ logger.trace("Ignored because not readable: " + resource);
+ }
+ }
+ }
+ }
+ catch (IOException ex) {
+ throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
+ }
+ return candidates;
+ }
+
+ /**
+ * Resolve the specified base package into a pattern specification for
+ * the package search path.
+ *
The default implementation resolves placeholders against system properties,
+ * and converts a "."-based package path to a "/"-based resource path.
+ * @param basePackage the base package as specified by the user
+ * @return the pattern specification to be used for package searching
+ */
+ protected String resolveBasePackage(String basePackage) {
+ return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
+ }
+
+ /**
+ * Determine whether the given class does not match any exclude filter
+ * and does match at least one include filter.
+ * @param metadataReader the ASM ClassReader for the class
+ * @return whether the class qualifies as a candidate component
+ */
+ protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
+ for (TypeFilter tf : this.excludeFilters) {
+ if (tf.match(metadataReader, this.metadataReaderFactory)) {
+ return false;
+ }
+ }
+ for (TypeFilter tf : this.includeFilters) {
+ if (tf.match(metadataReader, this.metadataReaderFactory)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determine whether the given bean definition qualifies as candidate.
+ *
The default implementation checks whether the class is concrete
+ * (i.e. not abstract and not an interface). Can be overridden in subclasses.
+ * @param beanDefinition the bean definition to check
+ * @return whether the bean definition qualifies as a candidate component
+ */
+ protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
+ return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
new file mode 100644
index 00000000000..af51a4a31bd
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/CommonAnnotationBeanPostProcessor.java
@@ -0,0 +1,712 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.annotation.Resource;
+import javax.ejb.EJB;
+import javax.xml.namespace.QName;
+import javax.xml.ws.Service;
+import javax.xml.ws.WebServiceClient;
+import javax.xml.ws.WebServiceRef;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
+import org.springframework.beans.factory.annotation.InjectionMetadata;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.Ordered;
+import org.springframework.jndi.support.SimpleJndiBeanFactory;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
+ * that supports common Java annotations out of the box, in particular the JSR-250
+ * annotations in the javax.annotation package. These common Java
+ * annotations are supported in many Java EE 5 technologies (e.g. JSF 1.2),
+ * as well as in Java 6's JAX-WS.
+ *
+ *
This post-processor includes support for the {@link javax.annotation.PostConstruct}
+ * and {@link javax.annotation.PreDestroy} annotations - as init annotation
+ * and destroy annotation, respectively - through inheriting from
+ * {@link InitDestroyAnnotationBeanPostProcessor} with pre-configured annotation types.
+ *
+ *
The central element is the {@link javax.annotation.Resource} annotation
+ * for annotation-driven injection of named beans, by default from the containing
+ * Spring BeanFactory, with only mappedName references resolved in JNDI.
+ * The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups
+ * equivalent to standard Java EE 5 resource injection for name references
+ * and default names as well. The target beans can be simple POJOs, with no special
+ * requirements other than the type having to match.
+ *
+ *
The JAX-WS {@link javax.xml.ws.WebServiceRef} annotation is supported too,
+ * analogous to {@link javax.annotation.Resource} but with the capability of creating
+ * specific JAX-WS service endpoints. This may either point to an explicitly defined
+ * resource by name or operate on a locally specified JAX-WS service class. Finally,
+ * this post-processor also supports the EJB 3 {@link javax.ejb.EJB} annotation,
+ * analogous to {@link javax.annotation.Resource} as well, with the capability to
+ * specify both a local bean name and a global JNDI name for fallback retrieval.
+ * The target beans can be plain POJOs as well as EJB 3 Session Beans in this case.
+ *
+ *
The common annotations supported by this post-processor are available
+ * in Java 6 (JDK 1.6) as well as in Java EE 5 (which provides a standalone jar for
+ * its common annotations as well, allowing for use in any Java 5 based application).
+ * Hence, this post-processor works out of the box on JDK 1.6, and requires the
+ * JSR-250 API jar (and optionally the JAX-WS API jar and/or the EJB 3 API jar)
+ * to be added to the classpath on JDK 1.5 (when running outside of Java EE 5).
+ *
+ *
For default usage, resolving resource names as Spring bean names,
+ * simply define the following in your application context:
+ *
+ *
+ *
+ * For direct JNDI access, resolving resource names as JNDI resource references
+ * within the Java EE application's "java:comp/env/" namespace, use the following:
+ *
+ *
+ *
+ * mappedName references will always be resolved in JNDI,
+ * allowing for global JNDI names (including "java:" prefix) as well. The
+ * "alwaysUseJndiLookup" flag just affects name references and
+ * default names (inferred from the field name / property name).
+ *
+ *
NOTE: A default CommonAnnotationBeanPostProcessor will be registered
+ * by the "context:annotation-config" and "context:component-scan" XML tags.
+ * Remove or turn off the default annotation configuration there if you intend
+ * to specify a custom CommonAnnotationBeanPostProcessor bean definition!
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see #setAlwaysUseJndiLookup
+ * @see #setResourceFactory
+ * @see org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor
+ * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
+ */
+public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
+ implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
+
+ private static Class extends Annotation> webServiceRefClass = null;
+
+ private static Class extends Annotation> ejbRefClass = null;
+
+ static {
+ try {
+ webServiceRefClass = ClassUtils.forName("javax.xml.ws.WebServiceRef",
+ CommonAnnotationBeanPostProcessor.class.getClassLoader());
+ }
+ catch (ClassNotFoundException ex) {
+ webServiceRefClass = null;
+ }
+ try {
+ ejbRefClass = ClassUtils.forName("javax.ejb.EJB",
+ CommonAnnotationBeanPostProcessor.class.getClassLoader());
+ }
+ catch (ClassNotFoundException ex) {
+ ejbRefClass = null;
+ }
+ }
+
+
+ private final Set ignoredResourceTypes = new HashSet(1);
+
+ private boolean fallbackToDefaultTypeMatch = true;
+
+ private boolean alwaysUseJndiLookup = false;
+
+ private transient BeanFactory jndiFactory = new SimpleJndiBeanFactory();
+
+ private transient BeanFactory resourceFactory;
+
+ private transient BeanFactory beanFactory;
+
+ private transient final Map, InjectionMetadata> injectionMetadataCache =
+ new ConcurrentHashMap, InjectionMetadata>();
+
+
+ /**
+ * Create a new CommonAnnotationBeanPostProcessor,
+ * with the init and destroy annotation types set to
+ * {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy},
+ * respectively.
+ */
+ public CommonAnnotationBeanPostProcessor() {
+ setOrder(Ordered.LOWEST_PRECEDENCE - 3);
+ setInitAnnotationType(PostConstruct.class);
+ setDestroyAnnotationType(PreDestroy.class);
+ ignoreResourceType("javax.xml.ws.WebServiceContext");
+ }
+
+
+ /**
+ * Ignore the given resource type when resolving @Resource
+ * annotations.
+ *
By default, the javax.xml.ws.WebServiceContext interface
+ * will be ignored, since it will be resolved by the JAX-WS runtime.
+ * @param resourceType the resource type to ignore
+ */
+ public void ignoreResourceType(String resourceType) {
+ Assert.notNull(resourceType, "Ignored resource type must not be null");
+ this.ignoredResourceTypes.add(resourceType);
+ }
+
+ /**
+ * Set whether to allow a fallback to a type match if no explicit name has been
+ * specified. The default name (i.e. the field name or bean property name) will
+ * still be checked first; if a bean of that name exists, it will be taken.
+ * However, if no bean of that name exists, a by-type resolution of the
+ * dependency will be attempted if this flag is "true".
+ *
Default is "true". Switch this flag to "false" in order to enforce a
+ * by-name lookup in all cases, throwing an exception in case of no name match.
+ * @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency
+ */
+ public void setFallbackToDefaultTypeMatch(boolean fallbackToDefaultTypeMatch) {
+ this.fallbackToDefaultTypeMatch = fallbackToDefaultTypeMatch;
+ }
+
+ /**
+ * Set whether to always use JNDI lookups equivalent to standard Java EE 5 resource
+ * injection, even for name attributes and default names.
+ *
Default is "false": Resource names are used for Spring bean lookups in the
+ * containing BeanFactory; only mappedName attributes point directly
+ * into JNDI. Switch this flag to "true" for enforcing Java EE style JNDI lookups
+ * in any case, even for name attributes and default names.
+ * @see #setJndiFactory
+ * @see #setResourceFactory
+ */
+ public void setAlwaysUseJndiLookup(boolean alwaysUseJndiLookup) {
+ this.alwaysUseJndiLookup = alwaysUseJndiLookup;
+ }
+
+ /**
+ * Specify the factory for objects to be injected into @Resource /
+ * @WebServiceRef / @EJB annotated fields and setter methods,
+ * for mappedName attributes that point directly into JNDI.
+ * This factory will also be used if "alwaysUseJndiLookup" is set to "true" in order
+ * to enforce JNDI lookups even for name attributes and default names.
+ *
The default is a {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
+ * for JNDI lookup behavior equivalent to standard Java EE 5 resource injection.
+ * @see #setResourceFactory
+ * @see #setAlwaysUseJndiLookup
+ */
+ public void setJndiFactory(BeanFactory jndiFactory) {
+ Assert.notNull(jndiFactory, "BeanFactory must not be null");
+ this.jndiFactory = jndiFactory;
+ }
+
+ /**
+ * Specify the factory for objects to be injected into @Resource /
+ * @WebServiceRef / @EJB annotated fields and setter methods,
+ * for name attributes and default names.
+ *
The default is the BeanFactory that this post-processor is defined in,
+ * if any, looking up resource names as Spring bean names. Specify the resource
+ * factory explicitly for programmatic usage of this post-processor.
+ *
Specifying Spring's {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
+ * leads to JNDI lookup behavior equivalent to standard Java EE 5 resource injection,
+ * even for name attributes and default names. This is the same behavior
+ * that the "alwaysUseJndiLookup" flag enables.
+ * @see #setAlwaysUseJndiLookup
+ */
+ public void setResourceFactory(BeanFactory resourceFactory) {
+ Assert.notNull(resourceFactory, "BeanFactory must not be null");
+ this.resourceFactory = resourceFactory;
+ }
+
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ Assert.notNull(beanFactory, "BeanFactory must not be null");
+ this.beanFactory = beanFactory;
+ if (this.resourceFactory == null) {
+ this.resourceFactory = beanFactory;
+ }
+ }
+
+
+ public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
+ super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
+ if (beanType != null) {
+ InjectionMetadata metadata = findResourceMetadata(beanType);
+ metadata.checkConfigMembers(beanDefinition);
+ }
+ }
+
+ public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
+ return null;
+ }
+
+ public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
+ InjectionMetadata metadata = findResourceMetadata(bean.getClass());
+ try {
+ metadata.injectFields(bean, beanName);
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(beanName, "Injection of resource fields failed", ex);
+ }
+ return true;
+ }
+
+ public PropertyValues postProcessPropertyValues(
+ PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
+
+ InjectionMetadata metadata = findResourceMetadata(bean.getClass());
+ try {
+ metadata.injectMethods(bean, beanName, pvs);
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(beanName, "Injection of resource methods failed", ex);
+ }
+ return pvs;
+ }
+
+
+ private InjectionMetadata findResourceMetadata(final Class clazz) {
+ // Quick check on the concurrent map first, with minimal locking.
+ InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
+ if (metadata == null) {
+ synchronized (this.injectionMetadataCache) {
+ metadata = this.injectionMetadataCache.get(clazz);
+ if (metadata == null) {
+ final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
+ ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
+ public void doWith(Field field) {
+ if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
+ }
+ newMetadata.addInjectedField(new WebServiceRefElement(field, null));
+ }
+ else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ throw new IllegalStateException("@EJB annotation is not supported on static fields");
+ }
+ newMetadata.addInjectedField(new EjbRefElement(field, null));
+ }
+ else if (field.isAnnotationPresent(Resource.class)) {
+ if (Modifier.isStatic(field.getModifiers())) {
+ throw new IllegalStateException("@Resource annotation is not supported on static fields");
+ }
+ if (!ignoredResourceTypes.contains(field.getType().getName())) {
+ newMetadata.addInjectedField(new ResourceElement(field, null));
+ }
+ }
+ }
+ });
+ ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
+ public void doWith(Method method) {
+ if (webServiceRefClass != null && method.isAnnotationPresent(webServiceRefClass) &&
+ method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
+ if (Modifier.isStatic(method.getModifiers())) {
+ throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
+ }
+ if (method.getParameterTypes().length != 1) {
+ throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
+ }
+ PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
+ newMetadata.addInjectedMethod(new WebServiceRefElement(method, pd));
+ }
+ else if (ejbRefClass != null && method.isAnnotationPresent(ejbRefClass) &&
+ method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
+ if (Modifier.isStatic(method.getModifiers())) {
+ throw new IllegalStateException("@EJB annotation is not supported on static methods");
+ }
+ if (method.getParameterTypes().length != 1) {
+ throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
+ }
+ PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
+ newMetadata.addInjectedMethod(new EjbRefElement(method, pd));
+ }
+ else if (method.isAnnotationPresent(Resource.class) &&
+ method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
+ if (Modifier.isStatic(method.getModifiers())) {
+ throw new IllegalStateException("@Resource annotation is not supported on static methods");
+ }
+ Class[] paramTypes = method.getParameterTypes();
+ if (paramTypes.length != 1) {
+ throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
+ }
+ if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
+ PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
+ newMetadata.addInjectedMethod(new ResourceElement(method, pd));
+ }
+ }
+ }
+ });
+ metadata = newMetadata;
+ this.injectionMetadataCache.put(clazz, metadata);
+ }
+ }
+ }
+ return metadata;
+ }
+
+ /**
+ * Obtain the resource object for the given name and type.
+ * @param element the descriptor for the annotated field/method
+ * @param requestingBeanName the name of the requesting bean
+ * @return the resource object (never null)
+ * @throws BeansException if we failed to obtain the target resource
+ */
+ protected Object getResource(LookupElement element, String requestingBeanName) throws BeansException {
+ if (StringUtils.hasLength(element.mappedName)) {
+ return this.jndiFactory.getBean(element.mappedName, element.lookupType);
+ }
+ if (this.alwaysUseJndiLookup) {
+ return this.jndiFactory.getBean(element.name, element.lookupType);
+ }
+ if (this.resourceFactory == null) {
+ throw new NoSuchBeanDefinitionException(element.lookupType,
+ "No resource factory configured - specify the 'resourceFactory' property");
+ }
+ return autowireResource(this.resourceFactory, element, requestingBeanName);
+ }
+
+ /**
+ * Obtain a resource object for the given name and type through autowiring
+ * based on the given factory.
+ * @param factory the factory to autowire against
+ * @param element the descriptor for the annotated field/method
+ * @param requestingBeanName the name of the requesting bean
+ * @return the resource object (never null)
+ * @throws BeansException if we failed to obtain the target resource
+ */
+ protected Object autowireResource(BeanFactory factory, LookupElement element, String requestingBeanName)
+ throws BeansException {
+
+ Object resource = null;
+ Set autowiredBeanNames = null;
+ String name = element.name;
+
+ if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
+ factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
+ autowiredBeanNames = new LinkedHashSet();
+ resource = ((AutowireCapableBeanFactory) factory).resolveDependency(
+ element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
+ }
+ else {
+ resource = factory.getBean(name, element.lookupType);
+ autowiredBeanNames = Collections.singleton(name);
+ }
+
+ if (factory instanceof ConfigurableBeanFactory) {
+ ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
+ for (String autowiredBeanName : autowiredBeanNames) {
+ beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
+ }
+ }
+
+ return resource;
+ }
+
+
+ /**
+ * Class representing generic injection information about an annotated field
+ * or setter method, supporting @Resource and related annotations.
+ */
+ protected abstract class LookupElement extends InjectionMetadata.InjectedElement {
+
+ protected String name;
+
+ protected boolean isDefaultName = false;
+
+ protected Class> lookupType;
+
+ protected String mappedName;
+
+ public LookupElement(Member member, PropertyDescriptor pd) {
+ super(member, pd);
+ initAnnotation((AnnotatedElement) member);
+ }
+
+ protected abstract void initAnnotation(AnnotatedElement ae);
+
+ /**
+ * Return the resource name for the lookup.
+ */
+ public final String getName() {
+ return this.name;
+ }
+
+ /**
+ * Return the desired type for the lookup.
+ */
+ public final Class> getLookupType() {
+ return this.lookupType;
+ }
+
+ /**
+ * Build a DependencyDescriptor for the underlying field/method.
+ */
+ public final DependencyDescriptor getDependencyDescriptor() {
+ if (this.isField) {
+ return new LookupDependencyDescriptor((Field) this.member, this.lookupType);
+ }
+ else {
+ return new LookupDependencyDescriptor((Method) this.member, this.lookupType);
+ }
+ }
+ }
+
+
+ /**
+ * Class representing injection information about an annotated field
+ * or setter method, supporting the @Resource annotation.
+ */
+ private class ResourceElement extends LookupElement {
+
+ protected boolean shareable = true;
+
+ public ResourceElement(Member member, PropertyDescriptor pd) {
+ super(member, pd);
+ }
+
+ protected void initAnnotation(AnnotatedElement ae) {
+ Resource resource = ae.getAnnotation(Resource.class);
+ String resourceName = resource.name();
+ Class resourceType = resource.type();
+ this.isDefaultName = !StringUtils.hasLength(resourceName);
+ if (this.isDefaultName) {
+ resourceName = this.member.getName();
+ if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
+ resourceName = Introspector.decapitalize(resourceName.substring(3));
+ }
+ }
+ if (resourceType != null && !Object.class.equals(resourceType)) {
+ checkResourceType(resourceType);
+ }
+ else {
+ // No resource type specified... check field/method.
+ resourceType = getResourceType();
+ }
+ this.name = resourceName;
+ this.lookupType = resourceType;
+ this.mappedName = resource.mappedName();
+ this.shareable = resource.shareable();
+ }
+
+ @Override
+ protected Object getResourceToInject(Object target, String requestingBeanName) {
+ return getResource(this, requestingBeanName);
+ }
+ }
+
+
+ /**
+ * Class representing injection information about an annotated field
+ * or setter method, supporting the @WebServiceRef annotation.
+ */
+ private class WebServiceRefElement extends LookupElement {
+
+ private Class elementType;
+
+ private String wsdlLocation;
+
+ public WebServiceRefElement(Member member, PropertyDescriptor pd) {
+ super(member, pd);
+ }
+
+ protected void initAnnotation(AnnotatedElement ae) {
+ WebServiceRef resource = ae.getAnnotation(WebServiceRef.class);
+ String resourceName = resource.name();
+ Class resourceType = resource.type();
+ this.isDefaultName = !StringUtils.hasLength(resourceName);
+ if (this.isDefaultName) {
+ resourceName = this.member.getName();
+ if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
+ resourceName = Introspector.decapitalize(resourceName.substring(3));
+ }
+ }
+ if (resourceType != null && !Object.class.equals(resourceType)) {
+ checkResourceType(resourceType);
+ }
+ else {
+ // No resource type specified... check field/method.
+ resourceType = getResourceType();
+ }
+ this.name = resourceName;
+ this.elementType = resourceType;
+ if (Service.class.isAssignableFrom(resourceType)) {
+ this.lookupType = resourceType;
+ }
+ else {
+ this.lookupType = (!Object.class.equals(resource.value()) ? resource.value() : Service.class);
+ }
+ this.mappedName = resource.mappedName();
+ this.wsdlLocation = resource.wsdlLocation();
+ }
+
+ @Override
+ protected Object getResourceToInject(Object target, String requestingBeanName) {
+ Service service = null;
+ try {
+ service = (Service) getResource(this, requestingBeanName);
+ }
+ catch (NoSuchBeanDefinitionException notFound) {
+ // Service to be created through generated class.
+ if (Service.class.equals(this.lookupType)) {
+ throw new IllegalStateException("No resource with name '" + this.name + "' found in context, " +
+ "and no specific JAX-WS Service subclass specified. The typical solution is to either specify " +
+ "a LocalJaxWsServiceFactoryBean with the given name or to specify the (generated) Service " +
+ "subclass as @WebServiceRef(...) value.");
+ }
+ if (StringUtils.hasLength(this.wsdlLocation)) {
+ try {
+ Constructor ctor = this.lookupType.getConstructor(new Class[] {URL.class, QName.class});
+ WebServiceClient clientAnn = this.lookupType.getAnnotation(WebServiceClient.class);
+ if (clientAnn == null) {
+ throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() +
+ "] does not carry a WebServiceClient annotation");
+ }
+ service = (Service) BeanUtils.instantiateClass(ctor,
+ new Object[] {new URL(this.wsdlLocation), new QName(clientAnn.targetNamespace(), clientAnn.name())});
+ }
+ catch (NoSuchMethodException ex) {
+ throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() +
+ "] does not have a (URL, QName) constructor. Cannot apply specified WSDL location [" +
+ this.wsdlLocation + "].");
+ }
+ catch (MalformedURLException ex) {
+ throw new IllegalArgumentException(
+ "Specified WSDL location [" + this.wsdlLocation + "] isn't a valid URL");
+ }
+ }
+ else {
+ service = (Service) BeanUtils.instantiateClass(this.lookupType);
+ }
+ }
+ return service.getPort(this.elementType);
+ }
+ }
+
+
+ /**
+ * Class representing injection information about an annotated field
+ * or setter method, supporting the @EJB annotation.
+ */
+ private class EjbRefElement extends LookupElement {
+
+ private String beanName;
+
+ public EjbRefElement(Member member, PropertyDescriptor pd) {
+ super(member, pd);
+ }
+
+ protected void initAnnotation(AnnotatedElement ae) {
+ EJB resource = ae.getAnnotation(EJB.class);
+ String resourceBeanName = resource.beanName();
+ String resourceName = resource.name();
+ this.isDefaultName = !StringUtils.hasLength(resourceName);
+ if (this.isDefaultName) {
+ resourceName = this.member.getName();
+ if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
+ resourceName = Introspector.decapitalize(resourceName.substring(3));
+ }
+ }
+ Class resourceType = resource.beanInterface();
+ if (resourceType != null && !Object.class.equals(resourceType)) {
+ checkResourceType(resourceType);
+ }
+ else {
+ // No resource type specified... check field/method.
+ resourceType = getResourceType();
+ }
+ this.beanName = resourceBeanName;
+ this.name = resourceName;
+ this.lookupType = resourceType;
+ this.mappedName = resource.mappedName();
+ }
+
+ @Override
+ protected Object getResourceToInject(Object target, String requestingBeanName) {
+ if (StringUtils.hasLength(this.beanName)) {
+ if (beanFactory != null && beanFactory.containsBean(this.beanName)) {
+ // Local match found for explicitly specified local bean name.
+ Object bean = beanFactory.getBean(this.beanName, this.lookupType);
+ if (beanFactory instanceof ConfigurableBeanFactory) {
+ ((ConfigurableBeanFactory) beanFactory).registerDependentBean(this.beanName, requestingBeanName);
+ }
+ return bean;
+ }
+ else if (this.isDefaultName && !StringUtils.hasLength(this.mappedName)) {
+ throw new NoSuchBeanDefinitionException(this.beanName,
+ "Cannot resolve 'beanName' in local BeanFactory. Consider specifying a general 'name' value instead.");
+ }
+ }
+ // JNDI name lookup - may still go to a local BeanFactory.
+ return getResource(this, requestingBeanName);
+ }
+ }
+
+
+ /**
+ * Extension of the DependencyDescriptor class,
+ * overriding the dependency type with the specified resource type.
+ */
+ private static class LookupDependencyDescriptor extends DependencyDescriptor {
+
+ private final Class lookupType;
+
+ public LookupDependencyDescriptor(Field field, Class lookupType) {
+ super(field, true);
+ this.lookupType = lookupType;
+ }
+
+ public LookupDependencyDescriptor(Method method, Class lookupType) {
+ super(new MethodParameter(method, 0), true);
+ this.lookupType = lookupType;
+ }
+
+ public Class getDependencyType() {
+ return this.lookupType;
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanBeanDefinitionParser.java
new file mode 100644
index 00000000000..de62f4ed0b9
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ComponentScanBeanDefinitionParser.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.lang.annotation.Annotation;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
+import org.springframework.beans.factory.support.BeanNameGenerator;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.beans.factory.xml.XmlReaderContext;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.core.type.filter.AspectJTypeFilter;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.core.type.filter.RegexPatternTypeFilter;
+import org.springframework.core.type.filter.TypeFilter;
+import org.springframework.util.StringUtils;
+
+/**
+ * Parser for the <context:component-scan/> element.
+ *
+ * @author Mark Fisher
+ * @author Ramnivas Laddad
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
+
+ private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
+
+ private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";
+
+ private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";
+
+ private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
+
+ private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
+
+ private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
+
+ private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";
+
+ private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";
+
+ private static final String INCLUDE_FILTER_ELEMENT = "include-filter";
+
+ private static final String FILTER_TYPE_ATTRIBUTE = "type";
+
+ private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
+
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ String[] basePackages =
+ StringUtils.commaDelimitedListToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE));
+
+ // Actually scan for bean definitions and register them.
+ ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
+ Set beanDefinitions = scanner.doScan(basePackages);
+ registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
+
+ return null;
+ }
+
+ protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
+ XmlReaderContext readerContext = parserContext.getReaderContext();
+
+ boolean useDefaultFilters = true;
+ if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
+ useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
+ }
+
+ // Delegate bean definition registration to scanner class.
+ ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
+ scanner.setResourceLoader(readerContext.getResourceLoader());
+ scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
+ scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
+
+ if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
+ scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
+ }
+
+ try {
+ parseBeanNameGenerator(element, scanner);
+ }
+ catch (Exception ex) {
+ readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
+ }
+
+ try {
+ parseScope(element, scanner);
+ }
+ catch (Exception ex) {
+ readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
+ }
+
+ parseTypeFilters(element, scanner, readerContext);
+
+ return scanner;
+ }
+
+ protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
+ return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters);
+ }
+
+ protected void registerComponents(
+ XmlReaderContext readerContext, Set beanDefinitions, Element element) {
+
+ Object source = readerContext.extractSource(element);
+ CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
+
+ for (Iterator it = beanDefinitions.iterator(); it.hasNext();) {
+ BeanDefinitionHolder beanDefHolder = (BeanDefinitionHolder) it.next();
+ compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
+ }
+
+ // Register annotation config processors, if necessary.
+ boolean annotationConfig = true;
+ if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
+ annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
+ }
+ if (annotationConfig) {
+ Set processorDefinitions =
+ AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
+ for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
+ compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
+ }
+ }
+
+ readerContext.fireComponentRegistered(compositeDef);
+ }
+
+ protected void parseBeanNameGenerator(Element element, ClassPathBeanDefinitionScanner scanner) {
+ if (element.hasAttribute(NAME_GENERATOR_ATTRIBUTE)) {
+ BeanNameGenerator beanNameGenerator = (BeanNameGenerator) instantiateUserDefinedStrategy(
+ element.getAttribute(NAME_GENERATOR_ATTRIBUTE), BeanNameGenerator.class,
+ scanner.getResourceLoader().getClassLoader());
+ scanner.setBeanNameGenerator(beanNameGenerator);
+ }
+ }
+
+ protected void parseScope(Element element, ClassPathBeanDefinitionScanner scanner) {
+ // Register ScopeMetadataResolver if class name provided.
+ if (element.hasAttribute(SCOPE_RESOLVER_ATTRIBUTE)) {
+ if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
+ throw new IllegalArgumentException(
+ "Cannot define both 'scope-resolver' and 'scoped-proxy' on tag");
+ }
+ ScopeMetadataResolver scopeMetadataResolver = (ScopeMetadataResolver) instantiateUserDefinedStrategy(
+ element.getAttribute(SCOPE_RESOLVER_ATTRIBUTE), ScopeMetadataResolver.class,
+ scanner.getResourceLoader().getClassLoader());
+ scanner.setScopeMetadataResolver(scopeMetadataResolver);
+ }
+
+ if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
+ String mode = element.getAttribute(SCOPED_PROXY_ATTRIBUTE);
+ if ("targetClass".equals(mode)) {
+ scanner.setScopedProxyMode(ScopedProxyMode.TARGET_CLASS);
+ }
+ else if ("interfaces".equals(mode)) {
+ scanner.setScopedProxyMode(ScopedProxyMode.INTERFACES);
+ }
+ else if ("no".equals(mode)) {
+ scanner.setScopedProxyMode(ScopedProxyMode.NO);
+ }
+ else {
+ throw new IllegalArgumentException("scoped-proxy only supports 'no', 'interfaces' and 'targetClass'");
+ }
+ }
+ }
+
+ protected void parseTypeFilters(
+ Element element, ClassPathBeanDefinitionScanner scanner, XmlReaderContext readerContext) {
+
+ // Parse exclude and include filter elements.
+ ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
+ NodeList nodeList = element.getChildNodes();
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node node = nodeList.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ String localName = node.getLocalName();
+ try {
+ if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
+ TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
+ scanner.addIncludeFilter(typeFilter);
+ }
+ else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
+ TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
+ scanner.addExcludeFilter(typeFilter);
+ }
+ }
+ catch (Exception ex) {
+ readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) {
+ String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
+ String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
+ try {
+ if ("annotation".equals(filterType)) {
+ return new AnnotationTypeFilter((Class) classLoader.loadClass(expression));
+ }
+ else if ("assignable".equals(filterType)) {
+ return new AssignableTypeFilter(classLoader.loadClass(expression));
+ }
+ else if ("aspectj".equals(filterType)) {
+ return new AspectJTypeFilter(expression, classLoader);
+ }
+ else if ("regex".equals(filterType)) {
+ return new RegexPatternTypeFilter(Pattern.compile(expression));
+ }
+ else if ("custom".equals(filterType)) {
+ Class filterClass = classLoader.loadClass(expression);
+ if (!TypeFilter.class.isAssignableFrom(filterClass)) {
+ throw new IllegalArgumentException(
+ "Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
+ }
+ return (TypeFilter) BeanUtils.instantiateClass(filterClass);
+ }
+ else {
+ throw new IllegalArgumentException("Unsupported filter type: " + filterType);
+ }
+ }
+ catch (ClassNotFoundException ex) {
+ throw new FatalBeanException("Type filter class not found: " + expression, ex);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object instantiateUserDefinedStrategy(String className, Class strategyType, ClassLoader classLoader) {
+ Object result = null;
+ try {
+ result = classLoader.loadClass(className).newInstance();
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalArgumentException("Class [" + className + "] for strategy [" +
+ strategyType.getName() + "] not found", ex);
+ }
+ catch (Exception ex) {
+ throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" +
+ strategyType.getName() + "]. A zero-argument constructor is required", ex);
+ }
+
+ if (!strategyType.isAssignableFrom(result.getClass())) {
+ throw new IllegalArgumentException("Provided class name must be an implementation of " + strategyType);
+ }
+ return result;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/FilterType.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/FilterType.java
new file mode 100644
index 00000000000..6b63b9a0c33
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/FilterType.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+/**
+ * Enumeration of the valid type filters to be added for annotation-driven configuration.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+public enum FilterType {
+
+ ANNOTATION,
+ ASSIGNABLE_TYPE,
+ ASPECTJ_PATTERN,
+ REGEX_PATTERN,
+ CUSTOM
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java
new file mode 100644
index 00000000000..22d918c8870
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScannedGenericBeanDefinition.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.core.type.classreading.MetadataReader;
+import org.springframework.util.Assert;
+
+/**
+ * Extension of the {@link org.springframework.beans.factory.support.GenericBeanDefinition}
+ * class, based on an ASM ClassReader, with support for annotation metadata exposed
+ * through the {@link AnnotatedBeanDefinition} interface.
+ *
+ *
This class does not load the bean Class early.
+ * It rather retrieves all relevant metadata from the ".class" file itself,
+ * parsed with the ASM ClassReader.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see #getMetadata()
+ * @see #getBeanClassName()
+ * @see org.springframework.core.type.classreading.MetadataReaderFactory
+ */
+public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
+
+ private final AnnotationMetadata metadata;
+
+
+ /**
+ * Create a new ScannedGenericBeanDefinition for the class that the
+ * given MetadataReader describes.
+ * @param metadataReader the MetadataReader for the scanned target class
+ */
+ public ScannedGenericBeanDefinition(MetadataReader metadataReader) {
+ Assert.notNull(metadataReader, "MetadataReader must not be null");
+ this.metadata = metadataReader.getAnnotationMetadata();
+ setBeanClassName(this.metadata.getClassName());
+ }
+
+
+ public final AnnotationMetadata getMetadata() {
+ return this.metadata;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
new file mode 100644
index 00000000000..951ab8b80e6
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Scope.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+
+/**
+ * Indicates the name of a scope to use for instances of the annotated class.
+ *
+ *
In this context, scope means the lifecycle of an instance, such as
+ * 'singleton', 'prototype', and so forth.
+ *
+ * @author Mark Fisher
+ * @since 2.5
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Scope {
+
+ /**
+ * Specifies the scope to use for instances of the annotated class.
+ * @return the desired scope
+ */
+ String value() default BeanDefinition.SCOPE_SINGLETON;
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadata.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadata.java
new file mode 100644
index 00000000000..65e8ed0c20c
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadata.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+
+/**
+ * Describes scope characteristics for a Spring-managed bean including the scope
+ * name and the scoped-proxy behavior.
+ *
+ *
The default scope is "singleton", and the default is to not create
+ * scoped-proxies.
+ *
+ * @author Mark Fisher
+ * @since 2.5
+ * @see ScopeMetadataResolver
+ * @see ScopedProxyMode
+ */
+public class ScopeMetadata {
+
+ private String scopeName = BeanDefinition.SCOPE_SINGLETON;
+
+ private ScopedProxyMode scopedProxyMode = ScopedProxyMode.NO;
+
+
+ /**
+ * Get the name of the scope.
+ * @return said scope name
+ */
+ public String getScopeName() {
+ return scopeName;
+ }
+
+ /**
+ * Set the name of the scope.
+ * @param scopeName said scope name
+ */
+ public void setScopeName(String scopeName) {
+ this.scopeName = scopeName;
+ }
+
+ /**
+ * Get the proxy-mode to be applied to the scoped instance.
+ * @return said scoped-proxy mode
+ */
+ public ScopedProxyMode getScopedProxyMode() {
+ return scopedProxyMode;
+ }
+
+ /**
+ * Set the proxy-mode to be applied to the scoped instance.
+ * @param scopedProxyMode said scoped-proxy mode
+ */
+ public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
+ this.scopedProxyMode = scopedProxyMode;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadataResolver.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadataResolver.java
new file mode 100644
index 00000000000..d25609e6581
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopeMetadataResolver.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+
+/**
+ * Strategy interface for resolving the scope of bean definitions.
+ *
+ * @author Mark Fisher
+ * @since 2.5
+ * @see Scope
+ */
+public interface ScopeMetadataResolver {
+
+ /**
+ * Resolve the {@link ScopeMetadata} appropriate to the supplied
+ * bean definition.
+ *
Implementations can of course use any strategy they like to
+ * determine the scope metadata, but some implementations that spring
+ * immediately to mind might be to use source level annotations
+ * present on {@link BeanDefinition#getBeanClassName() the class} of the
+ * supplied definition, or to use metadata present in the
+ * {@link BeanDefinition#attributeNames()} of the supplied definition.
+ * @param definition the target bean definition
+ * @return the relevant scope metadata; never null
+ */
+ ScopeMetadata resolveScopeMetadata(BeanDefinition definition);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopedProxyMode.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopedProxyMode.java
new file mode 100644
index 00000000000..771026eb478
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ScopedProxyMode.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.annotation;
+
+/**
+ * Enumerates the various scoped-proxy options.
+ *
+ *
For a fuller discussion of exactly what a scoped-proxy is, see that
+ * section of the Spring reference documentation entitled 'Scoped beans as
+ * dependencies'.
+ *
+ * @author Mark Fisher
+ * @since 2.5
+ * @see ScopeMetadata
+ */
+public enum ScopedProxyMode {
+
+ /**
+ * Do not create a scoped proxy.
+ *
This proxy-mode is not typically useful when used with a
+ * non-singleton scoped instance, which should favor the use of the
+ * {@link #INTERFACES} or {@link #TARGET_CLASS} proxy-modes instead if it
+ * is to be used as a dependency.
+ */
+ NO,
+
+ /**
+ * Create a JDK dynamic proxy implementing all interfaces exposed by
+ * the class of the target object.
+ */
+ INTERFACES,
+
+ /**
+ * Create a class-based proxy (requires CGLIB).
+ */
+ TARGET_CLASS
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/package.html b/org.springframework.context/src/main/java/org/springframework/context/annotation/package.html
new file mode 100644
index 00000000000..4762b9c5807
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/package.html
@@ -0,0 +1,8 @@
+
+
+
+Annotation support for context configuration,
+including classpath scanning for autowire candidates.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/AbstractPropertyLoadingBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/AbstractPropertyLoadingBeanDefinitionParser.java
new file mode 100644
index 00000000000..d18577b017d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/AbstractPropertyLoadingBeanDefinitionParser.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.util.StringUtils;
+
+/**
+ * Abstract parser for <context:property-.../> elements.
+ *
+ * @author Juergen Hoeller
+ * @author Arjen Poutsma
+ * @since 2.5.2
+ */
+abstract class AbstractPropertyLoadingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
+
+ protected boolean shouldGenerateId() {
+ return true;
+ }
+
+ protected void doParse(Element element, BeanDefinitionBuilder builder) {
+ String location = element.getAttribute("location");
+ if (StringUtils.hasLength(location)) {
+ String[] locations = StringUtils.commaDelimitedListToStringArray(location);
+ builder.addPropertyValue("locations", locations);
+ }
+ String propertiesRef = element.getAttribute("properties-ref");
+ if (StringUtils.hasLength(propertiesRef)) {
+ builder.addPropertyReference("properties", propertiesRef);
+ }
+ builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ }
+
+}
\ No newline at end of file
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/ContextNamespaceHandler.java b/org.springframework.context/src/main/java/org/springframework/context/config/ContextNamespaceHandler.java
new file mode 100644
index 00000000000..9a07fd2dc4a
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/ContextNamespaceHandler.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.core.JdkVersion;
+import org.springframework.util.ClassUtils;
+
+/**
+ * {@link org.springframework.beans.factory.xml.NamespaceHandler}
+ * for the 'context' namespace.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+public class ContextNamespaceHandler extends NamespaceHandlerSupport {
+
+ public void init() {
+ registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
+ registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
+ registerJava5DependentParser("annotation-config",
+ "org.springframework.context.annotation.AnnotationConfigBeanDefinitionParser");
+ registerJava5DependentParser("component-scan",
+ "org.springframework.context.annotation.ComponentScanBeanDefinitionParser");
+ registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
+ registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
+ registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
+ registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
+ }
+
+ private void registerJava5DependentParser(final String elementName, final String parserClassName) {
+ BeanDefinitionParser parser = null;
+ if (JdkVersion.isAtLeastJava15()) {
+ try {
+ Class parserClass = ClassUtils.forName(parserClassName, ContextNamespaceHandler.class.getClassLoader());
+ parser = (BeanDefinitionParser) parserClass.newInstance();
+ }
+ catch (Throwable ex) {
+ throw new IllegalStateException("Unable to create Java 1.5 dependent parser: " + parserClassName, ex);
+ }
+ }
+ else {
+ parser = new BeanDefinitionParser() {
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ throw new IllegalStateException("Context namespace element '" + elementName +
+ "' and its parser class [" + parserClassName + "] are only available on JDK 1.5 and higher");
+ }
+ };
+ }
+ registerBeanDefinitionParser(elementName, parser);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/LoadTimeWeaverBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/LoadTimeWeaverBeanDefinitionParser.java
new file mode 100644
index 00000000000..9d2e861d6db
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/LoadTimeWeaverBeanDefinitionParser.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Parser for the <context:load-time-weaver/> element.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+class LoadTimeWeaverBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
+
+ private static final String WEAVER_CLASS_ATTRIBUTE = "weaver-class";
+
+ private static final String ASPECTJ_WEAVING_ATTRIBUTE = "aspectj-weaving";
+
+ private static final String ASPECTJ_AOP_XML_RESOURCE = "META-INF/aop.xml";
+
+ private static final String DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME =
+ "org.springframework.context.weaving.DefaultContextLoadTimeWeaver";
+
+ private static final String ASPECTJ_WEAVING_ENABLER_CLASS_NAME =
+ "org.springframework.context.weaving.AspectJWeavingEnabler";
+
+
+ protected String getBeanClassName(Element element) {
+ if (element.hasAttribute(WEAVER_CLASS_ATTRIBUTE)) {
+ return element.getAttribute(WEAVER_CLASS_ATTRIBUTE);
+ }
+ return DEFAULT_LOAD_TIME_WEAVER_CLASS_NAME;
+ }
+
+ protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
+ return ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME;
+ }
+
+ protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+
+ if (isAspectJWeavingEnabled(element.getAttribute(ASPECTJ_WEAVING_ATTRIBUTE), parserContext)) {
+ RootBeanDefinition weavingEnablerDef = new RootBeanDefinition();
+ weavingEnablerDef.setBeanClassName(ASPECTJ_WEAVING_ENABLER_CLASS_NAME);
+ parserContext.getReaderContext().registerWithGeneratedName(weavingEnablerDef);
+
+ if (isBeanConfigurerAspectEnabled(parserContext.getReaderContext().getBeanClassLoader())) {
+ new SpringConfiguredBeanDefinitionParser().parse(element, parserContext);
+ }
+ }
+ }
+
+ protected boolean isAspectJWeavingEnabled(String value, ParserContext parserContext) {
+ if ("on".equals(value)) {
+ return true;
+ }
+ else if ("off".equals(value)) {
+ return false;
+ }
+ else {
+ // Determine default...
+ ClassLoader cl = parserContext.getReaderContext().getResourceLoader().getClassLoader();
+ return (cl.getResource(ASPECTJ_AOP_XML_RESOURCE) != null);
+ }
+ }
+
+ protected boolean isBeanConfigurerAspectEnabled(ClassLoader beanClassLoader) {
+ return ClassUtils.isPresent(SpringConfiguredBeanDefinitionParser.BEAN_CONFIGURER_ASPECT_CLASS_NAME,
+ beanClassLoader);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/MBeanExportBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/MBeanExportBeanDefinitionParser.java
new file mode 100644
index 00000000000..2bc9437302d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/MBeanExportBeanDefinitionParser.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.core.JdkVersion;
+import org.springframework.jmx.support.MBeanRegistrationSupport;
+import org.springframework.util.StringUtils;
+
+/**
+ * Parser for the <context:mbean-export/> element.
+ *
+ *
Registers an instance of
+ * {@link org.springframework.jmx.export.annotation.AnnotationMBeanExporter}
+ * within the context.
+ *
+ * @author Juergen Hoeller
+ * @author Mark Fisher
+ * @since 2.5
+ * @see org.springframework.jmx.export.annotation.AnnotationMBeanExporter
+ */
+class MBeanExportBeanDefinitionParser extends AbstractBeanDefinitionParser {
+
+ private static final String MBEAN_EXPORTER_BEAN_NAME = "mbeanExporter";
+
+ private static final String DEFAULT_DOMAIN_ATTRIBUTE = "default-domain";
+
+ private static final String SERVER_ATTRIBUTE = "server";
+
+ private static final String REGISTRATION_ATTRIBUTE = "registration";
+
+ private static final String REGISTRATION_IGNORE_EXISTING = "ignoreExisting";
+
+ private static final String REGISTRATION_REPLACE_EXISTING = "replaceExisting";
+
+
+ protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
+ return MBEAN_EXPORTER_BEAN_NAME;
+ }
+
+ protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
+ String beanClassName = (JdkVersion.isAtLeastJava15() ?
+ "org.springframework.jmx.export.annotation.AnnotationMBeanExporter" :
+ "org.springframework.jmx.export.MBeanExporter");
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(beanClassName);
+
+ // Mark as infrastructure bean and attach source location.
+ builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
+
+ String defaultDomain = element.getAttribute(DEFAULT_DOMAIN_ATTRIBUTE);
+ if (StringUtils.hasText(defaultDomain)) {
+ builder.addPropertyValue("defaultDomain", defaultDomain);
+ }
+
+ String serverBeanName = element.getAttribute(SERVER_ATTRIBUTE);
+ if (StringUtils.hasText(serverBeanName)) {
+ builder.addPropertyReference("server", serverBeanName);
+ }
+ else {
+ AbstractBeanDefinition specialServer = MBeanServerBeanDefinitionParser.findServerForSpecialEnvironment();
+ if (specialServer != null) {
+ builder.addPropertyValue("server", specialServer);
+ }
+ }
+
+ String registration = element.getAttribute(REGISTRATION_ATTRIBUTE);
+ int registrationBehavior = MBeanRegistrationSupport.REGISTRATION_FAIL_ON_EXISTING;
+ if (REGISTRATION_IGNORE_EXISTING.equals(registration)) {
+ registrationBehavior = MBeanRegistrationSupport.REGISTRATION_IGNORE_EXISTING;
+ }
+ else if (REGISTRATION_REPLACE_EXISTING.equals(registration)) {
+ registrationBehavior = MBeanRegistrationSupport.REGISTRATION_REPLACE_EXISTING;
+ }
+ builder.addPropertyValue("registrationBehavior", new Integer(registrationBehavior));
+
+ return builder.getBeanDefinition();
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/MBeanServerBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/MBeanServerBeanDefinitionParser.java
new file mode 100644
index 00000000000..08585b8910b
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/MBeanServerBeanDefinitionParser.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.jmx.support.MBeanServerFactoryBean;
+import org.springframework.jmx.support.WebSphereMBeanServerFactoryBean;
+import org.springframework.jndi.JndiObjectFactoryBean;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Parser for the <context:mbean-server/> element.
+ *
+ *
Registers an instance of
+ * {@link org.springframework.jmx.export.annotation.AnnotationMBeanExporter}
+ * within the context.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see org.springframework.jmx.export.annotation.AnnotationMBeanExporter
+ */
+class MBeanServerBeanDefinitionParser extends AbstractBeanDefinitionParser {
+
+ private static final String MBEAN_SERVER_BEAN_NAME = "mbeanServer";
+
+ private static final String AGENT_ID_ATTRIBUTE = "agent-id";
+
+
+ private static final boolean weblogicPresent = ClassUtils.isPresent(
+ "weblogic.management.Helper", MBeanServerBeanDefinitionParser.class.getClassLoader());
+
+ private static final boolean webspherePresent = ClassUtils.isPresent(
+ "com.ibm.websphere.management.AdminServiceFactory", MBeanServerBeanDefinitionParser.class.getClassLoader());
+
+
+ protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
+ String id = element.getAttribute(ID_ATTRIBUTE);
+ return (StringUtils.hasText(id) ? id : MBEAN_SERVER_BEAN_NAME);
+ }
+
+ protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
+ String agentId = element.getAttribute(AGENT_ID_ATTRIBUTE);
+ if (StringUtils.hasText(agentId)) {
+ RootBeanDefinition bd = new RootBeanDefinition(MBeanServerFactoryBean.class);
+ bd.getPropertyValues().addPropertyValue("agentId", agentId);
+ return bd;
+ }
+ AbstractBeanDefinition specialServer = findServerForSpecialEnvironment();
+ if (specialServer != null) {
+ return specialServer;
+ }
+ RootBeanDefinition bd = new RootBeanDefinition(MBeanServerFactoryBean.class);
+ bd.getPropertyValues().addPropertyValue("locateExistingServerIfPossible", Boolean.TRUE);
+
+ // Mark as infrastructure bean and attach source location.
+ bd.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ bd.setSource(parserContext.extractSource(element));
+ return bd;
+ }
+
+ static AbstractBeanDefinition findServerForSpecialEnvironment() {
+ if (weblogicPresent) {
+ RootBeanDefinition bd = new RootBeanDefinition(JndiObjectFactoryBean.class);
+ bd.getPropertyValues().addPropertyValue("jndiName", "java:comp/env/jmx/runtime");
+ return bd;
+ }
+ else if (webspherePresent) {
+ return new RootBeanDefinition(WebSphereMBeanServerFactoryBean.class);
+ }
+ else {
+ return null;
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/PropertyOverrideBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/PropertyOverrideBeanDefinitionParser.java
new file mode 100644
index 00000000000..0e00fb39343
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/PropertyOverrideBeanDefinitionParser.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.PropertyOverrideConfigurer;
+
+/**
+ * Parser for the <context:property-override/> element.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5.2
+ */
+class PropertyOverrideBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {
+
+ protected Class getBeanClass(Element element) {
+ return PropertyOverrideConfigurer.class;
+ }
+
+}
\ No newline at end of file
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/PropertyPlaceholderBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/PropertyPlaceholderBeanDefinitionParser.java
new file mode 100644
index 00000000000..95aff2c8c22
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/PropertyPlaceholderBeanDefinitionParser.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
+
+/**
+ * Parser for the <context:property-placeholder/> element.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+class PropertyPlaceholderBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {
+
+ protected Class getBeanClass(Element element) {
+ return PropertyPlaceholderConfigurer.class;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/SpringConfiguredBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/context/config/SpringConfiguredBeanDefinitionParser.java
new file mode 100644
index 00000000000..3094136710b
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/SpringConfiguredBeanDefinitionParser.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.BeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+
+/**
+ * {@link BeanDefinitionParser} responsible for parsing the
+ * <context:spring-configured/> tag.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+class SpringConfiguredBeanDefinitionParser implements BeanDefinitionParser {
+
+ /**
+ * The bean name of the internally managed bean configurer aspect.
+ */
+ public static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME =
+ "org.springframework.context.config.internalBeanConfigurerAspect";
+
+ static final String BEAN_CONFIGURER_ASPECT_CLASS_NAME =
+ "org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect";
+
+
+ public BeanDefinition parse(Element element, ParserContext parserContext) {
+ if (!parserContext.getRegistry().containsBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME)) {
+ RootBeanDefinition def = new RootBeanDefinition();
+ def.setBeanClassName(BEAN_CONFIGURER_ASPECT_CLASS_NAME);
+ def.setFactoryMethodName("aspectOf");
+
+ // Mark as infrastructure bean and attach source location.
+ def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
+ def.setSource(parserContext.extractSource(element));
+ parserContext.registerBeanComponent(new BeanComponentDefinition(def, BEAN_CONFIGURER_ASPECT_BEAN_NAME));
+ }
+ return null;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/package.html b/org.springframework.context/src/main/java/org/springframework/context/config/package.html
new file mode 100644
index 00000000000..9cf9ecee19d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/package.html
@@ -0,0 +1,8 @@
+
+
+
+Support package for advanced application context configuration,
+with XML schema being the primary configuration format.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/config/spring-context-2.5.xsd b/org.springframework.context/src/main/java/org/springframework/context/config/spring-context-2.5.xsd
new file mode 100644
index 00000000000..0cbb8da9273
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/config/spring-context-2.5.xsd
@@ -0,0 +1,423 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tag for that purpose.
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java b/org.springframework.context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java
new file mode 100644
index 00000000000..91af674736d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/AbstractApplicationEventMulticaster.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import java.util.Collection;
+import java.util.LinkedHashSet;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.CollectionFactory;
+
+/**
+ * Abstract implementation of the {@link ApplicationEventMulticaster} interface,
+ * providing the basic listener registration facility.
+ *
+ *
Doesn't permit multiple instances of the same listener by default,
+ * as it keeps listeners in a linked Set. The collection class used to hold
+ * ApplicationListener objects can be overridden through the "collectionClass"
+ * bean property.
+ *
+ *
Implementing ApplicationEventMulticaster's actual {@link #multicastEvent} method
+ * is left to subclasses. {@link SimpleApplicationEventMulticaster} simply multicasts
+ * all events to all registered listeners, invoking them in the calling thread.
+ * Alternative implementations could be more sophisticated in those respects.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2.3
+ * @see #setCollectionClass
+ * @see #getApplicationListeners()
+ * @see SimpleApplicationEventMulticaster
+ */
+public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster {
+
+ /** Collection of ApplicationListeners */
+ private Collection applicationListeners = new LinkedHashSet();
+
+
+ /**
+ * Set whether this multicaster should expect concurrent updates at runtime
+ * (i.e. after context startup finished). In case of concurrent updates,
+ * a copy-on-write strategy is applied, keeping iteration (for multicasting)
+ * without synchronization while still making listener updates thread-safe.
+ */
+ public void setConcurrentUpdates(boolean concurrent) {
+ Collection newColl = (concurrent ? CollectionFactory.createCopyOnWriteSet() : new LinkedHashSet());
+ // Add all previously registered listeners (usually none).
+ newColl.addAll(this.applicationListeners);
+ this.applicationListeners = newColl;
+ }
+
+ /**
+ * Specify the collection class to use. Can be populated with a fully
+ * qualified class name when defined in a Spring application context.
+ *
Default is a linked HashSet, keeping the registration order.
+ * Note that a Set class specified will not permit multiple instances
+ * of the same listener, while a List class will allow for registering
+ * the same listener multiple times.
+ */
+ public void setCollectionClass(Class collectionClass) {
+ if (collectionClass == null) {
+ throw new IllegalArgumentException("'collectionClass' must not be null");
+ }
+ if (!Collection.class.isAssignableFrom(collectionClass)) {
+ throw new IllegalArgumentException("'collectionClass' must implement [java.util.Collection]");
+ }
+ // Create desired collection instance.
+ Collection newColl = (Collection) BeanUtils.instantiateClass(collectionClass);
+ // Add all previously registered listeners (usually none).
+ newColl.addAll(this.applicationListeners);
+ this.applicationListeners = newColl;
+ }
+
+
+ public void addApplicationListener(ApplicationListener listener) {
+ this.applicationListeners.add(listener);
+ }
+
+ public void removeApplicationListener(ApplicationListener listener) {
+ this.applicationListeners.remove(listener);
+ }
+
+ public void removeAllListeners() {
+ this.applicationListeners.clear();
+ }
+
+ /**
+ * Return the current Collection of ApplicationListeners.
+ *
Note that this is the raw Collection of ApplicationListeners,
+ * potentially modified when new listeners get registered or
+ * existing ones get removed. This Collection is not a snapshot copy.
+ * @return a Collection of ApplicationListeners
+ * @see org.springframework.context.ApplicationListener
+ */
+ protected Collection getApplicationListeners() {
+ return this.applicationListeners;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/ApplicationContextEvent.java b/org.springframework.context/src/main/java/org/springframework/context/event/ApplicationContextEvent.java
new file mode 100644
index 00000000000..bc9dbb3d5f2
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/ApplicationContextEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * Base class for events raised for an ApplicationContext.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ */
+public abstract class ApplicationContextEvent extends ApplicationEvent {
+
+ /**
+ * Create a new ContextStartedEvent.
+ * @param source the ApplicationContext that the event is raised for
+ * (must not be null)
+ */
+ public ApplicationContextEvent(ApplicationContext source) {
+ super(source);
+ }
+
+ /**
+ * Get the ApplicationContext that the event was raised for.
+ */
+ public final ApplicationContext getApplicationContext() {
+ return (ApplicationContext) getSource();
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java b/org.springframework.context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java
new file mode 100644
index 00000000000..3998bb33a0b
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/ApplicationEventMulticaster.java
@@ -0,0 +1,58 @@
+ /*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+
+/**
+ * Interface to be implemented by objects that can manage a number
+ * of ApplicationListeners, and publish events to them. An example
+ * of such an object is an ApplicationEventPublisher, typically
+ * the ApplicationContext, which can use an ApplicationEventMulticaster
+ * as a helper to publish events to listeners.
+ *
+ * @author Rod Johnson
+ */
+public interface ApplicationEventMulticaster {
+
+ /**
+ * Add a listener to be notified of all events.
+ * @param listener the listener to add
+ */
+ void addApplicationListener(ApplicationListener listener);
+
+ /**
+ * Remove a listener from the notification list.
+ * @param listener the listener to remove
+ */
+ void removeApplicationListener(ApplicationListener listener);
+
+ /**
+ * Remove all listeners registered with this multicaster.
+ * It will perform no action on event notification until more
+ * listeners are registered.
+ */
+ void removeAllListeners();
+
+ /**
+ * Multicast the given application event to appropriate listeners.
+ * @param event the event to multicast
+ */
+ void multicastEvent(ApplicationEvent event);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/ContextClosedEvent.java b/org.springframework.context/src/main/java/org/springframework/context/event/ContextClosedEvent.java
new file mode 100644
index 00000000000..e53df338969
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/ContextClosedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import org.springframework.context.ApplicationContext;
+
+/**
+ * Event raised when an ApplicationContext gets closed.
+ *
+ * @author Juergen Hoeller
+ * @since 12.08.2003
+ * @see ContextRefreshedEvent
+ */
+public class ContextClosedEvent extends ApplicationContextEvent {
+
+ /**
+ * Creates a new ContextClosedEvent.
+ * @param source the ApplicationContext that has been closed
+ * (must not be null)
+ */
+ public ContextClosedEvent(ApplicationContext source) {
+ super(source);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/ContextRefreshedEvent.java b/org.springframework.context/src/main/java/org/springframework/context/event/ContextRefreshedEvent.java
new file mode 100644
index 00000000000..9b448cb3b42
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/ContextRefreshedEvent.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import org.springframework.context.ApplicationContext;
+
+/**
+ * Event raised when an ApplicationContext gets initialized or refreshed.
+ *
+ * @author Juergen Hoeller
+ * @since 04.03.2003
+ * @see ContextClosedEvent
+ */
+public class ContextRefreshedEvent extends ApplicationContextEvent {
+
+ /**
+ * Create a new ContextRefreshedEvent.
+ * @param source the ApplicationContext that has been initialized
+ * or refreshed (must not be null)
+ */
+ public ContextRefreshedEvent(ApplicationContext source) {
+ super(source);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/ContextStartedEvent.java b/org.springframework.context/src/main/java/org/springframework/context/event/ContextStartedEvent.java
new file mode 100644
index 00000000000..bc3e7163c13
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/ContextStartedEvent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import org.springframework.context.ApplicationContext;
+
+/**
+ * Event raised when an ApplicationContext gets started.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see ContextStoppedEvent
+ */
+public class ContextStartedEvent extends ApplicationContextEvent {
+
+ /**
+ * Create a new ContextStartedEvent.
+ * @param source the ApplicationContext that has been started
+ * (must not be null)
+ */
+ public ContextStartedEvent(ApplicationContext source) {
+ super(source);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/ContextStoppedEvent.java b/org.springframework.context/src/main/java/org/springframework/context/event/ContextStoppedEvent.java
new file mode 100644
index 00000000000..a00f82baea5
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/ContextStoppedEvent.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import org.springframework.context.ApplicationContext;
+
+/**
+ * Event raised when an ApplicationContext gets stopped.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see ContextStartedEvent
+ */
+public class ContextStoppedEvent extends ApplicationContextEvent {
+
+ /**
+ * Create a new ContextStoppedEvent.
+ * @param source the ApplicationContext that has been stopped
+ * (must not be null)
+ */
+ public ContextStoppedEvent(ApplicationContext source) {
+ super(source);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/EventPublicationInterceptor.java b/org.springframework.context/src/main/java/org/springframework/context/event/EventPublicationInterceptor.java
new file mode 100644
index 00000000000..98d08dbcf3d
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/EventPublicationInterceptor.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import java.lang.reflect.Constructor;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+
+/**
+ * {@link MethodInterceptor Interceptor} that publishes an
+ * ApplicationEvent to all ApplicationListeners
+ * registered with an ApplicationEventPublisher after each
+ * successful method invocation.
+ *
+ *
Note that this interceptor is only capable of publishing stateless
+ * events configured via the
+ * {@link #setApplicationEventClass "applicationEventClass"} property.
+ *
+ * @author Dmitriy Kopylenko
+ * @author Juergen Hoeller
+ * @author Rick Evans
+ * @see #setApplicationEventClass
+ * @see org.springframework.context.ApplicationEvent
+ * @see org.springframework.context.ApplicationListener
+ * @see org.springframework.context.ApplicationEventPublisher
+ * @see org.springframework.context.ApplicationContext
+ */
+public class EventPublicationInterceptor
+ implements MethodInterceptor, ApplicationEventPublisherAware, InitializingBean {
+
+ private Constructor applicationEventClassConstructor;
+
+ private ApplicationEventPublisher applicationEventPublisher;
+
+
+ /**
+ * Set the application event class to publish.
+ *
The event class must have a constructor with a single
+ * Object argument for the event source. The interceptor
+ * will pass in the invoked object.
+ * @throws IllegalArgumentException if the supplied Class is
+ * null or if it is not an ApplicationEvent subclass or
+ * if it does not expose a constructor that takes a single Object argument
+ */
+ public void setApplicationEventClass(Class applicationEventClass) {
+ if (ApplicationEvent.class.equals(applicationEventClass) ||
+ !ApplicationEvent.class.isAssignableFrom(applicationEventClass)) {
+ throw new IllegalArgumentException("applicationEventClass needs to extend ApplicationEvent");
+ }
+ try {
+ this.applicationEventClassConstructor =
+ applicationEventClass.getConstructor(new Class[] {Object.class});
+ }
+ catch (NoSuchMethodException ex) {
+ throw new IllegalArgumentException("applicationEventClass [" +
+ applicationEventClass.getName() + "] does not have the required Object constructor: " + ex);
+ }
+ }
+
+ public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+ this.applicationEventPublisher = applicationEventPublisher;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (this.applicationEventClassConstructor == null) {
+ throw new IllegalArgumentException("applicationEventClass is required");
+ }
+ }
+
+
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ Object retVal = invocation.proceed();
+
+ ApplicationEvent event = (ApplicationEvent)
+ this.applicationEventClassConstructor.newInstance(new Object[] {invocation.getThis()});
+ this.applicationEventPublisher.publishEvent(event);
+
+ return retVal;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java b/org.springframework.context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
new file mode 100644
index 00000000000..179e0761fac
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/SimpleApplicationEventMulticaster.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import java.util.Iterator;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.task.SyncTaskExecutor;
+import org.springframework.core.task.TaskExecutor;
+
+/**
+ * Simple implementation of the {@link ApplicationEventMulticaster} interface.
+ *
+ *
Multicasts all events to all registered listeners, leaving it up to
+ * the listeners to ignore events that they are not interested in.
+ * Listeners will usually perform corresponding instanceof
+ * checks on the passed-in event object.
+ *
+ *
By default, all listeners are invoked in the calling thread.
+ * This allows the danger of a rogue listener blocking the entire application,
+ * but adds minimal overhead. Specify an alternative TaskExecutor to have
+ * listeners executed in different threads, for example from a thread pool.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #setTaskExecutor
+ * @see #setConcurrentUpdates
+ */
+public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
+
+ private TaskExecutor taskExecutor = new SyncTaskExecutor();
+
+
+ /**
+ * Set the TaskExecutor to execute application listeners with.
+ *
Default is a SyncTaskExecutor, executing the listeners synchronously
+ * in the calling thread.
+ *
Consider specifying an asynchronous TaskExecutor here to not block the
+ * caller until all listeners have been executed. However, note that asynchronous
+ * execution will not participate in the caller's thread context (class loader,
+ * transaction association) unless the TaskExecutor explicitly supports this.
+ * @see org.springframework.core.task.SyncTaskExecutor
+ * @see org.springframework.core.task.SimpleAsyncTaskExecutor
+ * @see org.springframework.scheduling.timer.TimerTaskExecutor
+ */
+ public void setTaskExecutor(TaskExecutor taskExecutor) {
+ this.taskExecutor = (taskExecutor != null ? taskExecutor : new SyncTaskExecutor());
+ }
+
+ /**
+ * Return the current TaskExecutor for this multicaster.
+ */
+ protected TaskExecutor getTaskExecutor() {
+ return this.taskExecutor;
+ }
+
+
+ public void multicastEvent(final ApplicationEvent event) {
+ for (Iterator it = getApplicationListeners().iterator(); it.hasNext();) {
+ final ApplicationListener listener = (ApplicationListener) it.next();
+ getTaskExecutor().execute(new Runnable() {
+ public void run() {
+ listener.onApplicationEvent(event);
+ }
+ });
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/SourceFilteringListener.java b/org.springframework.context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
new file mode 100644
index 00000000000..0a4287bd4e4
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/SourceFilteringListener.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.event;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+
+/**
+ * {@link org.springframework.context.ApplicationListener} decorator that filters
+ * events from a specified event source, invoking its delegate listener for
+ * matching {@link org.springframework.context.ApplicationEvent} objects only.
+ *
+ *
Can also be used as base class, overriding the {@link #onApplicationEventInternal}
+ * method instead of specifying a delegate listener.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0.5
+ */
+public class SourceFilteringListener implements ApplicationListener {
+
+ private final Object source;
+
+ private ApplicationListener delegate;
+
+
+ /**
+ * Create a SourceFilteringListener for the given event source.
+ * @param source the event source that this listener filters for,
+ * only processing events from this source
+ * @param delegate the delegate listener to invoke with event
+ * from the specified source
+ */
+ public SourceFilteringListener(Object source, ApplicationListener delegate) {
+ this.source = source;
+ this.delegate = delegate;
+ }
+
+ /**
+ * Create a SourceFilteringListener for the given event source,
+ * expecting subclasses to override the {@link #onApplicationEventInternal}
+ * method (instead of specifying a delegate listener).
+ * @param source the event source that this listener filters for,
+ * only processing events from this source
+ */
+ protected SourceFilteringListener(Object source) {
+ this.source = source;
+ }
+
+
+ public void onApplicationEvent(ApplicationEvent event) {
+ if (event.getSource() == this.source) {
+ onApplicationEventInternal(event);
+ }
+ }
+
+ /**
+ * Actually process the event, after having filtered according to the
+ * desired event source already.
+ *
The default implementation invokes the specified delegate, if any.
+ * @param event the event to process (matching the specified source)
+ */
+ protected void onApplicationEventInternal(ApplicationEvent event) {
+ if (this.delegate == null) {
+ throw new IllegalStateException(
+ "Must specify a delegate object or override the onApplicationEventInternal method");
+ }
+ this.delegate.onApplicationEvent(event);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/event/package.html b/org.springframework.context/src/main/java/org/springframework/context/event/package.html
new file mode 100644
index 00000000000..17301dddbd5
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/event/package.html
@@ -0,0 +1,8 @@
+
+
+
+Support classes for application events, like standard context events.
+To be supported by all major application context implementations.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/i18n/LocaleContext.java b/org.springframework.context/src/main/java/org/springframework/context/i18n/LocaleContext.java
new file mode 100644
index 00000000000..c746151b28b
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/i18n/LocaleContext.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context.i18n;
+
+import java.util.Locale;
+
+/**
+ * Strategy interface for determining the current Locale.
+ *
+ *
A LocaleContext instance can be associated with a thread
+ * via the LocaleContextHolder class.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see LocaleContextHolder
+ * @see java.util.Locale
+ */
+public interface LocaleContext {
+
+ /**
+ * Return the current Locale, which can be fixed or determined dynamically,
+ * depending on the implementation strategy.
+ */
+ Locale getLocale();
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java b/org.springframework.context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java
new file mode 100644
index 00000000000..2b244f03321
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/i18n/LocaleContextHolder.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.i18n;
+
+import java.util.Locale;
+
+import org.springframework.core.NamedInheritableThreadLocal;
+import org.springframework.core.NamedThreadLocal;
+
+/**
+ * Simple holder class that associates a LocaleContext instance
+ * with the current thread. The LocaleContext will be inherited
+ * by any child threads spawned by the current thread.
+ *
+ *
Used as a central holder for the current Locale in Spring,
+ * wherever necessary: for example, in MessageSourceAccessor.
+ * DispatcherServlet automatically exposes its current Locale here.
+ * Other applications can expose theirs too, to make classes like
+ * MessageSourceAccessor automatically use that Locale.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see LocaleContext
+ * @see org.springframework.context.support.MessageSourceAccessor
+ * @see org.springframework.web.servlet.DispatcherServlet
+ */
+public abstract class LocaleContextHolder {
+
+ private static final ThreadLocal localeContextHolder = new NamedThreadLocal("Locale context");
+
+ private static final ThreadLocal inheritableLocaleContextHolder =
+ new NamedInheritableThreadLocal("Locale context");
+
+
+ /**
+ * Reset the LocaleContext for the current thread.
+ */
+ public static void resetLocaleContext() {
+ localeContextHolder.set(null);
+ inheritableLocaleContextHolder.set(null);
+ }
+
+ /**
+ * Associate the given LocaleContext with the current thread,
+ * not exposing it as inheritable for child threads.
+ * @param localeContext the current LocaleContext, or null to reset
+ * the thread-bound context
+ */
+ public static void setLocaleContext(LocaleContext localeContext) {
+ setLocaleContext(localeContext, false);
+ }
+
+ /**
+ * Associate the given LocaleContext with the current thread.
+ * @param localeContext the current LocaleContext, or null to reset
+ * the thread-bound context
+ * @param inheritable whether to expose the LocaleContext as inheritable
+ * for child threads (using an {@link java.lang.InheritableThreadLocal})
+ */
+ public static void setLocaleContext(LocaleContext localeContext, boolean inheritable) {
+ if (inheritable) {
+ inheritableLocaleContextHolder.set(localeContext);
+ localeContextHolder.set(null);
+ }
+ else {
+ localeContextHolder.set(localeContext);
+ inheritableLocaleContextHolder.set(null);
+ }
+ }
+
+ /**
+ * Return the LocaleContext associated with the current thread, if any.
+ * @return the current LocaleContext, or null if none
+ */
+ public static LocaleContext getLocaleContext() {
+ LocaleContext localeContext = (LocaleContext) localeContextHolder.get();
+ if (localeContext == null) {
+ localeContext = (LocaleContext) inheritableLocaleContextHolder.get();
+ }
+ return localeContext;
+ }
+
+ /**
+ * Associate the given Locale with the current thread.
+ *
Will implicitly create a LocaleContext for the given Locale,
+ * not exposing it as inheritable for child threads.
+ * @param locale the current Locale, or null to reset
+ * the thread-bound context
+ * @see SimpleLocaleContext#SimpleLocaleContext(java.util.Locale)
+ */
+ public static void setLocale(Locale locale) {
+ setLocale(locale, false);
+ }
+
+ /**
+ * Associate the given Locale with the current thread.
+ *
Will implicitly create a LocaleContext for the given Locale.
+ * @param locale the current Locale, or null to reset
+ * the thread-bound context
+ * @param inheritable whether to expose the LocaleContext as inheritable
+ * for child threads (using an {@link java.lang.InheritableThreadLocal})
+ * @see SimpleLocaleContext#SimpleLocaleContext(java.util.Locale)
+ */
+ public static void setLocale(Locale locale, boolean inheritable) {
+ LocaleContext localeContext = (locale != null ? new SimpleLocaleContext(locale) : null);
+ setLocaleContext(localeContext, inheritable);
+ }
+
+ /**
+ * Return the Locale associated with the current thread, if any,
+ * or the system default Locale else.
+ * @return the current Locale, or the system default Locale if no
+ * specific Locale has been associated with the current thread
+ * @see LocaleContext#getLocale()
+ * @see java.util.Locale#getDefault()
+ */
+ public static Locale getLocale() {
+ LocaleContext localeContext = getLocaleContext();
+ return (localeContext != null ? localeContext.getLocale() : Locale.getDefault());
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/i18n/SimpleLocaleContext.java b/org.springframework.context/src/main/java/org/springframework/context/i18n/SimpleLocaleContext.java
new file mode 100644
index 00000000000..9f0b8c04da9
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/i18n/SimpleLocaleContext.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.i18n;
+
+import java.util.Locale;
+
+import org.springframework.util.Assert;
+
+/**
+ * Simple implementation of the {@link LocaleContext} interface,
+ * always returning a specified Locale.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ */
+public class SimpleLocaleContext implements LocaleContext {
+
+ private final Locale locale;
+
+
+ /**
+ * Create a new SimpleLocaleContext that exposes the specified Locale.
+ * Every getLocale() will return this Locale.
+ * @param locale the Locale to expose
+ */
+ public SimpleLocaleContext(Locale locale) {
+ Assert.notNull(locale, "Locale must not be null");
+ this.locale = locale;
+ }
+
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+ public String toString() {
+ return this.locale.toString();
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/i18n/package.html b/org.springframework.context/src/main/java/org/springframework/context/i18n/package.html
new file mode 100644
index 00000000000..977d9a45fee
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/i18n/package.html
@@ -0,0 +1,8 @@
+
+
+
+Abstraction for determining the current Locale,
+plus global holder that exposes a thread-bound Locale.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/package.html b/org.springframework.context/src/main/java/org/springframework/context/package.html
new file mode 100644
index 00000000000..a398a9801b5
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/package.html
@@ -0,0 +1,16 @@
+
+
+
+This package builds on the beans package to add support for
+message sources and for the Observer design pattern, and the
+ability for application objects to obtain resources using a
+consistent API.
+
+
There is no necessity for Spring applications to depend
+on ApplicationContext or even BeanFactory functionality
+explicitly. One of the strengths of the Spring architecture
+is that application objects can often be configured without
+any dependency on Spring-specific APIs.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
new file mode 100644
index 00000000000..411d3e74377
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -0,0 +1,1204 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.support.ResourceEditorRegistrar;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.HierarchicalMessageSource;
+import org.springframework.context.Lifecycle;
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceAware;
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.context.event.ApplicationEventMulticaster;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.ContextStartedEvent;
+import org.springframework.context.event.ContextStoppedEvent;
+import org.springframework.context.event.SimpleApplicationEventMulticaster;
+import org.springframework.core.JdkVersion;
+import org.springframework.core.OrderComparator;
+import org.springframework.core.Ordered;
+import org.springframework.core.PriorityOrdered;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Abstract implementation of the {@link org.springframework.context.ApplicationContext}
+ * interface. Doesn't mandate the type of storage used for configuration; simply
+ * implements common context functionality. Uses the Template Method design pattern,
+ * requiring concrete subclasses to implement abstract methods.
+ *
+ *
In contrast to a plain BeanFactory, an ApplicationContext is supposed
+ * to detect special beans defined in its internal bean factory:
+ * Therefore, this class automatically registers
+ * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessors},
+ * {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessors}
+ * and {@link org.springframework.context.ApplicationListener ApplicationListeners}
+ * which are defined as beans in the context.
+ *
+ *
A {@link org.springframework.context.MessageSource} may also be supplied
+ * as a bean in the context, with the name "messageSource"; otherwise, message
+ * resolution is delegated to the parent context. Furthermore, a multicaster
+ * for application events can be supplied as "applicationEventMulticaster" bean
+ * of type {@link org.springframework.context.event.ApplicationEventMulticaster}
+ * in the context; otherwise, a default multicaster of type
+ * {@link org.springframework.context.event.SimpleApplicationEventMulticaster} will be used.
+ *
+ *
Implements resource loading through extending
+ * {@link org.springframework.core.io.DefaultResourceLoader}.
+ * Consequently treats non-URL resource paths as class path resources
+ * (supporting full class path resource names that include the package path,
+ * e.g. "mypackage/myresource.dat"), unless the {@link #getResourceByPath}
+ * method is overwritten in a subclass.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @author Mark Fisher
+ * @since January 21, 2001
+ * @see #refreshBeanFactory
+ * @see #getBeanFactory
+ * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
+ * @see org.springframework.beans.factory.config.BeanPostProcessor
+ * @see org.springframework.context.event.ApplicationEventMulticaster
+ * @see org.springframework.context.ApplicationListener
+ * @see org.springframework.context.MessageSource
+ */
+public abstract class AbstractApplicationContext extends DefaultResourceLoader
+ implements ConfigurableApplicationContext, DisposableBean {
+
+ /**
+ * Name of the MessageSource bean in the factory.
+ * If none is supplied, message resolution is delegated to the parent.
+ * @see MessageSource
+ */
+ public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";
+
+ /**
+ * Name of the ApplicationEventMulticaster bean in the factory.
+ * If none is supplied, a default SimpleApplicationEventMulticaster is used.
+ * @see org.springframework.context.event.ApplicationEventMulticaster
+ * @see org.springframework.context.event.SimpleApplicationEventMulticaster
+ */
+ public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
+
+
+ static {
+ // Eagerly load the ContextClosedEvent class to avoid weird classloader issues
+ // on application shutdown in WebLogic 8.1. (Reported by Dustin Woods.)
+ ContextClosedEvent.class.getName();
+ }
+
+
+ /** Logger used by this class. Available to subclasses. */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ /** Unique id for this context, if any */
+ private String id = ObjectUtils.identityToString(this);
+
+ /** Parent context */
+ private ApplicationContext parent;
+
+ /** BeanFactoryPostProcessors to apply on refresh */
+ private final List beanFactoryPostProcessors = new ArrayList();
+
+ /** Display name */
+ private String displayName;
+
+ /** System time in milliseconds when this context started */
+ private long startupDate;
+
+ /** Flag that indicates whether this context is currently active */
+ private boolean active = false;
+
+ /** Synchronization monitor for the "active" flag */
+ private final Object activeMonitor = new Object();
+
+ /** Synchronization monitor for the "refresh" and "destroy" */
+ private final Object startupShutdownMonitor = new Object();
+
+ /** Reference to the JVM shutdown hook, if registered */
+ private Thread shutdownHook;
+
+ /** ResourcePatternResolver used by this context */
+ private ResourcePatternResolver resourcePatternResolver;
+
+ /** MessageSource we delegate our implementation of this interface to */
+ private MessageSource messageSource;
+
+ /** Helper class used in event publishing */
+ private ApplicationEventMulticaster applicationEventMulticaster;
+
+ /** Statically specified listeners */
+ private List applicationListeners = new ArrayList();
+
+
+ /**
+ * Create a new AbstractApplicationContext with no parent.
+ */
+ public AbstractApplicationContext() {
+ this(null);
+ }
+
+ /**
+ * Create a new AbstractApplicationContext with the given parent context.
+ * @param parent the parent context
+ */
+ public AbstractApplicationContext(ApplicationContext parent) {
+ this.parent = parent;
+ this.resourcePatternResolver = getResourcePatternResolver();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of ApplicationContext interface
+ //---------------------------------------------------------------------
+
+ /**
+ * Set the unique id of this application context.
+ *
Default is the object id of the context instance, or the name
+ * of the context bean if the context is itself defined as a bean.
+ * @param id the unique id of the context
+ */
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ /**
+ * Return the parent context, or null if there is no parent
+ * (that is, this context is the root of the context hierarchy).
+ */
+ public ApplicationContext getParent() {
+ return this.parent;
+ }
+
+ /**
+ * Return this context's internal bean factory as AutowireCapableBeanFactory,
+ * if already available.
+ * @see #getBeanFactory()
+ */
+ public AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException {
+ return getBeanFactory();
+ }
+
+ /**
+ * Set a friendly name for this context.
+ * Typically done during initialization of concrete context implementations.
+ */
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ /**
+ * Return a friendly name for this context.
+ */
+ public String getDisplayName() {
+ return (this.displayName != null ? this.displayName : getId());
+ }
+
+ /**
+ * Return the timestamp (ms) when this context was first loaded.
+ */
+ public long getStartupDate() {
+ return this.startupDate;
+ }
+
+ /**
+ * Publish the given event to all listeners.
+ *
Note: Listeners get initialized after the MessageSource, to be able
+ * to access it within listener implementations. Thus, MessageSource
+ * implementations cannot publish events.
+ * @param event the event to publish (may be application-specific or a
+ * standard framework event)
+ */
+ public void publishEvent(ApplicationEvent event) {
+ Assert.notNull(event, "Event must not be null");
+ if (logger.isTraceEnabled()) {
+ logger.trace("Publishing event in context [" + getId() + "]: " + event);
+ }
+ getApplicationEventMulticaster().multicastEvent(event);
+ if (this.parent != null) {
+ this.parent.publishEvent(event);
+ }
+ }
+
+ /**
+ * Return the internal MessageSource used by the context.
+ * @return the internal MessageSource (never null)
+ * @throws IllegalStateException if the context has not been initialized yet
+ */
+ private ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
+ if (this.applicationEventMulticaster == null) {
+ throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
+ "call 'refresh' before multicasting events via the context: " + this);
+ }
+ return this.applicationEventMulticaster;
+ }
+
+ /**
+ * Return the ResourcePatternResolver to use for resolving location patterns
+ * into Resource instances. Default is a
+ * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver},
+ * supporting Ant-style location patterns.
+ *
Can be overridden in subclasses, for extended resolution strategies,
+ * for example in a web environment.
+ *
Do not call this when needing to resolve a location pattern.
+ * Call the context's getResources method instead, which
+ * will delegate to the ResourcePatternResolver.
+ * @return the ResourcePatternResolver for this context
+ * @see #getResources
+ * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
+ */
+ protected ResourcePatternResolver getResourcePatternResolver() {
+ return new PathMatchingResourcePatternResolver(this);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of ConfigurableApplicationContext interface
+ //---------------------------------------------------------------------
+
+ public void setParent(ApplicationContext parent) {
+ this.parent = parent;
+ }
+
+ public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
+ this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
+ }
+
+ /**
+ * Return the list of BeanFactoryPostProcessors that will get applied
+ * to the internal BeanFactory.
+ * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor
+ */
+ public List getBeanFactoryPostProcessors() {
+ return this.beanFactoryPostProcessors;
+ }
+
+ public void addApplicationListener(ApplicationListener listener) {
+ this.applicationListeners.add(listener);
+ }
+
+ /**
+ * Return the list of statically specified ApplicationListeners.
+ * @see org.springframework.context.ApplicationListener
+ */
+ public List getApplicationListeners() {
+ return this.applicationListeners;
+ }
+
+
+ public void refresh() throws BeansException, IllegalStateException {
+ synchronized (this.startupShutdownMonitor) {
+ // Prepare this context for refreshing.
+ prepareRefresh();
+
+ // Tell the subclass to refresh the internal bean factory.
+ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
+
+ // Prepare the bean factory for use in this context.
+ prepareBeanFactory(beanFactory);
+
+ try {
+ // Allows post-processing of the bean factory in context subclasses.
+ postProcessBeanFactory(beanFactory);
+
+ // Invoke factory processors registered as beans in the context.
+ invokeBeanFactoryPostProcessors(beanFactory);
+
+ // Register bean processors that intercept bean creation.
+ registerBeanPostProcessors(beanFactory);
+
+ // Initialize message source for this context.
+ initMessageSource();
+
+ // Initialize event multicaster for this context.
+ initApplicationEventMulticaster();
+
+ // Initialize other special beans in specific context subclasses.
+ onRefresh();
+
+ // Check for listener beans and register them.
+ registerListeners();
+
+ // Instantiate all remaining (non-lazy-init) singletons.
+ finishBeanFactoryInitialization(beanFactory);
+
+ // Last step: publish corresponding event.
+ finishRefresh();
+ }
+
+ catch (BeansException ex) {
+ // Destroy already created singletons to avoid dangling resources.
+ beanFactory.destroySingletons();
+
+ // Reset 'active' flag.
+ cancelRefresh(ex);
+
+ // Propagate exception to caller.
+ throw ex;
+ }
+ }
+ }
+
+ /**
+ * Prepare this context for refreshing, setting its startup date and
+ * active flag.
+ */
+ protected void prepareRefresh() {
+ this.startupDate = System.currentTimeMillis();
+
+ synchronized (this.activeMonitor) {
+ this.active = true;
+ }
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Refreshing " + this);
+ }
+ }
+
+ /**
+ * Tell the subclass to refresh the internal bean factory.
+ * @return the fresh BeanFactory instance
+ * @see #refreshBeanFactory()
+ * @see #getBeanFactory()
+ */
+ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
+ refreshBeanFactory();
+ ConfigurableListableBeanFactory beanFactory = getBeanFactory();
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Bean factory for application context [" + getId() + "]: " +
+ ObjectUtils.identityToString(beanFactory));
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug(beanFactory.getBeanDefinitionCount() + " beans defined in " + this);
+ }
+
+ return beanFactory;
+ }
+
+ /**
+ * Configure the factory's standard context characteristics,
+ * such as the context's ClassLoader and post-processors.
+ * @param beanFactory the BeanFactory to configure
+ */
+ protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
+ // Tell the internal bean factory to use the context's class loader.
+ beanFactory.setBeanClassLoader(getClassLoader());
+
+ // Populate the bean factory with context-specific resource editors.
+ beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this));
+
+ // Configure the bean factory with context callbacks.
+ beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
+ beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
+ beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
+ beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
+ beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
+
+ // BeanFactory interface not registered as resolvable type in a plain factory.
+ // MessageSource registered (and found for autowiring) as a bean.
+ beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
+ beanFactory.registerResolvableDependency(ResourceLoader.class, this);
+ beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
+ beanFactory.registerResolvableDependency(ApplicationContext.class, this);
+
+ // Detect a LoadTimeWeaver and prepare for weaving, if found.
+ if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME) && JdkVersion.isAtLeastJava15()) {
+ // Register the (JDK 1.5 specific) LoadTimeWeaverAwareProcessor.
+ try {
+ Class ltwapClass = ClassUtils.forName(
+ "org.springframework.context.weaving.LoadTimeWeaverAwareProcessor",
+ AbstractApplicationContext.class.getClassLoader());
+ BeanPostProcessor ltwap = (BeanPostProcessor) BeanUtils.instantiateClass(ltwapClass);
+ ((BeanFactoryAware) ltwap).setBeanFactory(beanFactory);
+ beanFactory.addBeanPostProcessor(ltwap);
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Spring's LoadTimeWeaverAwareProcessor class is not available");
+ }
+ // Set a temporary ClassLoader for type matching.
+ beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
+ }
+ }
+
+ /**
+ * Modify the application context's internal bean factory after its standard
+ * initialization. All bean definitions will have been loaded, but no beans
+ * will have been instantiated yet. This allows for registering special
+ * BeanPostProcessors etc in certain ApplicationContext implementations.
+ * @param beanFactory the bean factory used by the application context
+ */
+ protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
+ }
+
+ /**
+ * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
+ * respecting explicit order if given.
+ *
Must be called before singleton instantiation.
+ */
+ protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
+ // Invoke factory processors registered with the context instance.
+ for (Iterator it = getBeanFactoryPostProcessors().iterator(); it.hasNext();) {
+ BeanFactoryPostProcessor factoryProcessor = (BeanFactoryPostProcessor) it.next();
+ factoryProcessor.postProcessBeanFactory(beanFactory);
+ }
+
+ // Do not initialize FactoryBeans here: We need to leave all regular beans
+ // uninitialized to let the bean factory post-processors apply to them!
+ String[] postProcessorNames =
+ beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
+
+ // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
+ // Ordered, and the rest.
+ List priorityOrderedPostProcessors = new ArrayList();
+ List orderedPostProcessorNames = new ArrayList();
+ List nonOrderedPostProcessorNames = new ArrayList();
+ for (int i = 0; i < postProcessorNames.length; i++) {
+ if (isTypeMatch(postProcessorNames[i], PriorityOrdered.class)) {
+ priorityOrderedPostProcessors.add(beanFactory.getBean(postProcessorNames[i]));
+ }
+ else if (isTypeMatch(postProcessorNames[i], Ordered.class)) {
+ orderedPostProcessorNames.add(postProcessorNames[i]);
+ }
+ else {
+ nonOrderedPostProcessorNames.add(postProcessorNames[i]);
+ }
+ }
+
+ // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
+ Collections.sort(priorityOrderedPostProcessors, new OrderComparator());
+ invokeBeanFactoryPostProcessors(beanFactory, priorityOrderedPostProcessors);
+
+ // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
+ List orderedPostProcessors = new ArrayList();
+ for (Iterator it = orderedPostProcessorNames.iterator(); it.hasNext();) {
+ String postProcessorName = (String) it.next();
+ orderedPostProcessors.add(getBean(postProcessorName));
+ }
+ Collections.sort(orderedPostProcessors, new OrderComparator());
+ invokeBeanFactoryPostProcessors(beanFactory, orderedPostProcessors);
+
+ // Finally, invoke all other BeanFactoryPostProcessors.
+ List nonOrderedPostProcessors = new ArrayList();
+ for (Iterator it = nonOrderedPostProcessorNames.iterator(); it.hasNext();) {
+ String postProcessorName = (String) it.next();
+ nonOrderedPostProcessors.add(getBean(postProcessorName));
+ }
+ invokeBeanFactoryPostProcessors(beanFactory, nonOrderedPostProcessors);
+ }
+
+ /**
+ * Invoke the given BeanFactoryPostProcessor beans.
+ */
+ private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List postProcessors) {
+ for (Iterator it = postProcessors.iterator(); it.hasNext();) {
+ BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor) it.next();
+ postProcessor.postProcessBeanFactory(beanFactory);
+ }
+ }
+
+ /**
+ * Instantiate and invoke all registered BeanPostProcessor beans,
+ * respecting explicit order if given.
+ *
Must be called before any instantiation of application beans.
+ */
+ protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
+ String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
+
+ // Register BeanPostProcessorChecker that logs an info message when
+ // a bean is created during BeanPostProcessor instantiation, i.e. when
+ // a bean is not eligible for getting processed by all BeanPostProcessors.
+ int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
+ beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
+
+ // Separate between BeanPostProcessors that implement PriorityOrdered,
+ // Ordered, and the rest.
+ List priorityOrderedPostProcessors = new ArrayList();
+ List orderedPostProcessorNames = new ArrayList();
+ List nonOrderedPostProcessorNames = new ArrayList();
+ for (int i = 0; i < postProcessorNames.length; i++) {
+ if (isTypeMatch(postProcessorNames[i], PriorityOrdered.class)) {
+ priorityOrderedPostProcessors.add(beanFactory.getBean(postProcessorNames[i]));
+ }
+ else if (isTypeMatch(postProcessorNames[i], Ordered.class)) {
+ orderedPostProcessorNames.add(postProcessorNames[i]);
+ }
+ else {
+ nonOrderedPostProcessorNames.add(postProcessorNames[i]);
+ }
+ }
+
+ // First, register the BeanPostProcessors that implement PriorityOrdered.
+ Collections.sort(priorityOrderedPostProcessors, new OrderComparator());
+ registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
+
+ // Next, register the BeanPostProcessors that implement Ordered.
+ List orderedPostProcessors = new ArrayList();
+ for (Iterator it = orderedPostProcessorNames.iterator(); it.hasNext();) {
+ String postProcessorName = (String) it.next();
+ orderedPostProcessors.add(getBean(postProcessorName));
+ }
+ Collections.sort(orderedPostProcessors, new OrderComparator());
+ registerBeanPostProcessors(beanFactory, orderedPostProcessors);
+
+ // Finally, register all other BeanPostProcessors.
+ List nonOrderedPostProcessors = new ArrayList();
+ for (Iterator it = nonOrderedPostProcessorNames.iterator(); it.hasNext();) {
+ String postProcessorName = (String) it.next();
+ nonOrderedPostProcessors.add(getBean(postProcessorName));
+ }
+ registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
+ }
+
+ /**
+ * Register the given BeanPostProcessor beans.
+ */
+ private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, List postProcessors) {
+ for (Iterator it = postProcessors.iterator(); it.hasNext();) {
+ BeanPostProcessor postProcessor = (BeanPostProcessor) it.next();
+ beanFactory.addBeanPostProcessor(postProcessor);
+ }
+ }
+
+ /**
+ * Initialize the MessageSource.
+ * Use parent's if none defined in this context.
+ */
+ protected void initMessageSource() {
+ ConfigurableListableBeanFactory beanFactory = getBeanFactory();
+ if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
+ this.messageSource = (MessageSource) beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
+ // Make MessageSource aware of parent MessageSource.
+ if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
+ HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
+ if (hms.getParentMessageSource() == null) {
+ // Only set parent context as parent MessageSource if no parent MessageSource
+ // registered already.
+ hms.setParentMessageSource(getInternalParentMessageSource());
+ }
+ }
+ if (logger.isDebugEnabled()) {
+ logger.debug("Using MessageSource [" + this.messageSource + "]");
+ }
+ }
+ else {
+ // Use empty MessageSource to be able to accept getMessage calls.
+ DelegatingMessageSource dms = new DelegatingMessageSource();
+ dms.setParentMessageSource(getInternalParentMessageSource());
+ this.messageSource = dms;
+ beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
+ "': using default [" + this.messageSource + "]");
+ }
+ }
+ }
+
+ /**
+ * Initialize the ApplicationEventMulticaster.
+ * Uses SimpleApplicationEventMulticaster if none defined in the context.
+ * @see org.springframework.context.event.SimpleApplicationEventMulticaster
+ */
+ protected void initApplicationEventMulticaster() {
+ ConfigurableListableBeanFactory beanFactory = getBeanFactory();
+ if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
+ this.applicationEventMulticaster = (ApplicationEventMulticaster)
+ beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
+ }
+ }
+ else {
+ this.applicationEventMulticaster = new SimpleApplicationEventMulticaster();
+ beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
+ APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
+ "': using default [" + this.applicationEventMulticaster + "]");
+ }
+ }
+ }
+
+ /**
+ * Template method which can be overridden to add context-specific refresh work.
+ * Called on initialization of special beans, before instantiation of singletons.
+ *
This implementation is empty.
+ * @throws BeansException in case of errors
+ * @see #refresh()
+ */
+ protected void onRefresh() throws BeansException {
+ // For subclasses: do nothing by default.
+ }
+
+ /**
+ * Add beans that implement ApplicationListener as listeners.
+ * Doesn't affect other listeners, which can be added without being beans.
+ */
+ protected void registerListeners() {
+ // Register statically specified listeners first.
+ for (Iterator it = getApplicationListeners().iterator(); it.hasNext();) {
+ addListener((ApplicationListener) it.next());
+ }
+ // Do not initialize FactoryBeans here: We need to leave all regular beans
+ // uninitialized to let post-processors apply to them!
+ Collection listenerBeans = getBeansOfType(ApplicationListener.class, true, false).values();
+ for (Iterator it = listenerBeans.iterator(); it.hasNext();) {
+ addListener((ApplicationListener) it.next());
+ }
+ }
+
+ /**
+ * Subclasses can invoke this method to register a listener.
+ * Any beans in the context that are listeners are automatically added.
+ * @param listener the listener to register
+ */
+ protected void addListener(ApplicationListener listener) {
+ getApplicationEventMulticaster().addApplicationListener(listener);
+ }
+
+ /**
+ * Finish the initialization of this context's bean factory,
+ * initializing all remaining singleton beans.
+ */
+ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
+ // Stop using the temporary ClassLoader for type matching.
+ beanFactory.setTempClassLoader(null);
+
+ // Allow for caching all bean definition metadata, not expecting further changes.
+ beanFactory.freezeConfiguration();
+
+ // Instantiate all remaining (non-lazy-init) singletons.
+ beanFactory.preInstantiateSingletons();
+ }
+
+ /**
+ * Finish the refresh of this context, publishing the
+ * {@link org.springframework.context.event.ContextRefreshedEvent}.
+ */
+ protected void finishRefresh() {
+ publishEvent(new ContextRefreshedEvent(this));
+ }
+
+ /**
+ * Cancel this context's refresh attempt, resetting the active flag
+ * after an exception got thrown.
+ * @param ex the exception that led to the cancellation
+ */
+ protected void cancelRefresh(BeansException ex) {
+ synchronized (this.activeMonitor) {
+ this.active = false;
+ }
+ }
+
+
+ /**
+ * Register a shutdown hook with the JVM runtime, closing this context
+ * on JVM shutdown unless it has already been closed at that time.
+ *
Delegates to doClose() for the actual closing procedure.
+ * @see java.lang.Runtime#addShutdownHook
+ * @see #close()
+ * @see #doClose()
+ */
+ public void registerShutdownHook() {
+ if (this.shutdownHook == null) {
+ // No shutdown hook registered yet.
+ this.shutdownHook = new Thread() {
+ public void run() {
+ doClose();
+ }
+ };
+ Runtime.getRuntime().addShutdownHook(this.shutdownHook);
+ }
+ }
+
+ /**
+ * DisposableBean callback for destruction of this instance.
+ * Only called when the ApplicationContext itself is running
+ * as a bean in another BeanFactory or ApplicationContext,
+ * which is rather unusual.
+ *
The close method is the native way to
+ * shut down an ApplicationContext.
+ * @see #close()
+ * @see org.springframework.beans.factory.access.SingletonBeanFactoryLocator
+ */
+ public void destroy() {
+ close();
+ }
+
+ /**
+ * Close this application context, destroying all beans in its bean factory.
+ *
Delegates to doClose() for the actual closing procedure.
+ * Also removes a JVM shutdown hook, if registered, as it's not needed anymore.
+ * @see #doClose()
+ * @see #registerShutdownHook()
+ */
+ public void close() {
+ synchronized (this.startupShutdownMonitor) {
+ doClose();
+ // If we registered a JVM shutdown hook, we don't need it anymore now:
+ // We've already explicitly closed the context.
+ if (this.shutdownHook != null) {
+ Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
+ }
+ }
+ }
+
+ /**
+ * Actually performs context closing: publishes a ContextClosedEvent and
+ * destroys the singletons in the bean factory of this application context.
+ *
Called by both close() and a JVM shutdown hook, if any.
+ * @see org.springframework.context.event.ContextClosedEvent
+ * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons()
+ * @see #close()
+ * @see #registerShutdownHook()
+ */
+ protected void doClose() {
+ if (isActive()) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Closing " + this);
+ }
+ try {
+ // Publish shutdown event.
+ publishEvent(new ContextClosedEvent(this));
+ }
+ catch (Throwable ex) {
+ logger.error("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
+ }
+ // Stop all Lifecycle beans, to avoid delays during individual destruction.
+ Map lifecycleBeans = getLifecycleBeans();
+ for (Iterator it = new LinkedHashSet(lifecycleBeans.keySet()).iterator(); it.hasNext();) {
+ String beanName = (String) it.next();
+ doStop(lifecycleBeans, beanName);
+ }
+ // Destroy all cached singletons in the context's BeanFactory.
+ destroyBeans();
+ // Close the state of this context itself.
+ closeBeanFactory();
+ onClose();
+ synchronized (this.activeMonitor) {
+ this.active = false;
+ }
+ }
+ }
+
+ /**
+ * Template method for destroying all beans that this context manages.
+ * The default implementation destroy all cached singletons in this context,
+ * invoking DisposableBean.destroy() and/or the specified
+ * "destroy-method".
+ *
Can be overridden to add context-specific bean destruction steps
+ * right before or right after standard singleton destruction,
+ * while the context's BeanFactory is still active.
+ * @see #getBeanFactory()
+ * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons()
+ */
+ protected void destroyBeans() {
+ getBeanFactory().destroySingletons();
+ }
+
+ /**
+ * Template method which can be overridden to add context-specific shutdown work.
+ * The default implementation is empty.
+ *
Called at the end of {@link #doClose}'s shutdown procedure, after
+ * this context's BeanFactory has been closed. If custom shutdown logic
+ * needs to execute while the BeanFactory is still active, override
+ * the {@link #destroyBeans()} method instead.
+ */
+ protected void onClose() {
+ // For subclasses: do nothing by default.
+ }
+
+ public boolean isActive() {
+ synchronized (this.activeMonitor) {
+ return this.active;
+ }
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of BeanFactory interface
+ //---------------------------------------------------------------------
+
+ public Object getBean(String name) throws BeansException {
+ return getBeanFactory().getBean(name);
+ }
+
+ public Object getBean(String name, Class requiredType) throws BeansException {
+ return getBeanFactory().getBean(name, requiredType);
+ }
+
+ public Object getBean(String name, Object[] args) throws BeansException {
+ return getBeanFactory().getBean(name, args);
+ }
+
+ public boolean containsBean(String name) {
+ return getBeanFactory().containsBean(name);
+ }
+
+ public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+ return getBeanFactory().isSingleton(name);
+ }
+
+ public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
+ return getBeanFactory().isPrototype(name);
+ }
+
+ public boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException {
+ return getBeanFactory().isTypeMatch(name, targetType);
+ }
+
+ public Class getType(String name) throws NoSuchBeanDefinitionException {
+ return getBeanFactory().getType(name);
+ }
+
+ public String[] getAliases(String name) {
+ return getBeanFactory().getAliases(name);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of ListableBeanFactory interface
+ //---------------------------------------------------------------------
+
+ public boolean containsBeanDefinition(String name) {
+ return getBeanFactory().containsBeanDefinition(name);
+ }
+
+ public int getBeanDefinitionCount() {
+ return getBeanFactory().getBeanDefinitionCount();
+ }
+
+ public String[] getBeanDefinitionNames() {
+ return getBeanFactory().getBeanDefinitionNames();
+ }
+
+ public String[] getBeanNamesForType(Class type) {
+ return getBeanFactory().getBeanNamesForType(type);
+ }
+
+ public String[] getBeanNamesForType(Class type, boolean includePrototypes, boolean allowEagerInit) {
+ return getBeanFactory().getBeanNamesForType(type, includePrototypes, allowEagerInit);
+ }
+
+ public Map getBeansOfType(Class type) throws BeansException {
+ return getBeanFactory().getBeansOfType(type);
+ }
+
+ public Map getBeansOfType(Class type, boolean includePrototypes, boolean allowEagerInit)
+ throws BeansException {
+
+ return getBeanFactory().getBeansOfType(type, includePrototypes, allowEagerInit);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of HierarchicalBeanFactory interface
+ //---------------------------------------------------------------------
+
+ public BeanFactory getParentBeanFactory() {
+ return getParent();
+ }
+
+ public boolean containsLocalBean(String name) {
+ return getBeanFactory().containsLocalBean(name);
+ }
+
+ /**
+ * Return the internal bean factory of the parent context if it implements
+ * ConfigurableApplicationContext; else, return the parent context itself.
+ * @see org.springframework.context.ConfigurableApplicationContext#getBeanFactory
+ */
+ protected BeanFactory getInternalParentBeanFactory() {
+ return (getParent() instanceof ConfigurableApplicationContext) ?
+ ((ConfigurableApplicationContext) getParent()).getBeanFactory() : (BeanFactory) getParent();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of MessageSource interface
+ //---------------------------------------------------------------------
+
+ public String getMessage(String code, Object args[], String defaultMessage, Locale locale) {
+ return getMessageSource().getMessage(code, args, defaultMessage, locale);
+ }
+
+ public String getMessage(String code, Object args[], Locale locale) throws NoSuchMessageException {
+ return getMessageSource().getMessage(code, args, locale);
+ }
+
+ public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
+ return getMessageSource().getMessage(resolvable, locale);
+ }
+
+ /**
+ * Return the internal MessageSource used by the context.
+ * @return the internal MessageSource (never null)
+ * @throws IllegalStateException if the context has not been initialized yet
+ */
+ private MessageSource getMessageSource() throws IllegalStateException {
+ if (this.messageSource == null) {
+ throw new IllegalStateException("MessageSource not initialized - " +
+ "call 'refresh' before accessing messages via the context: " + this);
+ }
+ return this.messageSource;
+ }
+
+ /**
+ * Return the internal message source of the parent context if it is an
+ * AbstractApplicationContext too; else, return the parent context itself.
+ */
+ protected MessageSource getInternalParentMessageSource() {
+ return (getParent() instanceof AbstractApplicationContext) ?
+ ((AbstractApplicationContext) getParent()).messageSource : getParent();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of ResourcePatternResolver interface
+ //---------------------------------------------------------------------
+
+ public Resource[] getResources(String locationPattern) throws IOException {
+ return this.resourcePatternResolver.getResources(locationPattern);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of Lifecycle interface
+ //---------------------------------------------------------------------
+
+ public void start() {
+ Map lifecycleBeans = getLifecycleBeans();
+ for (Iterator it = new LinkedHashSet(lifecycleBeans.keySet()).iterator(); it.hasNext();) {
+ String beanName = (String) it.next();
+ doStart(lifecycleBeans, beanName);
+ }
+ publishEvent(new ContextStartedEvent(this));
+ }
+
+ public void stop() {
+ Map lifecycleBeans = getLifecycleBeans();
+ for (Iterator it = new LinkedHashSet(lifecycleBeans.keySet()).iterator(); it.hasNext();) {
+ String beanName = (String) it.next();
+ doStop(lifecycleBeans, beanName);
+ }
+ publishEvent(new ContextStoppedEvent(this));
+ }
+
+ public boolean isRunning() {
+ Iterator it = getLifecycleBeans().values().iterator();
+ while (it.hasNext()) {
+ Lifecycle lifecycle = (Lifecycle) it.next();
+ if (!lifecycle.isRunning()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return a Map of all singleton beans that implement the
+ * Lifecycle interface in this context.
+ * @return Map of Lifecycle beans with bean name as key
+ */
+ private Map getLifecycleBeans() {
+ ConfigurableListableBeanFactory beanFactory = getBeanFactory();
+ String[] beanNames = beanFactory.getSingletonNames();
+ Map beans = new LinkedHashMap();
+ for (int i = 0; i < beanNames.length; i++) {
+ Object bean = beanFactory.getSingleton(beanNames[i]);
+ if (bean instanceof Lifecycle) {
+ beans.put(beanNames[i], bean);
+ }
+ }
+ return beans;
+ }
+
+ /**
+ * Start the specified bean as part of the given set of Lifecycle beans,
+ * making sure that any beans that it depends on are started first.
+ * @param lifecycleBeans Map with bean name as key and Lifecycle instance as value
+ * @param beanName the name of the bean to start
+ */
+ private void doStart(Map lifecycleBeans, String beanName) {
+ Lifecycle bean = (Lifecycle) lifecycleBeans.get(beanName);
+ if (bean != null) {
+ String[] dependenciesForBean = getBeanFactory().getDependenciesForBean(beanName);
+ for (int i = 0; i < dependenciesForBean.length; i++) {
+ doStart(lifecycleBeans, dependenciesForBean[i]);
+ }
+ if (!bean.isRunning()) {
+ bean.start();
+ }
+ lifecycleBeans.remove(beanName);
+ }
+ }
+
+ /**
+ * Stop the specified bean as part of the given set of Lifecycle beans,
+ * making sure that any beans that depends on it are stopped first.
+ * @param lifecycleBeans Map with bean name as key and Lifecycle instance as value
+ * @param beanName the name of the bean to stop
+ */
+ private void doStop(Map lifecycleBeans, String beanName) {
+ Lifecycle bean = (Lifecycle) lifecycleBeans.get(beanName);
+ if (bean != null) {
+ String[] dependentBeans = getBeanFactory().getDependentBeans(beanName);
+ for (int i = 0; i < dependentBeans.length; i++) {
+ doStop(lifecycleBeans, dependentBeans[i]);
+ }
+ if (bean.isRunning()) {
+ bean.stop();
+ }
+ lifecycleBeans.remove(beanName);
+ }
+ }
+
+
+ //---------------------------------------------------------------------
+ // Abstract methods that must be implemented by subclasses
+ //---------------------------------------------------------------------
+
+ /**
+ * Subclasses must implement this method to perform the actual configuration load.
+ * The method is invoked by {@link #refresh()} before any other initialization work.
+ *
A subclass will either create a new bean factory and hold a reference to it,
+ * or return a single BeanFactory instance that it holds. In the latter case, it will
+ * usually throw an IllegalStateException if refreshing the context more than once.
+ * @throws BeansException if initialization of the bean factory failed
+ * @throws IllegalStateException if already initialized and multiple refresh
+ * attempts are not supported
+ */
+ protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
+
+ /**
+ * Subclasses must implement this method to release their internal bean factory.
+ * This method gets invoked by {@link #close()} after all other shutdown work.
+ *
Should never throw an exception but rather log shutdown failures.
+ */
+ protected abstract void closeBeanFactory();
+
+ /**
+ * Subclasses must return their internal bean factory here. They should implement the
+ * lookup efficiently, so that it can be called repeatedly without a performance penalty.
+ *
Note: Subclasses should check whether the context is still active before
+ * returning the internal bean factory. The internal factory should generally be
+ * considered unavailable once the context has been closed.
+ * @return this application context's internal bean factory (never null)
+ * @throws IllegalStateException if the context does not hold an internal bean factory yet
+ * (usually if {@link #refresh()} has never been called) or if the context has been
+ * closed already
+ * @see #refreshBeanFactory()
+ * @see #closeBeanFactory()
+ */
+ public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
+
+
+ /**
+ * Return information about this context.
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer(getId());
+ sb.append(": display name [").append(getDisplayName());
+ sb.append("]; startup date [").append(new Date(getStartupDate()));
+ sb.append("]; ");
+ ApplicationContext parent = getParent();
+ if (parent == null) {
+ sb.append("root of context hierarchy");
+ }
+ else {
+ sb.append("parent: ").append(parent.getId());
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * BeanPostProcessor that logs an info message when a bean is created during
+ * BeanPostProcessor instantiation, i.e. when a bean is not eligible for
+ * getting processed by all BeanPostProcessors.
+ */
+ private class BeanPostProcessorChecker implements BeanPostProcessor {
+
+ private final ConfigurableListableBeanFactory beanFactory;
+
+ private final int beanPostProcessorTargetCount;
+
+ public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) {
+ this.beanFactory = beanFactory;
+ this.beanPostProcessorTargetCount = beanPostProcessorTargetCount;
+ }
+
+ public Object postProcessBeforeInitialization(Object bean, String beanName) {
+ return bean;
+ }
+
+ public Object postProcessAfterInitialization(Object bean, String beanName) {
+ if (!(bean instanceof BeanPostProcessor) &&
+ this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Bean '" + beanName + "' is not eligible for getting processed by all " +
+ "BeanPostProcessors (for example: not eligible for auto-proxying)");
+ }
+ }
+ return bean;
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractMessageSource.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractMessageSource.java
new file mode 100644
index 00000000000..47dad021c40
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractMessageSource.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.springframework.context.HierarchicalMessageSource;
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Abstract implementation of the {@link HierarchicalMessageSource} interface,
+ * implementing common handling of message variants, making it easy
+ * to implement a specific strategy for a concrete MessageSource.
+ *
+ *
Subclasses must implement the abstract {@link #resolveCode}
+ * method. For efficient resolution of messages without arguments, the
+ * {@link #resolveCodeWithoutArguments} method should be overridden
+ * as well, resolving messages without a MessageFormat being involved.
+ *
+ *
Note: By default, message texts are only parsed through
+ * MessageFormat if arguments have been passed in for the message. In case
+ * of no arguments, message texts will be returned as-is. As a consequence,
+ * you should only use MessageFormat escaping for messages with actual
+ * arguments, and keep all other messages unescaped. If you prefer to
+ * escape all messages, set the "alwaysUseMessageFormat" flag to "true".
+ *
+ *
Supports not only MessageSourceResolvables as primary messages
+ * but also resolution of message arguments that are in turn
+ * MessageSourceResolvables themselves.
+ *
+ *
This class does not implement caching of messages per code, thus
+ * subclasses can dynamically change messages over time. Subclasses are
+ * encouraged to cache their messages in a modification-aware fashion,
+ * allowing for hot deployment of updated messages.
+ *
+ * @author Juergen Hoeller
+ * @author Rod Johnson
+ * @see #resolveCode(String, java.util.Locale)
+ * @see #resolveCodeWithoutArguments(String, java.util.Locale)
+ * @see #setAlwaysUseMessageFormat
+ * @see java.text.MessageFormat
+ */
+public abstract class AbstractMessageSource extends MessageSourceSupport implements HierarchicalMessageSource {
+
+ private MessageSource parentMessageSource;
+
+ private boolean useCodeAsDefaultMessage = false;
+
+
+ public void setParentMessageSource(MessageSource parent) {
+ this.parentMessageSource = parent;
+ }
+
+ public MessageSource getParentMessageSource() {
+ return this.parentMessageSource;
+ }
+
+ /**
+ * Set whether to use the message code as default message instead of
+ * throwing a NoSuchMessageException. Useful for development and debugging.
+ * Default is "false".
+ *
Note: In case of a MessageSourceResolvable with multiple codes
+ * (like a FieldError) and a MessageSource that has a parent MessageSource,
+ * do not activate "useCodeAsDefaultMessage" in the parent:
+ * Else, you'll get the first code returned as message by the parent,
+ * without attempts to check further codes.
+ *
To be able to work with "useCodeAsDefaultMessage" turned on in the parent,
+ * AbstractMessageSource and AbstractApplicationContext contain special checks
+ * to delegate to the internal getMessageInternal method if available.
+ * In general, it is recommended to just use "useCodeAsDefaultMessage" during
+ * development and not rely on it in production in the first place, though.
+ * @see #getMessage(String, Object[], Locale)
+ * @see #getMessageInternal
+ * @see org.springframework.validation.FieldError
+ */
+ public void setUseCodeAsDefaultMessage(boolean useCodeAsDefaultMessage) {
+ this.useCodeAsDefaultMessage = useCodeAsDefaultMessage;
+ }
+
+ /**
+ * Return whether to use the message code as default message instead of
+ * throwing a NoSuchMessageException. Useful for development and debugging.
+ * Default is "false".
+ *
Alternatively, consider overriding the getDefaultMessage
+ * method to return a custom fallback message for an unresolvable code.
+ * @see #getDefaultMessage(String)
+ */
+ protected boolean isUseCodeAsDefaultMessage() {
+ return this.useCodeAsDefaultMessage;
+ }
+
+
+ public final String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
+ String msg = getMessageInternal(code, args, locale);
+ if (msg != null) {
+ return msg;
+ }
+ if (defaultMessage == null) {
+ String fallback = getDefaultMessage(code);
+ if (fallback != null) {
+ return fallback;
+ }
+ }
+ return renderDefaultMessage(defaultMessage, args, locale);
+ }
+
+ public final String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
+ String msg = getMessageInternal(code, args, locale);
+ if (msg != null) {
+ return msg;
+ }
+ String fallback = getDefaultMessage(code);
+ if (fallback != null) {
+ return fallback;
+ }
+ throw new NoSuchMessageException(code, locale);
+ }
+
+ public final String getMessage(MessageSourceResolvable resolvable, Locale locale)
+ throws NoSuchMessageException {
+
+ String[] codes = resolvable.getCodes();
+ if (codes == null) {
+ codes = new String[0];
+ }
+ for (int i = 0; i < codes.length; i++) {
+ String msg = getMessageInternal(codes[i], resolvable.getArguments(), locale);
+ if (msg != null) {
+ return msg;
+ }
+ }
+ if (resolvable.getDefaultMessage() != null) {
+ return renderDefaultMessage(resolvable.getDefaultMessage(), resolvable.getArguments(), locale);
+ }
+ if (codes.length > 0) {
+ String fallback = getDefaultMessage(codes[0]);
+ if (fallback != null) {
+ return fallback;
+ }
+ }
+ throw new NoSuchMessageException(codes.length > 0 ? codes[codes.length - 1] : null, locale);
+ }
+
+
+ /**
+ * Resolve the given code and arguments as message in the given Locale,
+ * returning null if not found. Does not fall back to
+ * the code as default message. Invoked by getMessage methods.
+ * @param code the code to lookup up, such as 'calculator.noRateSet'
+ * @param args array of arguments that will be filled in for params
+ * within the message
+ * @param locale the Locale in which to do the lookup
+ * @return the resolved message, or null if not found
+ * @see #getMessage(String, Object[], String, Locale)
+ * @see #getMessage(String, Object[], Locale)
+ * @see #getMessage(MessageSourceResolvable, Locale)
+ * @see #setUseCodeAsDefaultMessage
+ */
+ protected String getMessageInternal(String code, Object[] args, Locale locale) {
+ if (code == null) {
+ return null;
+ }
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+ Object[] argsToUse = args;
+
+ if (!isAlwaysUseMessageFormat() && ObjectUtils.isEmpty(args)) {
+ // Optimized resolution: no arguments to apply,
+ // therefore no MessageFormat needs to be involved.
+ // Note that the default implementation still uses MessageFormat;
+ // this can be overridden in specific subclasses.
+ String message = resolveCodeWithoutArguments(code, locale);
+ if (message != null) {
+ return message;
+ }
+ }
+
+ else {
+ // Resolve arguments eagerly, for the case where the message
+ // is defined in a parent MessageSource but resolvable arguments
+ // are defined in the child MessageSource.
+ argsToUse = resolveArguments(args, locale);
+
+ MessageFormat messageFormat = resolveCode(code, locale);
+ if (messageFormat != null) {
+ synchronized (messageFormat) {
+ return messageFormat.format(argsToUse);
+ }
+ }
+ }
+
+ // Not found -> check parent, if any.
+ return getMessageFromParent(code, argsToUse, locale);
+ }
+
+ /**
+ * Try to retrieve the given message from the parent MessageSource, if any.
+ * @param code the code to lookup up, such as 'calculator.noRateSet'
+ * @param args array of arguments that will be filled in for params
+ * within the message
+ * @param locale the Locale in which to do the lookup
+ * @return the resolved message, or null if not found
+ * @see #getParentMessageSource()
+ */
+ protected String getMessageFromParent(String code, Object[] args, Locale locale) {
+ MessageSource parent = getParentMessageSource();
+ if (parent != null) {
+ if (parent instanceof AbstractMessageSource) {
+ // Call internal method to avoid getting the default code back
+ // in case of "useCodeAsDefaultMessage" being activated.
+ return ((AbstractMessageSource) parent).getMessageInternal(code, args, locale);
+ }
+ else {
+ // Check parent MessageSource, returning null if not found there.
+ return parent.getMessage(code, args, null, locale);
+ }
+ }
+ // Not found in parent either.
+ return null;
+ }
+
+ /**
+ * Return a fallback default message for the given code, if any.
+ *
Default is to return the code itself if "useCodeAsDefaultMessage"
+ * is activated, or return no fallback else. In case of no fallback,
+ * the caller will usually receive a NoSuchMessageException from
+ * getMessage.
+ * @param code the message code that we couldn't resolve
+ * and that we didn't receive an explicit default message for
+ * @return the default message to use, or null if none
+ * @see #setUseCodeAsDefaultMessage
+ */
+ protected String getDefaultMessage(String code) {
+ if (isUseCodeAsDefaultMessage()) {
+ return code;
+ }
+ return null;
+ }
+
+ /**
+ * Render the given default message String. The default message is
+ * passed in as specified by the caller and can be rendered into
+ * a fully formatted default message shown to the user.
+ *
The default implementation passes the String to formatMessage,
+ * resolving any argument placeholders found in them. Subclasses may override
+ * this method to plug in custom processing of default messages.
+ * @param defaultMessage the passed-in default message String
+ * @param args array of arguments that will be filled in for params within
+ * the message, or null if none.
+ * @param locale the Locale used for formatting
+ * @return the rendered default message (with resolved arguments)
+ * @see #formatMessage(String, Object[], java.util.Locale)
+ */
+ protected String renderDefaultMessage(String defaultMessage, Object[] args, Locale locale) {
+ return formatMessage(defaultMessage, args, locale);
+ }
+
+
+ /**
+ * Searches through the given array of objects, find any
+ * MessageSourceResolvable objects and resolve them.
+ *
Allows for messages to have MessageSourceResolvables as arguments.
+ * @param args array of arguments for a message
+ * @param locale the locale to resolve through
+ * @return an array of arguments with any MessageSourceResolvables resolved
+ */
+ protected Object[] resolveArguments(Object[] args, Locale locale) {
+ if (args == null) {
+ return new Object[0];
+ }
+ List resolvedArgs = new ArrayList(args.length);
+ for (int i = 0; i < args.length; i++) {
+ if (args[i] instanceof MessageSourceResolvable) {
+ resolvedArgs.add(getMessage((MessageSourceResolvable) args[i], locale));
+ }
+ else {
+ resolvedArgs.add(args[i]);
+ }
+ }
+ return resolvedArgs.toArray(new Object[resolvedArgs.size()]);
+ }
+
+ /**
+ * Subclasses can override this method to resolve a message without
+ * arguments in an optimized fashion, that is, to resolve a message
+ * without involving a MessageFormat.
+ *
The default implementation does use MessageFormat,
+ * through delegating to the resolveCode method.
+ * Subclasses are encouraged to replace this with optimized resolution.
+ *
Unfortunately, java.text.MessageFormat is not
+ * implemented in an efficient fashion. In particular, it does not
+ * detect that a message pattern doesn't contain argument placeholders
+ * in the first place. Therefore, it's advisable to circumvent
+ * MessageFormat completely for messages without arguments.
+ * @param code the code of the message to resolve
+ * @param locale the Locale to resolve the code for
+ * (subclasses are encouraged to support internationalization)
+ * @return the message String, or null if not found
+ * @see #resolveCode
+ * @see java.text.MessageFormat
+ */
+ protected String resolveCodeWithoutArguments(String code, Locale locale) {
+ MessageFormat messageFormat = resolveCode(code, locale);
+ if (messageFormat != null) {
+ synchronized (messageFormat) {
+ return messageFormat.format(new Object[0]);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Subclasses must implement this method to resolve a message.
+ *
Returns a MessageFormat instance rather than a message String,
+ * to allow for appropriate caching of MessageFormats in subclasses.
+ *
Subclasses are encouraged to provide optimized resolution
+ * for messages without arguments, not involving MessageFormat.
+ * See resolveCodeWithoutArguments javadoc for details.
+ * @param code the code of the message to resolve
+ * @param locale the Locale to resolve the code for
+ * (subclasses are encouraged to support internationalization)
+ * @return the MessageFormat for the message, or null if not found
+ * @see #resolveCodeWithoutArguments(String, java.util.Locale)
+ */
+ protected abstract MessageFormat resolveCode(String code, Locale locale);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
new file mode 100644
index 00000000000..88e56ca179f
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableApplicationContext.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.io.IOException;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextException;
+
+/**
+ * Base class for {@link org.springframework.context.ApplicationContext}
+ * implementations which are supposed to support multiple refreshs,
+ * creating a new internal bean factory instance every time.
+ * Typically (but not necessarily), such a context will be driven by
+ * a set of config locations to load bean definitions from.
+ *
+ *
The only method to be implemented by subclasses is {@link #loadBeanDefinitions},
+ * which gets invoked on each refresh. A concrete implementation is supposed to load
+ * bean definitions into the given
+ * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory},
+ * typically delegating to one or more specific bean definition readers.
+ *
+ *
Note that there is a similar base class for WebApplicationContexts.
+ * {@link org.springframework.web.context.support.AbstractRefreshableWebApplicationContext}
+ * provides the same subclassing strategy, but additionally pre-implements
+ * all context functionality for web environments. There is also a
+ * pre-defined way to receive config locations for a web context.
+ *
+ *
Concrete standalone subclasses of this base class, reading in a
+ * specific bean definition format, are {@link ClassPathXmlApplicationContext}
+ * and {@link FileSystemXmlApplicationContext}, which both derive from the
+ * common {@link AbstractXmlApplicationContext} base class.
+ *
+ * @author Juergen Hoeller
+ * @since 1.1.3
+ * @see #loadBeanDefinitions
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
+ * @see org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
+ * @see AbstractXmlApplicationContext
+ * @see ClassPathXmlApplicationContext
+ * @see FileSystemXmlApplicationContext
+ */
+public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
+
+ private Boolean allowBeanDefinitionOverriding;
+
+ private Boolean allowCircularReferences;
+
+ /** Bean factory for this context */
+ private DefaultListableBeanFactory beanFactory;
+
+ /** Synchronization monitor for the internal BeanFactory */
+ private final Object beanFactoryMonitor = new Object();
+
+
+ /**
+ * Create a new AbstractRefreshableApplicationContext with no parent.
+ */
+ public AbstractRefreshableApplicationContext() {
+ }
+
+ /**
+ * Create a new AbstractRefreshableApplicationContext with the given parent context.
+ * @param parent the parent context
+ */
+ public AbstractRefreshableApplicationContext(ApplicationContext parent) {
+ super(parent);
+ }
+
+
+ /**
+ * Set whether it should be allowed to override bean definitions by registering
+ * a different definition with the same name, automatically replacing the former.
+ * If not, an exception will be thrown. Default is "true".
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
+ */
+ public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
+ this.allowBeanDefinitionOverriding = Boolean.valueOf(allowBeanDefinitionOverriding);
+ }
+
+ /**
+ * Set whether to allow circular references between beans - and automatically
+ * try to resolve them.
+ *
Default is "true". Turn this off to throw an exception when encountering
+ * a circular reference, disallowing them completely.
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
+ */
+ public void setAllowCircularReferences(boolean allowCircularReferences) {
+ this.allowCircularReferences = Boolean.valueOf(allowCircularReferences);
+ }
+
+
+ /**
+ * This implementation performs an actual refresh of this context's underlying
+ * bean factory, shutting down the previous bean factory (if any) and
+ * initializing a fresh bean factory for the next phase of the context's lifecycle.
+ */
+ protected final void refreshBeanFactory() throws BeansException {
+ if (hasBeanFactory()) {
+ destroyBeans();
+ closeBeanFactory();
+ }
+ try {
+ DefaultListableBeanFactory beanFactory = createBeanFactory();
+ customizeBeanFactory(beanFactory);
+ loadBeanDefinitions(beanFactory);
+ synchronized (this.beanFactoryMonitor) {
+ this.beanFactory = beanFactory;
+ }
+ }
+ catch (IOException ex) {
+ throw new ApplicationContextException(
+ "I/O error parsing XML document for application context [" + getDisplayName() + "]", ex);
+ }
+ }
+
+ protected final void closeBeanFactory() {
+ synchronized (this.beanFactoryMonitor) {
+ this.beanFactory = null;
+ }
+ }
+
+ /**
+ * Determine whether this context currently holds a bean factory,
+ * i.e. has been refreshed at least once and not been closed yet.
+ */
+ protected final boolean hasBeanFactory() {
+ synchronized (this.beanFactoryMonitor) {
+ return (this.beanFactory != null);
+ }
+ }
+
+ public final ConfigurableListableBeanFactory getBeanFactory() {
+ synchronized (this.beanFactoryMonitor) {
+ if (this.beanFactory == null) {
+ throw new IllegalStateException("BeanFactory not initialized or already closed - " +
+ "call 'refresh' before accessing beans via the ApplicationContext");
+ }
+ return this.beanFactory;
+ }
+ }
+
+
+ /**
+ * Create an internal bean factory for this context.
+ * Called for each {@link #refresh()} attempt.
+ *
The default implementation creates a
+ * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
+ * with the {@link #getInternalParentBeanFactory() internal bean factory} of this
+ * context's parent as parent bean factory. Can be overridden in subclasses,
+ * for example to customize DefaultListableBeanFactory's settings.
+ * @return the bean factory for this context
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowEagerClassLoading
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowCircularReferences
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
+ */
+ protected DefaultListableBeanFactory createBeanFactory() {
+ return new DefaultListableBeanFactory(getInternalParentBeanFactory());
+ }
+
+ /**
+ * Customize the internal bean factory used by this context.
+ * Called for each {@link #refresh()} attempt.
+ *
The default implementation applies this context's
+ * {@link #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
+ * and {@link #setAllowCircularReferences "allowCircularReferences"} settings,
+ * if specified. Can be overridden in subclasses to customize any of
+ * {@link DefaultListableBeanFactory}'s settings.
+ * @param beanFactory the newly created bean factory for this context
+ * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
+ * @see DefaultListableBeanFactory#setAllowCircularReferences
+ * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
+ * @see DefaultListableBeanFactory#setAllowEagerClassLoading
+ */
+ protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
+ if (this.allowBeanDefinitionOverriding != null) {
+ beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding.booleanValue());
+ }
+ if (this.allowCircularReferences != null) {
+ beanFactory.setAllowCircularReferences(this.allowCircularReferences.booleanValue());
+ }
+ }
+
+ /**
+ * Load bean definitions into the given bean factory, typically through
+ * delegating to one or more bean definition readers.
+ * @param beanFactory the bean factory to load bean definitions into
+ * @throws IOException if loading of bean definition files failed
+ * @throws BeansException if parsing of the bean definitions failed
+ * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ */
+ protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
+ throws IOException, BeansException;
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java
new file mode 100644
index 00000000000..a2ff6dd3c85
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractRefreshableConfigApplicationContext.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.springframework.util.SystemPropertyUtils;
+
+/**
+ * {@link AbstractRefreshableApplicationContext} subclass that adds common handling
+ * of specified config locations. Serves as base class for XML-based application
+ * context implementations such as {@link ClassPathXmlApplicationContext} and
+ * {@link FileSystemXmlApplicationContext}, as well as
+ * {@link org.springframework.web.context.support.XmlWebApplicationContext} and
+ * {@link org.springframework.web.portlet.context.XmlPortletApplicationContext}.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5.2
+ * @see #setConfigLocation
+ * @see #setConfigLocations
+ * @see #getDefaultConfigLocations
+ */
+public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
+ implements BeanNameAware, InitializingBean {
+
+ private String[] configLocations;
+
+ private boolean setIdCalled = false;
+
+
+ /**
+ * Create a new AbstractRefreshableConfigApplicationContext with no parent.
+ */
+ public AbstractRefreshableConfigApplicationContext() {
+ }
+
+ /**
+ * Create a new AbstractRefreshableConfigApplicationContext with the given parent context.
+ * @param parent the parent context
+ */
+ public AbstractRefreshableConfigApplicationContext(ApplicationContext parent) {
+ super(parent);
+ }
+
+
+ /**
+ * Set the config locations for this application context in init-param style,
+ * i.e. with distinct locations separated by commas, semicolons or whitespace.
+ *
If not set, the implementation may use a default as appropriate.
+ */
+ public void setConfigLocation(String location) {
+ setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
+ }
+
+ /**
+ * Set the config locations for this application context.
+ *
If not set, the implementation may use a default as appropriate.
+ */
+ public void setConfigLocations(String[] locations) {
+ if (locations != null) {
+ Assert.noNullElements(locations, "Config locations must not be null");
+ this.configLocations = new String[locations.length];
+ for (int i = 0; i < locations.length; i++) {
+ this.configLocations[i] = resolvePath(locations[i]).trim();
+ }
+ }
+ else {
+ this.configLocations = null;
+ }
+ }
+
+ /**
+ * Return an array of resource locations, referring to the XML bean definition
+ * files that this context should be built with. Can also include location
+ * patterns, which will get resolved via a ResourcePatternResolver.
+ *
The default implementation returns null. Subclasses can override
+ * this to provide a set of resource locations to load bean definitions from.
+ * @return an array of resource locations, or null if none
+ * @see #getResources
+ * @see #getResourcePatternResolver
+ */
+ protected String[] getConfigLocations() {
+ return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());
+ }
+
+ /**
+ * Return the default config locations to use, for the case where no
+ * explicit config locations have been specified.
+ *
The default implementation returns null,
+ * requiring explicit config locations.
+ * @return an array of default config locations, if any
+ * @see #setConfigLocations
+ */
+ protected String[] getDefaultConfigLocations() {
+ return null;
+ }
+
+ /**
+ * Resolve the given path, replacing placeholders with corresponding
+ * system property values if necessary. Applied to config locations.
+ * @param path the original file path
+ * @return the resolved file path
+ * @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
+ */
+ protected String resolvePath(String path) {
+ return SystemPropertyUtils.resolvePlaceholders(path);
+ }
+
+
+ public void setId(String id) {
+ super.setId(id);
+ this.setIdCalled = true;
+ }
+
+ /**
+ * Sets the id of this context to the bean name by default,
+ * for cases where the context instance is itself defined as a bean.
+ */
+ public void setBeanName(String name) {
+ if (!this.setIdCalled) {
+ super.setId(name);
+ }
+ }
+
+ /**
+ * Triggers {@link #refresh()} if not refreshed in the concrete context's
+ * constructor already.
+ */
+ public void afterPropertiesSet() {
+ if (!isActive()) {
+ refresh();
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/AbstractXmlApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractXmlApplicationContext.java
new file mode 100644
index 00000000000..24c7266e92f
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/AbstractXmlApplicationContext.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.io.IOException;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.factory.xml.ResourceEntityResolver;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
+
+/**
+ * Convenient base class for {@link org.springframework.context.ApplicationContext}
+ * implementations, drawing configuration from XML documents containing bean definitions
+ * understood by an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
+ *
+ *
Subclasses just have to implement the {@link #getConfigResources} and/or
+ * the {@link #getConfigLocations} method. Furthermore, they might override
+ * the {@link #getResourceByPath} hook to interpret relative paths in an
+ * environment-specific fashion, and/or {@link #getResourcePatternResolver}
+ * for extended pattern resolution.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #getConfigResources
+ * @see #getConfigLocations
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ */
+public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
+
+ /**
+ * Create a new AbstractXmlApplicationContext with no parent.
+ */
+ public AbstractXmlApplicationContext() {
+ }
+
+ /**
+ * Create a new AbstractXmlApplicationContext with the given parent context.
+ * @param parent the parent context
+ */
+ public AbstractXmlApplicationContext(ApplicationContext parent) {
+ super(parent);
+ }
+
+
+ /**
+ * Loads the bean definitions via an XmlBeanDefinitionReader.
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ * @see #initBeanDefinitionReader
+ * @see #loadBeanDefinitions
+ */
+ protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
+ // Create a new XmlBeanDefinitionReader for the given BeanFactory.
+ XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
+
+ // Configure the bean definition reader with this context's
+ // resource loading environment.
+ beanDefinitionReader.setResourceLoader(this);
+ beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
+
+ // Allow a subclass to provide custom initialization of the reader,
+ // then proceed with actually loading the bean definitions.
+ initBeanDefinitionReader(beanDefinitionReader);
+ loadBeanDefinitions(beanDefinitionReader);
+ }
+
+ /**
+ * Initialize the bean definition reader used for loading the bean
+ * definitions of this context. Default implementation is empty.
+ *
Can be overridden in subclasses, e.g. for turning off XML validation
+ * or using a different XmlBeanDefinitionParser implementation.
+ * @param beanDefinitionReader the bean definition reader used by this context
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
+ */
+ protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
+ }
+
+ /**
+ * Load the bean definitions with the given XmlBeanDefinitionReader.
+ *
The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
+ * method; hence this method is just supposed to load and/or register bean definitions.
+ * @param reader the XmlBeanDefinitionReader to use
+ * @throws BeansException in case of bean registration errors
+ * @throws IOException if the required XML document isn't found
+ * @see #refreshBeanFactory
+ * @see #getConfigLocations
+ * @see #getResources
+ * @see #getResourcePatternResolver
+ */
+ protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
+ Resource[] configResources = getConfigResources();
+ if (configResources != null) {
+ reader.loadBeanDefinitions(configResources);
+ }
+ String[] configLocations = getConfigLocations();
+ if (configLocations != null) {
+ reader.loadBeanDefinitions(configLocations);
+ }
+ }
+
+ /**
+ * Return an array of Resource objects, referring to the XML bean definition
+ * files that this context should be built with.
+ *
The default implementation returns null. Subclasses can override
+ * this to provide pre-built Resource objects rather than location Strings.
+ * @return an array of Resource objects, or null if none
+ * @see #getConfigLocations()
+ */
+ protected Resource[] getConfigResources() {
+ return null;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationContextAwareProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationContextAwareProcessor.java
new file mode 100644
index 00000000000..e904facbd11
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationContextAwareProcessor.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.context.MessageSourceAware;
+import org.springframework.context.ResourceLoaderAware;
+
+/**
+ * {@link org.springframework.beans.factory.config.BeanPostProcessor}
+ * implementation that passes the ApplicationContext to beans that
+ * implement the {@link ResourceLoaderAware}, {@link MessageSourceAware},
+ * {@link ApplicationEventPublisherAware} and/or
+ * {@link ApplicationContextAware} interfaces.
+ * If all of them are implemented, they are satisfied in the given order.
+ *
+ *
Application contexts will automatically register this with their
+ * underlying bean factory. Applications do not use this directly.
+ *
+ * @author Juergen Hoeller
+ * @since 10.10.2003
+ * @see org.springframework.context.ResourceLoaderAware
+ * @see org.springframework.context.MessageSourceAware
+ * @see org.springframework.context.ApplicationEventPublisherAware
+ * @see org.springframework.context.ApplicationContextAware
+ * @see org.springframework.context.support.AbstractApplicationContext#refresh()
+ */
+class ApplicationContextAwareProcessor implements BeanPostProcessor {
+
+ private final ApplicationContext applicationContext;
+
+
+ /**
+ * Create a new ApplicationContextAwareProcessor for the given context.
+ */
+ public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof ResourceLoaderAware) {
+ ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
+ }
+ if (bean instanceof ApplicationEventPublisherAware) {
+ ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
+ }
+ if (bean instanceof MessageSourceAware) {
+ ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
+ }
+ if (bean instanceof ApplicationContextAware) {
+ ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
+ }
+ return bean;
+ }
+
+ public Object postProcessAfterInitialization(Object bean, String name) {
+ return bean;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationObjectSupport.java b/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationObjectSupport.java
new file mode 100644
index 00000000000..0a0acb70f7b
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/ApplicationObjectSupport.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationContextException;
+
+/**
+ * Convenient superclass for application objects that want to be aware of
+ * the application context, e.g. for custom lookup of collaborating beans
+ * or for context-specific resource access. It saves the application
+ * context reference and provides an initialization callback method.
+ * Furthermore, it offers numerous convenience methods for message lookup.
+ *
+ *
There is no requirement to subclass this class: It just makes things
+ * a little easier if you need access to the context, e.g. for access to
+ * file resources or to the message source. Note that many application
+ * objects do not need to be aware of the application context at all,
+ * as they can receive collaborating beans via bean references.
+ *
+ *
Many framework classes are derived from this class, particularly
+ * within the web support.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see org.springframework.web.context.support.WebApplicationObjectSupport
+ */
+public abstract class ApplicationObjectSupport implements ApplicationContextAware {
+
+ /** Logger that is available to subclasses */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ /** ApplicationContext this object runs in */
+ private ApplicationContext applicationContext;
+
+ /** MessageSourceAccessor for easy message access */
+ private MessageSourceAccessor messageSourceAccessor;
+
+
+ public final void setApplicationContext(ApplicationContext context) throws BeansException {
+ if (context == null && !isContextRequired()) {
+ // Reset internal context state.
+ this.applicationContext = null;
+ this.messageSourceAccessor = null;
+ }
+ else if (this.applicationContext == null) {
+ // Initialize with passed-in context.
+ if (!requiredContextClass().isInstance(context)) {
+ throw new ApplicationContextException(
+ "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
+ }
+ this.applicationContext = context;
+ this.messageSourceAccessor = new MessageSourceAccessor(context);
+ initApplicationContext(context);
+ }
+ else {
+ // Ignore reinitialization if same context passed in.
+ if (this.applicationContext != context) {
+ throw new ApplicationContextException(
+ "Cannot reinitialize with different application context: current one is [" +
+ this.applicationContext + "], passed-in one is [" + context + "]");
+ }
+ }
+ }
+
+ /**
+ * Determine whether this application object needs to run in an ApplicationContext.
+ *
Default is "false". Can be overridden to enforce running in a context
+ * (i.e. to throw IllegalStateException on accessors if outside a context).
+ * @see #getApplicationContext
+ * @see #getMessageSourceAccessor
+ */
+ protected boolean isContextRequired() {
+ return false;
+ }
+
+ /**
+ * Determine the context class that any context passed to
+ * setApplicationContext must be an instance of.
+ * Can be overridden in subclasses.
+ * @see #setApplicationContext
+ */
+ protected Class requiredContextClass() {
+ return ApplicationContext.class;
+ }
+
+ /**
+ * Subclasses can override this for custom initialization behavior.
+ * Gets called by setApplicationContext after setting the context instance.
+ *
Note: Does not get called on reinitialization of the context
+ * but rather just on first initialization of this object's context reference.
+ *
The default implementation calls the overloaded {@link #initApplicationContext()}
+ * method without ApplicationContext reference.
+ * @param context the containing ApplicationContext
+ * @throws ApplicationContextException in case of initialization errors
+ * @throws BeansException if thrown by ApplicationContext methods
+ * @see #setApplicationContext
+ */
+ protected void initApplicationContext(ApplicationContext context) throws BeansException {
+ initApplicationContext();
+ }
+
+ /**
+ * Subclasses can override this for custom initialization behavior.
+ *
The default implementation is empty. Called by
+ * {@link #initApplicationContext(org.springframework.context.ApplicationContext)}.
+ * @throws ApplicationContextException in case of initialization errors
+ * @throws BeansException if thrown by ApplicationContext methods
+ * @see #setApplicationContext
+ */
+ protected void initApplicationContext() throws BeansException {
+ }
+
+
+ /**
+ * Return the ApplicationContext that this object is associated with.
+ * @throws IllegalStateException if not running in an ApplicationContext
+ */
+ public final ApplicationContext getApplicationContext() throws IllegalStateException {
+ if (this.applicationContext == null && isContextRequired()) {
+ throw new IllegalStateException(
+ "ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
+ }
+ return this.applicationContext;
+ }
+
+ /**
+ * Return a MessageSourceAccessor for the application context
+ * used by this object, for easy message access.
+ * @throws IllegalStateException if not running in an ApplicationContext
+ */
+ protected final MessageSourceAccessor getMessageSourceAccessor() throws IllegalStateException {
+ if (this.messageSourceAccessor == null && isContextRequired()) {
+ throw new IllegalStateException(
+ "ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
+ }
+ return this.messageSourceAccessor;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ClassPathXmlApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/ClassPathXmlApplicationContext.java
new file mode 100644
index 00000000000..d22a74e1e16
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/ClassPathXmlApplicationContext.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
+
+/**
+ * Standalone XML application context, taking the context definition files
+ * from the class path, interpreting plain paths as class path resource names
+ * that include the package path (e.g. "mypackage/myresource.txt"). Useful for
+ * test harnesses as well as for application contexts embedded within JARs.
+ *
+ *
The config location defaults can be overridden via {@link #getConfigLocations},
+ * Config locations can either denote concrete files like "/myfiles/context.xml"
+ * or Ant-style patterns like "/myfiles/*-context.xml" (see the
+ * {@link org.springframework.util.AntPathMatcher} javadoc for pattern details).
+ *
+ *
Note: In case of multiple config locations, later bean definitions will
+ * override ones defined in earlier loaded files. This can be leveraged to
+ * deliberately override certain bean definitions via an extra XML file.
+ *
+ *
This is a simple, one-stop shop convenience ApplicationContext.
+ * Consider using the {@link GenericApplicationContext} class in combination
+ * with an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}
+ * for more flexible context setup.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #getResource
+ * @see #getResourceByPath
+ * @see GenericApplicationContext
+ */
+public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
+
+ private Resource[] configResources;
+
+
+ /**
+ * Create a new ClassPathXmlApplicationContext for bean-style configuration.
+ * @see #setConfigLocation
+ * @see #setConfigLocations
+ * @see #afterPropertiesSet()
+ */
+ public ClassPathXmlApplicationContext() {
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext for bean-style configuration.
+ * @param parent the parent context
+ * @see #setConfigLocation
+ * @see #setConfigLocations
+ * @see #afterPropertiesSet()
+ */
+ public ClassPathXmlApplicationContext(ApplicationContext parent) {
+ super(parent);
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext, loading the definitions
+ * from the given XML file and automatically refreshing the context.
+ * @param configLocation resource location
+ * @throws BeansException if context creation failed
+ */
+ public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
+ this(new String[] {configLocation}, true, null);
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext, loading the definitions
+ * from the given XML files and automatically refreshing the context.
+ * @param configLocations array of resource locations
+ * @throws BeansException if context creation failed
+ */
+ public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
+ this(configLocations, true, null);
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext with the given parent,
+ * loading the definitions from the given XML files and automatically
+ * refreshing the context.
+ * @param configLocations array of resource locations
+ * @param parent the parent context
+ * @throws BeansException if context creation failed
+ */
+ public ClassPathXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
+ this(configLocations, true, parent);
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext, loading the definitions
+ * from the given XML files.
+ * @param configLocations array of resource locations
+ * @param refresh whether to automatically refresh the context,
+ * loading all bean definitions and creating all singletons.
+ * Alternatively, call refresh manually after further configuring the context.
+ * @throws BeansException if context creation failed
+ * @see #refresh()
+ */
+ public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
+ this(configLocations, refresh, null);
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext with the given parent,
+ * loading the definitions from the given XML files.
+ * @param configLocations array of resource locations
+ * @param refresh whether to automatically refresh the context,
+ * loading all bean definitions and creating all singletons.
+ * Alternatively, call refresh manually after further configuring the context.
+ * @param parent the parent context
+ * @throws BeansException if context creation failed
+ * @see #refresh()
+ */
+ public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
+ throws BeansException {
+
+ super(parent);
+ setConfigLocations(configLocations);
+ if (refresh) {
+ refresh();
+ }
+ }
+
+
+ /**
+ * Create a new ClassPathXmlApplicationContext, loading the definitions
+ * from the given XML file and automatically refreshing the context.
+ *
This is a convenience method to load class path resources relative to a
+ * given Class. For full flexibility, consider using a GenericApplicationContext
+ * with an XmlBeanDefinitionReader and a ClassPathResource argument.
+ * @param path relative (or absolute) path within the class path
+ * @param clazz the class to load resources with (basis for the given paths)
+ * @throws BeansException if context creation failed
+ * @see org.springframework.core.io.ClassPathResource#ClassPathResource(String, Class)
+ * @see org.springframework.context.support.GenericApplicationContext
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ */
+ public ClassPathXmlApplicationContext(String path, Class clazz) throws BeansException {
+ this(new String[] {path}, clazz);
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext, loading the definitions
+ * from the given XML files and automatically refreshing the context.
+ * @param paths array of relative (or absolute) paths within the class path
+ * @param clazz the class to load resources with (basis for the given paths)
+ * @throws BeansException if context creation failed
+ * @see org.springframework.core.io.ClassPathResource#ClassPathResource(String, Class)
+ * @see org.springframework.context.support.GenericApplicationContext
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ */
+ public ClassPathXmlApplicationContext(String[] paths, Class clazz) throws BeansException {
+ this(paths, clazz, null);
+ }
+
+ /**
+ * Create a new ClassPathXmlApplicationContext with the given parent,
+ * loading the definitions from the given XML files and automatically
+ * refreshing the context.
+ * @param paths array of relative (or absolute) paths within the class path
+ * @param clazz the class to load resources with (basis for the given paths)
+ * @param parent the parent context
+ * @throws BeansException if context creation failed
+ * @see org.springframework.core.io.ClassPathResource#ClassPathResource(String, Class)
+ * @see org.springframework.context.support.GenericApplicationContext
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ */
+ public ClassPathXmlApplicationContext(String[] paths, Class clazz, ApplicationContext parent)
+ throws BeansException {
+
+ super(parent);
+ Assert.notNull(paths, "Path array must not be null");
+ Assert.notNull(clazz, "Class argument must not be null");
+ this.configResources = new Resource[paths.length];
+ for (int i = 0; i < paths.length; i++) {
+ this.configResources[i] = new ClassPathResource(paths[i], clazz);
+ }
+ refresh();
+ }
+
+
+ protected Resource[] getConfigResources() {
+ return this.configResources;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ContextTypeMatchClassLoader.java b/org.springframework.context/src/main/java/org/springframework/context/support/ContextTypeMatchClassLoader.java
new file mode 100644
index 00000000000..dd7f3477351
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/ContextTypeMatchClassLoader.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.springframework.core.DecoratingClassLoader;
+import org.springframework.core.OverridingClassLoader;
+import org.springframework.core.SmartClassLoader;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * Special variant of an overriding ClassLoader, used for temporary type
+ * matching in {@link AbstractApplicationContext}. Redefines classes from
+ * a cached byte array for every loadClass call in order to
+ * pick up recently loaded types in the parent ClassLoader.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see AbstractApplicationContext
+ * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setTempClassLoader
+ */
+class ContextTypeMatchClassLoader extends DecoratingClassLoader implements SmartClassLoader {
+
+ private static Method findLoadedClassMethod;
+
+ static {
+ try {
+ findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[] {String.class});
+ }
+ catch (NoSuchMethodException ex) {
+ throw new IllegalStateException("Invalid [java.lang.ClassLoader] class: no 'findLoadedClass' method defined!");
+ }
+ }
+
+
+ /** Cache for byte array per class name */
+ private final Map bytesCache = new HashMap();
+
+
+ public ContextTypeMatchClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ public Class loadClass(String name) throws ClassNotFoundException {
+ return new ContextOverridingClassLoader(getParent()).loadClass(name);
+ }
+
+ public boolean isClassReloadable(Class clazz) {
+ return (clazz.getClassLoader() instanceof ContextOverridingClassLoader);
+ }
+
+
+ /**
+ * ClassLoader to be created for each loaded class.
+ * Caches class file content but redefines class for each call.
+ */
+ private class ContextOverridingClassLoader extends OverridingClassLoader {
+
+ public ContextOverridingClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ protected boolean isEligibleForOverriding(String className) {
+ if (isExcluded(className) || ContextTypeMatchClassLoader.this.isExcluded(className)) {
+ return false;
+ }
+ ReflectionUtils.makeAccessible(findLoadedClassMethod);
+ ClassLoader parent = getParent();
+ while (parent != null) {
+ if (ReflectionUtils.invokeMethod(findLoadedClassMethod, parent, new Object[] {className}) != null) {
+ return false;
+ }
+ parent = parent.getParent();
+ }
+ return true;
+ }
+
+ protected Class loadClassForOverriding(String name) throws ClassNotFoundException {
+ byte[] bytes = (byte[]) bytesCache.get(name);
+ if (bytes == null) {
+ bytes = loadBytesForClass(name);
+ if (bytes != null) {
+ bytesCache.put(name, bytes);
+ }
+ else {
+ return null;
+ }
+ }
+ return defineClass(name, bytes, 0, bytes.length);
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java b/org.springframework.context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java
new file mode 100644
index 00000000000..4809dc12215
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/DefaultMessageSourceResolvable.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.io.Serializable;
+
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Default implementation of the {@link MessageSourceResolvable} interface.
+ * Offers an easy way to store all the necessary values needed to resolve
+ * a message via a {@link org.springframework.context.MessageSource}.
+ *
+ * @author Juergen Hoeller
+ * @since 13.02.2004
+ * @see org.springframework.context.MessageSource#getMessage(MessageSourceResolvable, java.util.Locale)
+ */
+public class DefaultMessageSourceResolvable implements MessageSourceResolvable, Serializable {
+
+ private final String[] codes;
+
+ private final Object[] arguments;
+
+ private final String defaultMessage;
+
+
+ /**
+ * Create a new DefaultMessageSourceResolvable.
+ * @param code the code to be used to resolve this message
+ */
+ public DefaultMessageSourceResolvable(String code) {
+ this(new String[] {code}, null, null);
+ }
+
+ /**
+ * Create a new DefaultMessageSourceResolvable.
+ * @param codes the codes to be used to resolve this message
+ */
+ public DefaultMessageSourceResolvable(String[] codes) {
+ this(codes, null, null);
+ }
+
+ /**
+ * Create a new DefaultMessageSourceResolvable.
+ * @param codes the codes to be used to resolve this message
+ * @param defaultMessage the default message to be used to resolve this message
+ */
+ public DefaultMessageSourceResolvable(String[] codes, String defaultMessage) {
+ this(codes, null, defaultMessage);
+ }
+
+ /**
+ * Create a new DefaultMessageSourceResolvable.
+ * @param codes the codes to be used to resolve this message
+ * @param arguments the array of arguments to be used to resolve this message
+ */
+ public DefaultMessageSourceResolvable(String[] codes, Object[] arguments) {
+ this(codes, arguments, null);
+ }
+
+ /**
+ * Create a new DefaultMessageSourceResolvable.
+ * @param codes the codes to be used to resolve this message
+ * @param arguments the array of arguments to be used to resolve this message
+ * @param defaultMessage the default message to be used to resolve this message
+ */
+ public DefaultMessageSourceResolvable(String[] codes, Object[] arguments, String defaultMessage) {
+ this.codes = codes;
+ this.arguments = arguments;
+ this.defaultMessage = defaultMessage;
+ }
+
+ /**
+ * Copy constructor: Create a new instance from another resolvable.
+ * @param resolvable the resolvable to copy from
+ */
+ public DefaultMessageSourceResolvable(MessageSourceResolvable resolvable) {
+ this(resolvable.getCodes(), resolvable.getArguments(), resolvable.getDefaultMessage());
+ }
+
+
+ public String[] getCodes() {
+ return this.codes;
+ }
+
+ /**
+ * Return the default code of this resolvable, that is,
+ * the last one in the codes array.
+ */
+ public String getCode() {
+ return (this.codes != null && this.codes.length > 0) ? this.codes[this.codes.length - 1] : null;
+ }
+
+ public Object[] getArguments() {
+ return this.arguments;
+ }
+
+ public String getDefaultMessage() {
+ return this.defaultMessage;
+ }
+
+
+ /**
+ * Build a default String representation for this MessageSourceResolvable:
+ * including codes, arguments, and default message.
+ */
+ protected final String resolvableToString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("codes [").append(StringUtils.arrayToDelimitedString(this.codes, ","));
+ buf.append("]; arguments [" + StringUtils.arrayToDelimitedString(this.arguments, ","));
+ buf.append("]; default message [").append(this.defaultMessage).append(']');
+ return buf.toString();
+ }
+
+ /**
+ * Default implementation exposes the attributes of this MessageSourceResolvable.
+ * To be overridden in more specific subclasses, potentially including the
+ * resolvable content through resolvableToString().
+ * @see #resolvableToString()
+ */
+ public String toString() {
+ return getClass().getName() + ": " + resolvableToString();
+ }
+
+
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof MessageSourceResolvable)) {
+ return false;
+ }
+ MessageSourceResolvable otherResolvable = (MessageSourceResolvable) other;
+ return ObjectUtils.nullSafeEquals(getCodes(), otherResolvable.getCodes()) &&
+ ObjectUtils.nullSafeEquals(getArguments(), otherResolvable.getArguments()) &&
+ ObjectUtils.nullSafeEquals(getDefaultMessage(), otherResolvable.getDefaultMessage());
+ }
+
+ public int hashCode() {
+ int hashCode = ObjectUtils.nullSafeHashCode(getCodes());
+ hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(getArguments());
+ hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(getDefaultMessage());
+ return hashCode;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/DelegatingMessageSource.java b/org.springframework.context/src/main/java/org/springframework/context/support/DelegatingMessageSource.java
new file mode 100644
index 00000000000..c42e38d5eaa
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/DelegatingMessageSource.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.util.Locale;
+
+import org.springframework.context.HierarchicalMessageSource;
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.context.NoSuchMessageException;
+
+/**
+ * Empty {@link MessageSource} that delegates all calls to the parent MessageSource.
+ * If no parent is available, it simply won't resolve any message.
+ *
+ *
Used as placeholder by AbstractApplicationContext, if the context doesn't
+ * define its own MessageSource. Not intended for direct use in applications.
+ *
+ * @author Juergen Hoeller
+ * @since 1.1.5
+ * @see AbstractApplicationContext
+ */
+public class DelegatingMessageSource extends MessageSourceSupport implements HierarchicalMessageSource {
+
+ private MessageSource parentMessageSource;
+
+
+ public void setParentMessageSource(MessageSource parent) {
+ this.parentMessageSource = parent;
+ }
+
+ public MessageSource getParentMessageSource() {
+ return this.parentMessageSource;
+ }
+
+
+ public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
+ if (this.parentMessageSource != null) {
+ return this.parentMessageSource.getMessage(code, args, defaultMessage, locale);
+ }
+ else {
+ return renderDefaultMessage(defaultMessage, args, locale);
+ }
+ }
+
+ public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
+ if (this.parentMessageSource != null) {
+ return this.parentMessageSource.getMessage(code, args, locale);
+ }
+ else {
+ throw new NoSuchMessageException(code, locale);
+ }
+ }
+
+ public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
+ if (this.parentMessageSource != null) {
+ return this.parentMessageSource.getMessage(resolvable, locale);
+ }
+ else {
+ if (resolvable.getDefaultMessage() != null) {
+ return renderDefaultMessage(resolvable.getDefaultMessage(), resolvable.getArguments(), locale);
+ }
+ String[] codes = resolvable.getCodes();
+ String code = (codes != null && codes.length > 0 ? codes[0] : null);
+ throw new NoSuchMessageException(code, locale);
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/FileSystemXmlApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/FileSystemXmlApplicationContext.java
new file mode 100644
index 00000000000..c09dfdfddc3
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/FileSystemXmlApplicationContext.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+
+/**
+ * Standalone XML application context, taking the context definition files
+ * from the file system or from URLs, interpreting plain paths as relative
+ * file system locations (e.g. "mydir/myfile.txt"). Useful for test harnesses
+ * as well as for standalone environments.
+ *
+ *
NOTE: Plain paths will always be interpreted as relative
+ * to the current VM working directory, even if they start with a slash.
+ * (This is consistent with the semantics in a Servlet container.)
+ * Use an explicit "file:" prefix to enforce an absolute file path.
+ *
+ *
The config location defaults can be overridden via {@link #getConfigLocations},
+ * Config locations can either denote concrete files like "/myfiles/context.xml"
+ * or Ant-style patterns like "/myfiles/*-context.xml" (see the
+ * {@link org.springframework.util.AntPathMatcher} javadoc for pattern details).
+ *
+ *
Note: In case of multiple config locations, later bean definitions will
+ * override ones defined in earlier loaded files. This can be leveraged to
+ * deliberately override certain bean definitions via an extra XML file.
+ *
+ *
This is a simple, one-stop shop convenience ApplicationContext.
+ * Consider using the {@link GenericApplicationContext} class in combination
+ * with an {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}
+ * for more flexible context setup.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #getResource
+ * @see #getResourceByPath
+ * @see GenericApplicationContext
+ */
+public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext {
+
+ /**
+ * Create a new FileSystemXmlApplicationContext for bean-style configuration.
+ * @see #setConfigLocation
+ * @see #setConfigLocations
+ * @see #afterPropertiesSet()
+ */
+ public FileSystemXmlApplicationContext() {
+ }
+
+ /**
+ * Create a new FileSystemXmlApplicationContext for bean-style configuration.
+ * @param parent the parent context
+ * @see #setConfigLocation
+ * @see #setConfigLocations
+ * @see #afterPropertiesSet()
+ */
+ public FileSystemXmlApplicationContext(ApplicationContext parent) {
+ super(parent);
+ }
+
+ /**
+ * Create a new FileSystemXmlApplicationContext, loading the definitions
+ * from the given XML file and automatically refreshing the context.
+ * @param configLocation file path
+ * @throws BeansException if context creation failed
+ */
+ public FileSystemXmlApplicationContext(String configLocation) throws BeansException {
+ this(new String[] {configLocation}, true, null);
+ }
+
+ /**
+ * Create a new FileSystemXmlApplicationContext, loading the definitions
+ * from the given XML files and automatically refreshing the context.
+ * @param configLocations array of file paths
+ * @throws BeansException if context creation failed
+ */
+ public FileSystemXmlApplicationContext(String[] configLocations) throws BeansException {
+ this(configLocations, true, null);
+ }
+
+ /**
+ * Create a new FileSystemXmlApplicationContext with the given parent,
+ * loading the definitions from the given XML files and automatically
+ * refreshing the context.
+ * @param configLocations array of file paths
+ * @param parent the parent context
+ * @throws BeansException if context creation failed
+ */
+ public FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent) throws BeansException {
+ this(configLocations, true, parent);
+ }
+
+ /**
+ * Create a new FileSystemXmlApplicationContext, loading the definitions
+ * from the given XML files.
+ * @param configLocations array of file paths
+ * @param refresh whether to automatically refresh the context,
+ * loading all bean definitions and creating all singletons.
+ * Alternatively, call refresh manually after further configuring the context.
+ * @throws BeansException if context creation failed
+ * @see #refresh()
+ */
+ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh) throws BeansException {
+ this(configLocations, refresh, null);
+ }
+
+ /**
+ * Create a new FileSystemXmlApplicationContext with the given parent,
+ * loading the definitions from the given XML files.
+ * @param configLocations array of file paths
+ * @param refresh whether to automatically refresh the context,
+ * loading all bean definitions and creating all singletons.
+ * Alternatively, call refresh manually after further configuring the context.
+ * @param parent the parent context
+ * @throws BeansException if context creation failed
+ * @see #refresh()
+ */
+ public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
+ throws BeansException {
+
+ super(parent);
+ setConfigLocations(configLocations);
+ if (refresh) {
+ refresh();
+ }
+ }
+
+
+ /**
+ * Resolve resource paths as file system paths.
+ *
Note: Even if a given path starts with a slash, it will get
+ * interpreted as relative to the current VM working directory.
+ * This is consistent with the semantics in a Servlet container.
+ * @param path path to the resource
+ * @return Resource handle
+ * @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
+ */
+ protected Resource getResourceByPath(String path) {
+ if (path != null && path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ return new FileSystemResource(path);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
new file mode 100644
index 00000000000..cc62a89e12f
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/GenericApplicationContext.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.io.IOException;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.util.Assert;
+
+/**
+ * Generic ApplicationContext implementation that holds a single internal
+ * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
+ * instance and does not assume a specific bean definition format. Implements
+ * the {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
+ * interface in order to allow for applying any bean definition readers to it.
+ *
+ *
Typical usage is to register a variety of bean definitions via the
+ * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
+ * interface and then call {@link #refresh()} to initialize those beans
+ * with application context semantics (handling
+ * {@link org.springframework.context.ApplicationContextAware}, auto-detecting
+ * {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessors},
+ * etc).
+ *
+ *
In contrast to other ApplicationContext implementations that create a new
+ * internal BeanFactory instance for each refresh, the internal BeanFactory of
+ * this context is available right from the start, to be able to register bean
+ * definitions on it. {@link #refresh()} may only be called once.
+ *
+ *
+ *
+ * For the typical case of XML bean definitions, simply use
+ * {@link ClassPathXmlApplicationContext} or {@link FileSystemXmlApplicationContext},
+ * which are easier to set up - but less flexible, since you can just use standard
+ * resource locations for XML bean definitions, rather than mixing arbitrary bean
+ * definition formats. The equivalent in a web environment is
+ * {@link org.springframework.web.context.support.XmlWebApplicationContext}.
+ *
+ *
For custom application context implementations that are supposed to read
+ * special bean definition formats in a refreshable manner, consider deriving
+ * from the {@link AbstractRefreshableApplicationContext} base class.
+ *
+ * @author Juergen Hoeller
+ * @since 1.1.2
+ * @see #registerBeanDefinition
+ * @see #refresh()
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ * @see org.springframework.beans.factory.support.PropertiesBeanDefinitionReader
+ */
+public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
+
+ private final DefaultListableBeanFactory beanFactory;
+
+ private ResourceLoader resourceLoader;
+
+ private boolean refreshed = false;
+
+
+ /**
+ * Create a new GenericApplicationContext.
+ * @see #registerBeanDefinition
+ * @see #refresh
+ */
+ public GenericApplicationContext() {
+ this.beanFactory = new DefaultListableBeanFactory();
+ }
+
+ /**
+ * Create a new GenericApplicationContext with the given DefaultListableBeanFactory.
+ * @param beanFactory the DefaultListableBeanFactory instance to use for this context
+ * @see #registerBeanDefinition
+ * @see #refresh
+ */
+ public GenericApplicationContext(DefaultListableBeanFactory beanFactory) {
+ Assert.notNull(beanFactory, "BeanFactory must not be null");
+ this.beanFactory = beanFactory;
+ }
+
+ /**
+ * Create a new GenericApplicationContext with the given parent.
+ * @param parent the parent application context
+ * @see #registerBeanDefinition
+ * @see #refresh
+ */
+ public GenericApplicationContext(ApplicationContext parent) {
+ this();
+ setParent(parent);
+ }
+
+ /**
+ * Create a new GenericApplicationContext with the given DefaultListableBeanFactory.
+ * @param beanFactory the DefaultListableBeanFactory instance to use for this context
+ * @param parent the parent application context
+ * @see #registerBeanDefinition
+ * @see #refresh
+ */
+ public GenericApplicationContext(DefaultListableBeanFactory beanFactory, ApplicationContext parent) {
+ this(beanFactory);
+ setParent(parent);
+ }
+
+
+ /**
+ * Set the parent of this application context, also setting
+ * the parent of the internal BeanFactory accordingly.
+ * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setParentBeanFactory
+ */
+ public void setParent(ApplicationContext parent) {
+ super.setParent(parent);
+ this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory());
+ }
+
+ /**
+ * Set a ResourceLoader to use for this context. If set, the context will
+ * delegate all getResource calls to the given ResourceLoader.
+ * If not set, default resource loading will apply.
+ *
The main reason to specify a custom ResourceLoader is to resolve
+ * resource paths (withour URL prefix) in a specific fashion.
+ * The default behavior is to resolve such paths as class path locations.
+ * To resolve resource paths as file system locations, specify a
+ * FileSystemResourceLoader here.
+ *
You can also pass in a full ResourcePatternResolver, which will
+ * be autodetected by the context and used for getResources
+ * calls as well. Else, default resource pattern matching will apply.
+ * @see #getResource
+ * @see org.springframework.core.io.DefaultResourceLoader
+ * @see org.springframework.core.io.FileSystemResourceLoader
+ * @see org.springframework.core.io.support.ResourcePatternResolver
+ * @see #getResources
+ */
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = resourceLoader;
+ }
+
+
+ /**
+ * This implementation delegates to this context's ResourceLoader if set,
+ * falling back to the default superclass behavior else.
+ * @see #setResourceLoader
+ */
+ public Resource getResource(String location) {
+ if (this.resourceLoader != null) {
+ return this.resourceLoader.getResource(location);
+ }
+ return super.getResource(location);
+ }
+
+ /**
+ * This implementation delegates to this context's ResourceLoader if it
+ * implements the ResourcePatternResolver interface, falling back to the
+ * default superclass behavior else.
+ * @see #setResourceLoader
+ */
+ public Resource[] getResources(String locationPattern) throws IOException {
+ if (this.resourceLoader instanceof ResourcePatternResolver) {
+ return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern);
+ }
+ return super.getResources(locationPattern);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementations of AbstractApplicationContext's template methods
+ //---------------------------------------------------------------------
+
+ /**
+ * Do nothing: We hold a single internal BeanFactory and rely on callers
+ * to register beans through our public methods (or the BeanFactory's).
+ * @see #registerBeanDefinition
+ */
+ protected final void refreshBeanFactory() throws IllegalStateException {
+ if (this.refreshed) {
+ throw new IllegalStateException(
+ "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
+ }
+ this.refreshed = true;
+ }
+
+ /**
+ * Do nothing: We hold a single internal BeanFactory that will never
+ * get released.
+ */
+ protected final void closeBeanFactory() {
+ }
+
+ /**
+ * Return the single internal BeanFactory held by this context
+ * (as ConfigurableListableBeanFactory).
+ */
+ public final ConfigurableListableBeanFactory getBeanFactory() {
+ return this.beanFactory;
+ }
+
+ /**
+ * Return the underlying bean factory of this context,
+ * available for registering bean definitions.
+ *
NOTE: You need to call {@link #refresh()} to initialize the
+ * bean factory and its contained beans with application context semantics
+ * (autodetecting BeanFactoryPostProcessors, etc).
+ * @return the internal bean factory (as DefaultListableBeanFactory)
+ */
+ public final DefaultListableBeanFactory getDefaultListableBeanFactory() {
+ return this.beanFactory;
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of BeanDefinitionRegistry
+ //---------------------------------------------------------------------
+
+ public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
+ throws BeanDefinitionStoreException {
+
+ this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
+ }
+
+ public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
+ this.beanFactory.removeBeanDefinition(beanName);
+ }
+
+ public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
+ return this.beanFactory.getBeanDefinition(beanName);
+ }
+
+ public boolean isBeanNameInUse(String beanName) {
+ return this.beanFactory.isBeanNameInUse(beanName);
+ }
+
+ public void registerAlias(String beanName, String alias) {
+ this.beanFactory.registerAlias(beanName, alias);
+ }
+
+ public void removeAlias(String alias) {
+ this.beanFactory.removeAlias(alias);
+ }
+
+ public boolean isAlias(String beanName) {
+ return this.beanFactory.isAlias(beanName);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceAccessor.java b/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceAccessor.java
new file mode 100644
index 00000000000..2d977350913
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceAccessor.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.util.Locale;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.MessageSourceResolvable;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+/**
+ * Helper class for easy access to messages from a MessageSource,
+ * providing various overloaded getMessage methods.
+ *
+ *
Available from ApplicationObjectSupport, but also reusable
+ * as a standalone helper to delegate to in application objects.
+ *
+ * @author Juergen Hoeller
+ * @since 23.10.2003
+ * @see ApplicationObjectSupport#getMessageSourceAccessor
+ */
+public class MessageSourceAccessor {
+
+ private final MessageSource messageSource;
+
+ private final Locale defaultLocale;
+
+ /**
+ * Create a new MessageSourceAccessor, using LocaleContextHolder's locale
+ * as default locale.
+ * @param messageSource the MessageSource to wrap
+ * @see org.springframework.context.i18n.LocaleContextHolder#getLocale()
+ */
+ public MessageSourceAccessor(MessageSource messageSource) {
+ this.messageSource = messageSource;
+ this.defaultLocale = null;
+ }
+
+ /**
+ * Create a new MessageSourceAccessor, using the given default locale.
+ * @param messageSource the MessageSource to wrap
+ * @param defaultLocale the default locale to use for message access
+ */
+ public MessageSourceAccessor(MessageSource messageSource, Locale defaultLocale) {
+ this.messageSource = messageSource;
+ this.defaultLocale = defaultLocale;
+ }
+
+ /**
+ * Return the default locale to use if no explicit locale has been given.
+ *
The default implementation returns the default locale passed into the
+ * corresponding constructor, or LocaleContextHolder's locale as fallback.
+ * Can be overridden in subclasses.
+ * @see #MessageSourceAccessor(org.springframework.context.MessageSource, java.util.Locale)
+ * @see org.springframework.context.i18n.LocaleContextHolder#getLocale()
+ */
+ protected Locale getDefaultLocale() {
+ return (this.defaultLocale != null ? this.defaultLocale : LocaleContextHolder.getLocale());
+ }
+
+ /**
+ * Retrieve the message for the given code and the default Locale.
+ * @param code code of the message
+ * @param defaultMessage String to return if the lookup fails
+ * @return the message
+ */
+ public String getMessage(String code, String defaultMessage) {
+ return this.messageSource.getMessage(code, null, defaultMessage, getDefaultLocale());
+ }
+
+ /**
+ * Retrieve the message for the given code and the given Locale.
+ * @param code code of the message
+ * @param defaultMessage String to return if the lookup fails
+ * @param locale Locale in which to do lookup
+ * @return the message
+ */
+ public String getMessage(String code, String defaultMessage, Locale locale) {
+ return this.messageSource.getMessage(code, null, defaultMessage, locale);
+ }
+
+ /**
+ * Retrieve the message for the given code and the default Locale.
+ * @param code code of the message
+ * @param args arguments for the message, or null if none
+ * @param defaultMessage String to return if the lookup fails
+ * @return the message
+ */
+ public String getMessage(String code, Object[] args, String defaultMessage) {
+ return this.messageSource.getMessage(code, args, defaultMessage, getDefaultLocale());
+ }
+
+ /**
+ * Retrieve the message for the given code and the given Locale.
+ * @param code code of the message
+ * @param args arguments for the message, or null if none
+ * @param defaultMessage String to return if the lookup fails
+ * @param locale Locale in which to do lookup
+ * @return the message
+ */
+ public String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
+ return this.messageSource.getMessage(code, args, defaultMessage, locale);
+ }
+
+ /**
+ * Retrieve the message for the given code and the default Locale.
+ * @param code code of the message
+ * @return the message
+ * @throws org.springframework.context.NoSuchMessageException if not found
+ */
+ public String getMessage(String code) throws NoSuchMessageException {
+ return this.messageSource.getMessage(code, null, getDefaultLocale());
+ }
+
+ /**
+ * Retrieve the message for the given code and the given Locale.
+ * @param code code of the message
+ * @param locale Locale in which to do lookup
+ * @return the message
+ * @throws org.springframework.context.NoSuchMessageException if not found
+ */
+ public String getMessage(String code, Locale locale) throws NoSuchMessageException {
+ return this.messageSource.getMessage(code, null, locale);
+ }
+
+ /**
+ * Retrieve the message for the given code and the default Locale.
+ * @param code code of the message
+ * @param args arguments for the message, or null if none
+ * @return the message
+ * @throws org.springframework.context.NoSuchMessageException if not found
+ */
+ public String getMessage(String code, Object[] args) throws NoSuchMessageException {
+ return this.messageSource.getMessage(code, args, getDefaultLocale());
+ }
+
+ /**
+ * Retrieve the message for the given code and the given Locale.
+ * @param code code of the message
+ * @param args arguments for the message, or null if none
+ * @param locale Locale in which to do lookup
+ * @return the message
+ * @throws org.springframework.context.NoSuchMessageException if not found
+ */
+ public String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
+ return this.messageSource.getMessage(code, args, locale);
+ }
+
+ /**
+ * Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance)
+ * in the default Locale.
+ * @param resolvable the MessageSourceResolvable
+ * @return the message
+ * @throws org.springframework.context.NoSuchMessageException if not found
+ */
+ public String getMessage(MessageSourceResolvable resolvable) throws NoSuchMessageException {
+ return this.messageSource.getMessage(resolvable, getDefaultLocale());
+ }
+
+ /**
+ * Retrieve the given MessageSourceResolvable (e.g. an ObjectError instance)
+ * in the given Locale.
+ * @param resolvable the MessageSourceResolvable
+ * @param locale Locale in which to do lookup
+ * @return the message
+ * @throws org.springframework.context.NoSuchMessageException if not found
+ */
+ public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
+ return this.messageSource.getMessage(resolvable, locale);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceResourceBundle.java b/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceResourceBundle.java
new file mode 100644
index 00000000000..e8a0e0568a0
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceResourceBundle.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import org.springframework.context.MessageSource;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.util.Assert;
+
+/**
+ * Helper class that allows for accessing a Spring
+ * {@link org.springframework.context.MessageSource} as a {@link java.util.ResourceBundle}.
+ * Used for example to expose a Spring MessageSource to JSTL web views.
+ *
+ * @author Juergen Hoeller
+ * @since 27.02.2003
+ * @see org.springframework.context.MessageSource
+ * @see java.util.ResourceBundle
+ * @see org.springframework.web.servlet.support.JstlUtils#exposeLocalizationContext
+ */
+public class MessageSourceResourceBundle extends ResourceBundle {
+
+ private final MessageSource messageSource;
+
+ private final Locale locale;
+
+
+ /**
+ * Create a new MessageSourceResourceBundle for the given MessageSource and Locale.
+ * @param source the MessageSource to retrieve messages from
+ * @param locale the Locale to retrieve messages for
+ */
+ public MessageSourceResourceBundle(MessageSource source, Locale locale) {
+ Assert.notNull(source, "MessageSource must not be null");
+ this.messageSource = source;
+ this.locale = locale;
+ }
+
+ /**
+ * Create a new MessageSourceResourceBundle for the given MessageSource and Locale.
+ * @param source the MessageSource to retrieve messages from
+ * @param locale the Locale to retrieve messages for
+ * @param parent the parent ResourceBundle to delegate to if no local message found
+ */
+ public MessageSourceResourceBundle(MessageSource source, Locale locale, ResourceBundle parent) {
+ this(source, locale);
+ setParent(parent);
+ }
+
+
+ /**
+ * This implementation resolves the code in the MessageSource.
+ * Returns null if the message could not be resolved.
+ */
+ protected Object handleGetObject(String code) {
+ try {
+ return this.messageSource.getMessage(code, null, this.locale);
+ }
+ catch (NoSuchMessageException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * This implementation returns null, as a MessageSource does
+ * not allow for enumerating the defined message codes.
+ */
+ public Enumeration getKeys() {
+ return null;
+ }
+
+ /**
+ * This implementation exposes the specified Locale for introspection
+ * through the standard ResourceBundle.getLocale() method.
+ */
+ public Locale getLocale() {
+ return this.locale;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceSupport.java b/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceSupport.java
new file mode 100644
index 00000000000..63b19f0c565
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/MessageSourceSupport.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Base class for message source implementations, providing support infrastructure
+ * such as {@link java.text.MessageFormat} handling but not implementing concrete
+ * methods defined in the {@link org.springframework.context.MessageSource}.
+ *
+ *
{@link AbstractMessageSource} derives from this class, providing concrete
+ * getMessage implementations that delegate to a central template
+ * method for message code resolution.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5.5
+ */
+public abstract class MessageSourceSupport {
+
+ /** Logger available to subclasses */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private boolean alwaysUseMessageFormat = false;
+
+ /**
+ * Cache to hold already generated MessageFormats per message.
+ * Used for passed-in default messages. MessageFormats for resolved
+ * codes are cached on a specific basis in subclasses.
+ */
+ private final Map cachedMessageFormats = new HashMap();
+
+
+ /**
+ * Set whether to always apply the MessageFormat rules, parsing even
+ * messages without arguments.
+ *
Default is "false": Messages without arguments are by default
+ * returned as-is, without parsing them through MessageFormat.
+ * Set this to "true" to enforce MessageFormat for all messages,
+ * expecting all message texts to be written with MessageFormat escaping.
+ *
For example, MessageFormat expects a single quote to be escaped
+ * as "''". If your message texts are all written with such escaping,
+ * even when not defining argument placeholders, you need to set this
+ * flag to "true". Else, only message texts with actual arguments
+ * are supposed to be written with MessageFormat escaping.
+ * @see java.text.MessageFormat
+ */
+ public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {
+ this.alwaysUseMessageFormat = alwaysUseMessageFormat;
+ }
+
+ /**
+ * Return whether to always apply the MessageFormat rules, parsing even
+ * messages without arguments.
+ */
+ protected boolean isAlwaysUseMessageFormat() {
+ return this.alwaysUseMessageFormat;
+ }
+
+
+ /**
+ * Format the given message String, using cached MessageFormats.
+ * By default invoked for passed-in default messages, to resolve
+ * any argument placeholders found in them.
+ * @param msg the message to format
+ * @param args array of arguments that will be filled in for params within
+ * the message, or null if none.
+ * @param locale the Locale used for formatting
+ * @return the formatted message (with resolved arguments)
+ */
+ protected String formatMessage(String msg, Object[] args, Locale locale) {
+ if (msg == null || (!this.alwaysUseMessageFormat && (args == null || args.length == 0))) {
+ return msg;
+ }
+ MessageFormat messageFormat = null;
+ synchronized (this.cachedMessageFormats) {
+ messageFormat = (MessageFormat) this.cachedMessageFormats.get(msg);
+ if (messageFormat == null) {
+ messageFormat = createMessageFormat(msg, locale);
+ this.cachedMessageFormats.put(msg, messageFormat);
+ }
+ }
+ synchronized (messageFormat) {
+ return messageFormat.format(resolveArguments(args, locale));
+ }
+ }
+
+ /**
+ * Create a MessageFormat for the given message and Locale.
+ * @param msg the message to create a MessageFormat for
+ * @param locale the Locale to create a MessageFormat for
+ * @return the MessageFormat instance
+ */
+ protected MessageFormat createMessageFormat(String msg, Locale locale) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Creating MessageFormat for pattern [" + msg + "] and locale '" + locale + "'");
+ }
+ return new MessageFormat((msg != null ? msg : ""), locale);
+ }
+
+ /**
+ * Template method for resolving argument objects.
+ *
The default implementation simply returns the given argument
+ * array as-is. Can be overridden in subclasses in order to resolve
+ * special argument types.
+ * @param args the original argument array
+ * @param locale the Locale to resolve against
+ * @return the resolved argument array
+ */
+ protected Object[] resolveArguments(Object[] args, Locale locale) {
+ return args;
+ }
+
+
+ /**
+ * Render the given default message String. The default message is
+ * passed in as specified by the caller and can be rendered into
+ * a fully formatted default message shown to the user.
+ *
The default implementation passes the String to formatMessage,
+ * resolving any argument placeholders found in them. Subclasses may override
+ * this method to plug in custom processing of default messages.
+ * @param defaultMessage the passed-in default message String
+ * @param args array of arguments that will be filled in for params within
+ * the message, or null if none.
+ * @param locale the Locale used for formatting
+ * @return the rendered default message (with resolved arguments)
+ * @see #formatMessage(String, Object[], java.util.Locale)
+ */
+ protected String renderDefaultMessage(String defaultMessage, Object[] args, Locale locale) {
+ return formatMessage(defaultMessage, args, locale);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java b/org.springframework.context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java
new file mode 100644
index 00000000000..e075a3a853a
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/ReloadableResourceBundleMessageSource.java
@@ -0,0 +1,663 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.util.Assert;
+import org.springframework.util.DefaultPropertiesPersister;
+import org.springframework.util.PropertiesPersister;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link org.springframework.context.MessageSource} implementation that
+ * accesses resource bundles using specified basenames. This class uses
+ * {@link java.util.Properties} instances as its custom data structure for
+ * messages, loading them via a {@link org.springframework.util.PropertiesPersister}
+ * strategy: The default strategy is capable of loading properties files
+ * with a specific character encoding, if desired.
+ *
+ *
In contrast to {@link ResourceBundleMessageSource}, this class supports
+ * reloading of properties files through the {@link #setCacheSeconds "cacheSeconds"}
+ * setting, and also through programmatically clearing the properties cache.
+ * Since application servers typically cache all files loaded from the classpath,
+ * it is necessary to store resources somewhere else (for example, in the
+ * "WEB-INF" directory of a web app). Otherwise changes of files in the
+ * classpath will not be reflected in the application.
+ *
+ *
Note that the base names set as {@link #setBasenames "basenames"} property
+ * are treated in a slightly different fashion than the "basenames" property of
+ * {@link ResourceBundleMessageSource}. It follows the basic ResourceBundle rule of not
+ * specifying file extension or language codes, but can refer to any Spring resource
+ * location (instead of being restricted to classpath resources). With a "classpath:"
+ * prefix, resources can still be loaded from the classpath, but "cacheSeconds" values
+ * other than "-1" (caching forever) will not work in this case.
+ *
+ *
This MessageSource implementation is usually slightly faster than
+ * {@link ResourceBundleMessageSource}, which builds on {@link java.util.ResourceBundle}
+ * - in the default mode, i.e. when caching forever. With "cacheSeconds" set to 1,
+ * message lookup takes about twice as long - with the benefit that changes in
+ * individual properties files are detected with a maximum delay of 1 second.
+ * Higher "cacheSeconds" values usually do not make a significant difference.
+ *
+ *
This MessageSource can easily be used outside of an
+ * {@link org.springframework.context.ApplicationContext}: It will use a
+ * {@link org.springframework.core.io.DefaultResourceLoader} as default,
+ * simply getting overridden with the ApplicationContext's resource loader
+ * if running in a context. It does not have any other specific dependencies.
+ *
+ *
Thanks to Thomas Achleitner for providing the initial implementation of
+ * this message source!
+ *
+ * @author Juergen Hoeller
+ * @see #setCacheSeconds
+ * @see #setBasenames
+ * @see #setDefaultEncoding
+ * @see #setFileEncodings
+ * @see #setPropertiesPersister
+ * @see #setResourceLoader
+ * @see org.springframework.util.DefaultPropertiesPersister
+ * @see org.springframework.core.io.DefaultResourceLoader
+ * @see ResourceBundleMessageSource
+ * @see java.util.ResourceBundle
+ */
+public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
+ implements ResourceLoaderAware {
+
+ private static final String PROPERTIES_SUFFIX = ".properties";
+
+ private static final String XML_SUFFIX = ".xml";
+
+
+ private String[] basenames = new String[0];
+
+ private String defaultEncoding;
+
+ private Properties fileEncodings;
+
+ private boolean fallbackToSystemLocale = true;
+
+ private long cacheMillis = -1;
+
+ private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
+
+ private ResourceLoader resourceLoader = new DefaultResourceLoader();
+
+ /** Cache to hold filename lists per Locale */
+ private final Map cachedFilenames = new HashMap();
+
+ /** Cache to hold already loaded properties per filename */
+ private final Map cachedProperties = new HashMap();
+
+ /** Cache to hold merged loaded properties per basename */
+ private final Map cachedMergedProperties = new HashMap();
+
+
+ /**
+ * Set a single basename, following the basic ResourceBundle convention of
+ * not specifying file extension or language codes, but in contrast to
+ * {@link ResourceBundleMessageSource} referring to a Spring resource location:
+ * e.g. "WEB-INF/messages" for "WEB-INF/messages.properties",
+ * "WEB-INF/messages_en.properties", etc.
+ *
As of Spring 1.2.2, XML properties files are also supported:
+ * e.g. "WEB-INF/messages" will find and load "WEB-INF/messages.xml",
+ * "WEB-INF/messages_en.xml", etc as well. Note that this will only
+ * work on JDK 1.5+.
+ * @param basename the single basename
+ * @see #setBasenames
+ * @see org.springframework.core.io.ResourceEditor
+ * @see java.util.ResourceBundle
+ */
+ public void setBasename(String basename) {
+ setBasenames(new String[] {basename});
+ }
+
+ /**
+ * Set an array of basenames, each following the basic ResourceBundle convention
+ * of not specifying file extension or language codes, but in contrast to
+ * {@link ResourceBundleMessageSource} referring to a Spring resource location:
+ * e.g. "WEB-INF/messages" for "WEB-INF/messages.properties",
+ * "WEB-INF/messages_en.properties", etc.
+ *
As of Spring 1.2.2, XML properties files are also supported:
+ * e.g. "WEB-INF/messages" will find and load "WEB-INF/messages.xml",
+ * "WEB-INF/messages_en.xml", etc as well. Note that this will only
+ * work on JDK 1.5+.
+ *
The associated resource bundles will be checked sequentially
+ * when resolving a message code. Note that message definitions in a
+ * previous resource bundle will override ones in a later bundle,
+ * due to the sequential lookup.
+ * @param basenames an array of basenames
+ * @see #setBasename
+ * @see java.util.ResourceBundle
+ */
+ public void setBasenames(String[] basenames) {
+ if (basenames != null) {
+ this.basenames = new String[basenames.length];
+ for (int i = 0; i < basenames.length; i++) {
+ String basename = basenames[i];
+ Assert.hasText(basename, "Basename must not be empty");
+ this.basenames[i] = basename.trim();
+ }
+ }
+ else {
+ this.basenames = new String[0];
+ }
+ }
+
+ /**
+ * Set the default charset to use for parsing properties files.
+ * Used if no file-specific charset is specified for a file.
+ *
Default is none, using the java.util.Properties
+ * default encoding.
+ *
Only applies to classic properties files, not to XML files.
+ * @param defaultEncoding the default charset
+ * @see #setFileEncodings
+ * @see org.springframework.util.PropertiesPersister#load
+ */
+ public void setDefaultEncoding(String defaultEncoding) {
+ this.defaultEncoding = defaultEncoding;
+ }
+
+ /**
+ * Set per-file charsets to use for parsing properties files.
+ *
Only applies to classic properties files, not to XML files.
+ * @param fileEncodings Properties with filenames as keys and charset
+ * names as values. Filenames have to match the basename syntax,
+ * with optional locale-specific appendices: e.g. "WEB-INF/messages"
+ * or "WEB-INF/messages_en".
+ * @see #setBasenames
+ * @see org.springframework.util.PropertiesPersister#load
+ */
+ public void setFileEncodings(Properties fileEncodings) {
+ this.fileEncodings = fileEncodings;
+ }
+
+ /**
+ * Set whether to fall back to the system Locale if no files for a specific
+ * Locale have been found. Default is "true"; if this is turned off, the only
+ * fallback will be the default file (e.g. "messages.properties" for
+ * basename "messages").
+ *
Falling back to the system Locale is the default behavior of
+ * java.util.ResourceBundle. However, this is often not
+ * desirable in an application server environment, where the system Locale
+ * is not relevant to the application at all: Set this flag to "false"
+ * in such a scenario.
+ */
+ public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
+ this.fallbackToSystemLocale = fallbackToSystemLocale;
+ }
+
+ /**
+ * Set the number of seconds to cache loaded properties files.
+ *
+ *
Default is "-1", indicating to cache forever (just like
+ * java.util.ResourceBundle).
+ *
A positive number will cache loaded properties files for the given
+ * number of seconds. This is essentially the interval between refresh checks.
+ * Note that a refresh attempt will first check the last-modified timestamp
+ * of the file before actually reloading it; so if files don't change, this
+ * interval can be set rather low, as refresh attempts will not actually reload.
+ *
A value of "0" will check the last-modified timestamp of the file on
+ * every message access. Do not use this in a production environment!
+ *
+ */
+ public void setCacheSeconds(int cacheSeconds) {
+ this.cacheMillis = (cacheSeconds * 1000);
+ }
+
+ /**
+ * Set the PropertiesPersister to use for parsing properties files.
+ *
The default is a DefaultPropertiesPersister.
+ * @see org.springframework.util.DefaultPropertiesPersister
+ */
+ public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
+ this.propertiesPersister =
+ (propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister());
+ }
+
+ /**
+ * Set the ResourceLoader to use for loading bundle properties files.
+ *
The default is a DefaultResourceLoader. Will get overridden by the
+ * ApplicationContext if running in a context, as it implements the
+ * ResourceLoaderAware interface. Can be manually overridden when
+ * running outside of an ApplicationContext.
+ * @see org.springframework.core.io.DefaultResourceLoader
+ * @see org.springframework.context.ResourceLoaderAware
+ */
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
+ }
+
+
+ /**
+ * Resolves the given message code as key in the retrieved bundle files,
+ * returning the value found in the bundle as-is (without MessageFormat parsing).
+ */
+ protected String resolveCodeWithoutArguments(String code, Locale locale) {
+ if (this.cacheMillis < 0) {
+ PropertiesHolder propHolder = getMergedProperties(locale);
+ String result = propHolder.getProperty(code);
+ if (result != null) {
+ return result;
+ }
+ }
+ else {
+ for (int i = 0; i < this.basenames.length; i++) {
+ List filenames = calculateAllFilenames(this.basenames[i], locale);
+ for (int j = 0; j < filenames.size(); j++) {
+ String filename = (String) filenames.get(j);
+ PropertiesHolder propHolder = getProperties(filename);
+ String result = propHolder.getProperty(code);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Resolves the given message code as key in the retrieved bundle files,
+ * using a cached MessageFormat instance per message code.
+ */
+ protected MessageFormat resolveCode(String code, Locale locale) {
+ if (this.cacheMillis < 0) {
+ PropertiesHolder propHolder = getMergedProperties(locale);
+ MessageFormat result = propHolder.getMessageFormat(code, locale);
+ if (result != null) {
+ return result;
+ }
+ }
+ else {
+ for (int i = 0; i < this.basenames.length; i++) {
+ List filenames = calculateAllFilenames(this.basenames[i], locale);
+ for (int j = 0; j < filenames.size(); j++) {
+ String filename = (String) filenames.get(j);
+ PropertiesHolder propHolder = getProperties(filename);
+ MessageFormat result = propHolder.getMessageFormat(code, locale);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Get a PropertiesHolder that contains the actually visible properties
+ * for a Locale, after merging all specified resource bundles.
+ * Either fetches the holder from the cache or freshly loads it.
+ *
Only used when caching resource bundle contents forever, i.e.
+ * with cacheSeconds < 0. Therefore, merged properties are always
+ * cached forever.
+ */
+ protected PropertiesHolder getMergedProperties(Locale locale) {
+ synchronized (this.cachedMergedProperties) {
+ PropertiesHolder mergedHolder = (PropertiesHolder) this.cachedMergedProperties.get(locale);
+ if (mergedHolder != null) {
+ return mergedHolder;
+ }
+ Properties mergedProps = new Properties();
+ mergedHolder = new PropertiesHolder(mergedProps, -1);
+ for (int i = this.basenames.length - 1; i >= 0; i--) {
+ List filenames = calculateAllFilenames(this.basenames[i], locale);
+ for (int j = filenames.size() - 1; j >= 0; j--) {
+ String filename = (String) filenames.get(j);
+ PropertiesHolder propHolder = getProperties(filename);
+ if (propHolder.getProperties() != null) {
+ mergedProps.putAll(propHolder.getProperties());
+ }
+ }
+ }
+ this.cachedMergedProperties.put(locale, mergedHolder);
+ return mergedHolder;
+ }
+ }
+
+ /**
+ * Calculate all filenames for the given bundle basename and Locale.
+ * Will calculate filenames for the given Locale, the system Locale
+ * (if applicable), and the default file.
+ * @param basename the basename of the bundle
+ * @param locale the locale
+ * @return the List of filenames to check
+ * @see #setFallbackToSystemLocale
+ * @see #calculateFilenamesForLocale
+ */
+ protected List calculateAllFilenames(String basename, Locale locale) {
+ synchronized (this.cachedFilenames) {
+ Map localeMap = (Map) this.cachedFilenames.get(basename);
+ if (localeMap != null) {
+ List filenames = (List) localeMap.get(locale);
+ if (filenames != null) {
+ return filenames;
+ }
+ }
+ List filenames = new ArrayList(7);
+ filenames.addAll(calculateFilenamesForLocale(basename, locale));
+ if (this.fallbackToSystemLocale && !locale.equals(Locale.getDefault())) {
+ List fallbackFilenames = calculateFilenamesForLocale(basename, Locale.getDefault());
+ for (Iterator it = fallbackFilenames.iterator(); it.hasNext();) {
+ String fallbackFilename = (String) it.next();
+ if (!filenames.contains(fallbackFilename)) {
+ // Entry for fallback locale that isn't already in filenames list.
+ filenames.add(fallbackFilename);
+ }
+ }
+ }
+ filenames.add(basename);
+ if (localeMap != null) {
+ localeMap.put(locale, filenames);
+ }
+ else {
+ localeMap = new HashMap();
+ localeMap.put(locale, filenames);
+ this.cachedFilenames.put(basename, localeMap);
+ }
+ return filenames;
+ }
+ }
+
+ /**
+ * Calculate the filenames for the given bundle basename and Locale,
+ * appending language code, country code, and variant code.
+ * E.g.: basename "messages", Locale "de_AT_oo" -> "messages_de_AT_OO",
+ * "messages_de_AT", "messages_de".
+ * @param basename the basename of the bundle
+ * @param locale the locale
+ * @return the List of filenames to check
+ */
+ protected List calculateFilenamesForLocale(String basename, Locale locale) {
+ List result = new ArrayList(3);
+ String language = locale.getLanguage();
+ String country = locale.getCountry();
+ String variant = locale.getVariant();
+ StringBuffer temp = new StringBuffer(basename);
+
+ if (language.length() > 0) {
+ temp.append('_').append(language);
+ result.add(0, temp.toString());
+ }
+
+ if (country.length() > 0) {
+ temp.append('_').append(country);
+ result.add(0, temp.toString());
+ }
+
+ if (variant.length() > 0) {
+ temp.append('_').append(variant);
+ result.add(0, temp.toString());
+ }
+
+ return result;
+ }
+
+
+ /**
+ * Get a PropertiesHolder for the given filename, either from the
+ * cache or freshly loaded.
+ * @param filename the bundle filename (basename + Locale)
+ * @return the current PropertiesHolder for the bundle
+ */
+ protected PropertiesHolder getProperties(String filename) {
+ synchronized (this.cachedProperties) {
+ PropertiesHolder propHolder = (PropertiesHolder) this.cachedProperties.get(filename);
+ if (propHolder != null &&
+ (propHolder.getRefreshTimestamp() < 0 ||
+ propHolder.getRefreshTimestamp() > System.currentTimeMillis() - this.cacheMillis)) {
+ // up to date
+ return propHolder;
+ }
+ return refreshProperties(filename, propHolder);
+ }
+ }
+
+ /**
+ * Refresh the PropertiesHolder for the given bundle filename.
+ * The holder can be null if not cached before, or a timed-out cache entry
+ * (potentially getting re-validated against the current last-modified timestamp).
+ * @param filename the bundle filename (basename + Locale)
+ * @param propHolder the current PropertiesHolder for the bundle
+ */
+ protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
+ long refreshTimestamp = (this.cacheMillis < 0) ? -1 : System.currentTimeMillis();
+
+ Resource resource = this.resourceLoader.getResource(filename + PROPERTIES_SUFFIX);
+ if (!resource.exists()) {
+ resource = this.resourceLoader.getResource(filename + XML_SUFFIX);
+ }
+
+ if (resource.exists()) {
+ long fileTimestamp = -1;
+ if (this.cacheMillis >= 0) {
+ // Last-modified timestamp of file will just be read if caching with timeout.
+ try {
+ fileTimestamp = resource.lastModified();
+ if (propHolder != null && propHolder.getFileTimestamp() == fileTimestamp) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Re-caching properties for filename [" + filename + "] - file hasn't been modified");
+ }
+ propHolder.setRefreshTimestamp(refreshTimestamp);
+ return propHolder;
+ }
+ }
+ catch (IOException ex) {
+ // Probably a class path resource: cache it forever.
+ if (logger.isDebugEnabled()) {
+ logger.debug(
+ resource + " could not be resolved in the file system - assuming that is hasn't changed", ex);
+ }
+ fileTimestamp = -1;
+ }
+ }
+ try {
+ Properties props = loadProperties(resource, filename);
+ propHolder = new PropertiesHolder(props, fileTimestamp);
+ }
+ catch (IOException ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("Could not parse properties file [" + resource.getFilename() + "]", ex);
+ }
+ // Empty holder representing "not valid".
+ propHolder = new PropertiesHolder();
+ }
+ }
+
+ else {
+ // Resource does not exist.
+ if (logger.isDebugEnabled()) {
+ logger.debug("No properties file found for [" + filename + "] - neither plain properties nor XML");
+ }
+ // Empty holder representing "not found".
+ propHolder = new PropertiesHolder();
+ }
+
+ propHolder.setRefreshTimestamp(refreshTimestamp);
+ this.cachedProperties.put(filename, propHolder);
+ return propHolder;
+ }
+
+ /**
+ * Load the properties from the given resource.
+ * @param resource the resource to load from
+ * @param filename the original bundle filename (basename + Locale)
+ * @return the populated Properties instance
+ * @throws IOException if properties loading failed
+ */
+ protected Properties loadProperties(Resource resource, String filename) throws IOException {
+ InputStream is = resource.getInputStream();
+ Properties props = new Properties();
+ try {
+ if (resource.getFilename().endsWith(XML_SUFFIX)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loading properties [" + resource.getFilename() + "]");
+ }
+ this.propertiesPersister.loadFromXml(props, is);
+ }
+ else {
+ String encoding = null;
+ if (this.fileEncodings != null) {
+ encoding = this.fileEncodings.getProperty(filename);
+ }
+ if (encoding == null) {
+ encoding = this.defaultEncoding;
+ }
+ if (encoding != null) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loading properties [" + resource.getFilename() + "] with encoding '" + encoding + "'");
+ }
+ this.propertiesPersister.load(props, new InputStreamReader(is, encoding));
+ }
+ else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loading properties [" + resource.getFilename() + "]");
+ }
+ this.propertiesPersister.load(props, is);
+ }
+ }
+ return props;
+ }
+ finally {
+ is.close();
+ }
+ }
+
+
+ /**
+ * Clear the resource bundle cache.
+ * Subsequent resolve calls will lead to reloading of the properties files.
+ */
+ public void clearCache() {
+ logger.debug("Clearing entire resource bundle cache");
+ synchronized (this.cachedProperties) {
+ this.cachedProperties.clear();
+ }
+ synchronized (this.cachedMergedProperties) {
+ this.cachedMergedProperties.clear();
+ }
+ }
+
+ /**
+ * Clear the resource bundle caches of this MessageSource and all its ancestors.
+ * @see #clearCache
+ */
+ public void clearCacheIncludingAncestors() {
+ clearCache();
+ if (getParentMessageSource() instanceof ReloadableResourceBundleMessageSource) {
+ ((ReloadableResourceBundleMessageSource) getParentMessageSource()).clearCacheIncludingAncestors();
+ }
+ }
+
+
+ public String toString() {
+ return getClass().getName() + ": basenames=[" + StringUtils.arrayToCommaDelimitedString(this.basenames) + "]";
+ }
+
+
+ /**
+ * PropertiesHolder for caching.
+ * Stores the last-modified timestamp of the source file for efficient
+ * change detection, and the timestamp of the last refresh attempt
+ * (updated every time the cache entry gets re-validated).
+ */
+ protected class PropertiesHolder {
+
+ private Properties properties;
+
+ private long fileTimestamp = -1;
+
+ private long refreshTimestamp = -1;
+
+ /** Cache to hold already generated MessageFormats per message code */
+ private final Map cachedMessageFormats = new HashMap();
+
+ public PropertiesHolder(Properties properties, long fileTimestamp) {
+ this.properties = properties;
+ this.fileTimestamp = fileTimestamp;
+ }
+
+ public PropertiesHolder() {
+ }
+
+ public Properties getProperties() {
+ return properties;
+ }
+
+ public long getFileTimestamp() {
+ return fileTimestamp;
+ }
+
+ public void setRefreshTimestamp(long refreshTimestamp) {
+ this.refreshTimestamp = refreshTimestamp;
+ }
+
+ public long getRefreshTimestamp() {
+ return refreshTimestamp;
+ }
+
+ public String getProperty(String code) {
+ if (this.properties == null) {
+ return null;
+ }
+ return this.properties.getProperty(code);
+ }
+
+ public MessageFormat getMessageFormat(String code, Locale locale) {
+ if (this.properties == null) {
+ return null;
+ }
+ synchronized (this.cachedMessageFormats) {
+ Map localeMap = (Map) this.cachedMessageFormats.get(code);
+ if (localeMap != null) {
+ MessageFormat result = (MessageFormat) localeMap.get(locale);
+ if (result != null) {
+ return result;
+ }
+ }
+ String msg = this.properties.getProperty(code);
+ if (msg != null) {
+ if (localeMap == null) {
+ localeMap = new HashMap();
+ this.cachedMessageFormats.put(code, localeMap);
+ }
+ MessageFormat result = createMessageFormat(msg, locale);
+ localeMap.put(locale, result);
+ return result;
+ }
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ResourceBundleMessageSource.java b/org.springframework.context/src/main/java/org/springframework/context/support/ResourceBundleMessageSource.java
new file mode 100644
index 00000000000..90df82e8f78
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/ResourceBundleMessageSource.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link org.springframework.context.MessageSource} implementation that
+ * accesses resource bundles using specified basenames. This class relies
+ * on the underlying JDK's {@link java.util.ResourceBundle} implementation,
+ * in combination with the JDK's standard message parsing provided by
+ * {@link java.text.MessageFormat}.
+ *
+ *
This MessageSource caches both the accessed ResourceBundle instances and
+ * the generated MessageFormats for each message. It also implements rendering of
+ * no-arg messages without MessageFormat, as supported by the AbstractMessageSource
+ * base class. The caching provided by this MessageSource is significantly faster
+ * than the built-in caching of the java.util.ResourceBundle class.
+ *
+ *
Unfortunately, java.util.ResourceBundle caches loaded bundles
+ * forever: Reloading a bundle during VM execution is not possible.
+ * As this MessageSource relies on ResourceBundle, it faces the same limitation.
+ * Consider {@link ReloadableResourceBundleMessageSource} for an alternative
+ * that is capable of refreshing the underlying bundle files.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #setBasenames
+ * @see ReloadableResourceBundleMessageSource
+ * @see java.util.ResourceBundle
+ * @see java.text.MessageFormat
+ */
+public class ResourceBundleMessageSource extends AbstractMessageSource implements BeanClassLoaderAware {
+
+ private String[] basenames = new String[0];
+
+ private ClassLoader bundleClassLoader;
+
+ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
+
+ /**
+ * Cache to hold loaded ResourceBundles.
+ * This Map is keyed with the bundle basename, which holds a Map that is
+ * keyed with the Locale and in turn holds the ResourceBundle instances.
+ * This allows for very efficient hash lookups, significantly faster
+ * than the ResourceBundle class's own cache.
+ */
+ private final Map cachedResourceBundles = new HashMap();
+
+ /**
+ * Cache to hold already generated MessageFormats.
+ * This Map is keyed with the ResourceBundle, which holds a Map that is
+ * keyed with the message code, which in turn holds a Map that is keyed
+ * with the Locale and holds the MessageFormat values. This allows for
+ * very efficient hash lookups without concatenated keys.
+ * @see #getMessageFormat
+ */
+ private final Map cachedBundleMessageFormats = new HashMap();
+
+
+ /**
+ * Set a single basename, following {@link java.util.ResourceBundle} conventions:
+ * essentially, a fully-qualified classpath location. If it doesn't contain a
+ * package qualifier (such as org.mypackage), it will be resolved
+ * from the classpath root.
+ *
Messages will normally be held in the "/lib" or "/classes" directory of
+ * a web application's WAR structure. They can also be held in jar files on
+ * the class path.
+ *
Note that ResourceBundle names are effectively classpath locations: As a
+ * consequence, the JDK's standard ResourceBundle treats dots as package separators.
+ * This means that "test.theme" is effectively equivalent to "test/theme",
+ * just like it is for programmatic java.util.ResourceBundle usage.
+ * @see #setBasenames
+ * @see java.util.ResourceBundle#getBundle(String)
+ */
+ public void setBasename(String basename) {
+ setBasenames(new String[] {basename});
+ }
+
+ /**
+ * Set an array of basenames, each following {@link java.util.ResourceBundle}
+ * conventions: essentially, a fully-qualified classpath location. If it
+ * doesn't contain a package qualifier (such as org.mypackage),
+ * it will be resolved from the classpath root.
+ *
The associated resource bundles will be checked sequentially
+ * when resolving a message code. Note that message definitions in a
+ * previous resource bundle will override ones in a later bundle,
+ * due to the sequential lookup.
+ *
Note that ResourceBundle names are effectively classpath locations: As a
+ * consequence, the JDK's standard ResourceBundle treats dots as package separators.
+ * This means that "test.theme" is effectively equivalent to "test/theme",
+ * just like it is for programmatic java.util.ResourceBundle usage.
+ * @see #setBasename
+ * @see java.util.ResourceBundle#getBundle(String)
+ */
+ public void setBasenames(String[] basenames) {
+ if (basenames != null) {
+ this.basenames = new String[basenames.length];
+ for (int i = 0; i < basenames.length; i++) {
+ String basename = basenames[i];
+ Assert.hasText(basename, "Basename must not be empty");
+ this.basenames[i] = basename.trim();
+ }
+ }
+ else {
+ this.basenames = new String[0];
+ }
+ }
+
+ /**
+ * Set the ClassLoader to load resource bundles with.
+ *
Default is the containing BeanFactory's
+ * {@link org.springframework.beans.factory.BeanClassLoaderAware bean ClassLoader},
+ * or the default ClassLoader determined by
+ * {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}
+ * if not running within a BeanFactory.
+ */
+ public void setBundleClassLoader(ClassLoader classLoader) {
+ this.bundleClassLoader = classLoader;
+ }
+
+ /**
+ * Return the ClassLoader to load resource bundles with.
+ *
Default is the containing BeanFactory's bean ClassLoader.
+ * @see #setBundleClassLoader
+ */
+ protected ClassLoader getBundleClassLoader() {
+ return (this.bundleClassLoader != null ? this.bundleClassLoader : this.beanClassLoader);
+ }
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.beanClassLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
+ }
+
+
+ /**
+ * Resolves the given message code as key in the registered resource bundles,
+ * returning the value found in the bundle as-is (without MessageFormat parsing).
+ */
+ protected String resolveCodeWithoutArguments(String code, Locale locale) {
+ String result = null;
+ for (int i = 0; result == null && i < this.basenames.length; i++) {
+ ResourceBundle bundle = getResourceBundle(this.basenames[i], locale);
+ if (bundle != null) {
+ result = getStringOrNull(bundle, code);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Resolves the given message code as key in the registered resource bundles,
+ * using a cached MessageFormat instance per message code.
+ */
+ protected MessageFormat resolveCode(String code, Locale locale) {
+ MessageFormat messageFormat = null;
+ for (int i = 0; messageFormat == null && i < this.basenames.length; i++) {
+ ResourceBundle bundle = getResourceBundle(this.basenames[i], locale);
+ if (bundle != null) {
+ messageFormat = getMessageFormat(bundle, code, locale);
+ }
+ }
+ return messageFormat;
+ }
+
+
+ /**
+ * Return a ResourceBundle for the given basename and code,
+ * fetching already generated MessageFormats from the cache.
+ * @param basename the basename of the ResourceBundle
+ * @param locale the Locale to find the ResourceBundle for
+ * @return the resulting ResourceBundle, or null if none
+ * found for the given basename and Locale
+ */
+ protected ResourceBundle getResourceBundle(String basename, Locale locale) {
+ synchronized (this.cachedResourceBundles) {
+ Map localeMap = (Map) this.cachedResourceBundles.get(basename);
+ if (localeMap != null) {
+ ResourceBundle bundle = (ResourceBundle) localeMap.get(locale);
+ if (bundle != null) {
+ return bundle;
+ }
+ }
+ try {
+ ResourceBundle bundle = doGetBundle(basename, locale);
+ if (localeMap == null) {
+ localeMap = new HashMap();
+ this.cachedResourceBundles.put(basename, localeMap);
+ }
+ localeMap.put(locale, bundle);
+ return bundle;
+ }
+ catch (MissingResourceException ex) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
+ }
+ // Assume bundle not found
+ // -> do NOT throw the exception to allow for checking parent message source.
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Obtain the resource bundle for the given basename and Locale.
+ * @param basename the basename to look for
+ * @param locale the Locale to look for
+ * @return the corresponding ResourceBundle
+ * @throws MissingResourceException if no matching bundle could be found
+ * @see java.util.ResourceBundle#getBundle(String, java.util.Locale, ClassLoader)
+ * @see #getBundleClassLoader()
+ */
+ protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
+ return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
+ }
+
+ /**
+ * Return a MessageFormat for the given bundle and code,
+ * fetching already generated MessageFormats from the cache.
+ * @param bundle the ResourceBundle to work on
+ * @param code the message code to retrieve
+ * @param locale the Locale to use to build the MessageFormat
+ * @return the resulting MessageFormat, or null if no message
+ * defined for the given code
+ * @throws MissingResourceException if thrown by the ResourceBundle
+ */
+ protected MessageFormat getMessageFormat(ResourceBundle bundle, String code, Locale locale)
+ throws MissingResourceException {
+
+ synchronized (this.cachedBundleMessageFormats) {
+ Map codeMap = (Map) this.cachedBundleMessageFormats.get(bundle);
+ Map localeMap = null;
+ if (codeMap != null) {
+ localeMap = (Map) codeMap.get(code);
+ if (localeMap != null) {
+ MessageFormat result = (MessageFormat) localeMap.get(locale);
+ if (result != null) {
+ return result;
+ }
+ }
+ }
+
+ String msg = getStringOrNull(bundle, code);
+ if (msg != null) {
+ if (codeMap == null) {
+ codeMap = new HashMap();
+ this.cachedBundleMessageFormats.put(bundle, codeMap);
+ }
+ if (localeMap == null) {
+ localeMap = new HashMap();
+ codeMap.put(code, localeMap);
+ }
+ MessageFormat result = createMessageFormat(msg, locale);
+ localeMap.put(locale, result);
+ return result;
+ }
+
+ return null;
+ }
+ }
+
+ private String getStringOrNull(ResourceBundle bundle, String key) {
+ try {
+ return bundle.getString(key);
+ }
+ catch (MissingResourceException ex) {
+ // Assume key not found
+ // -> do NOT throw the exception to allow for checking parent message source.
+ return null;
+ }
+ }
+
+
+ /**
+ * Show the configuration of this MessageSource.
+ */
+ public String toString() {
+ return getClass().getName() + ": basenames=[" +
+ StringUtils.arrayToCommaDelimitedString(this.basenames) + "]";
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/ResourceMapFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/context/support/ResourceMapFactoryBean.java
new file mode 100644
index 00000000000..674d8daf6a4
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/ResourceMapFactoryBean.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2002-2005 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.springframework.beans.factory.config.PropertiesFactoryBean;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+
+/**
+ * FactoryBean that creates a Map with String keys and Resource values from
+ * properties, interpreting passed-in String values as resource locations.
+ *
+ *
Extends PropertiesFactoryBean to inherit the capability of defining
+ * local properties and loading from properties files.
+ *
+ *
Implements the ResourceLoaderAware interface to automatically use
+ * the context ResourceLoader if running in an ApplicationContext.
+ * Uses DefaultResourceLoader else.
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @since 1.0.2
+ * @see org.springframework.core.io.DefaultResourceLoader
+ */
+public class ResourceMapFactoryBean extends PropertiesFactoryBean implements ResourceLoaderAware {
+
+ private String resourceBasePath = "";
+
+ private ResourceLoader resourceLoader = new DefaultResourceLoader();
+
+
+ /**
+ * Set a base path to prepend to each resource location value
+ * in the properties file.
+ *
E.g.: resourceBasePath="/images", value="/test.gif"
+ * -> location="/images/test.gif"
+ */
+ public void setResourceBasePath(String resourceBasePath) {
+ this.resourceBasePath = (resourceBasePath != null ? resourceBasePath : "");
+ }
+
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
+ }
+
+
+ public Class getObjectType() {
+ return Map.class;
+ }
+
+ /**
+ * Create the Map instance, populated with keys and Resource values.
+ */
+ protected Object createInstance() throws IOException {
+ Map resourceMap = new HashMap();
+ Properties props = mergeProperties();
+ for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {
+ String key = (String) en.nextElement();
+ String location = props.getProperty(key);
+ resourceMap.put(key, getResource(location));
+ }
+ return resourceMap;
+ }
+
+ /**
+ * Fetch the Resource handle for the given location,
+ * prepeding the resource base path.
+ * @param location the resource location
+ * @return the Resource handle
+ * @see org.springframework.core.io.ResourceLoader#getResource(String)
+ */
+ protected Resource getResource(String location) {
+ return this.resourceLoader.getResource(this.resourceBasePath + location);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/StaticApplicationContext.java b/org.springframework.context/src/main/java/org/springframework/context/support/StaticApplicationContext.java
new file mode 100644
index 00000000000..c03f3446dc7
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/StaticApplicationContext.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.util.Locale;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * {@link org.springframework.context.ApplicationContext} implementation
+ * which supports programmatic registration of beans and messages,
+ * rather than reading bean definitions from external configuration sources.
+ * Mainly useful for testing.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see #registerSingleton
+ * @see #registerPrototype
+ * @see #registerBeanDefinition
+ * @see #refresh
+ */
+public class StaticApplicationContext extends GenericApplicationContext {
+
+ private final StaticMessageSource staticMessageSource;
+
+
+ /**
+ * Create a new StaticApplicationContext.
+ * @see #registerSingleton
+ * @see #registerPrototype
+ * @see #registerBeanDefinition
+ * @see #refresh
+ */
+ public StaticApplicationContext() throws BeansException {
+ this(null);
+ }
+
+ /**
+ * Create a new StaticApplicationContext with the given parent.
+ * @see #registerSingleton
+ * @see #registerPrototype
+ * @see #registerBeanDefinition
+ * @see #refresh
+ */
+ public StaticApplicationContext(ApplicationContext parent) throws BeansException {
+ super(parent);
+
+ // Initialize and register a StaticMessageSource.
+ this.staticMessageSource = new StaticMessageSource();
+ getBeanFactory().registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.staticMessageSource);
+ }
+
+
+ /**
+ * Return the internal StaticMessageSource used by this context.
+ * Can be used to register messages on it.
+ * @see #addMessage
+ */
+ public final StaticMessageSource getStaticMessageSource() {
+ return this.staticMessageSource;
+ }
+
+
+ /**
+ * Register a singleton bean with the underlying bean factory.
+ *
For more advanced needs, register with the underlying BeanFactory directly.
+ * @see #getDefaultListableBeanFactory
+ */
+ public void registerSingleton(String name, Class clazz) throws BeansException {
+ GenericBeanDefinition bd = new GenericBeanDefinition();
+ bd.setBeanClass(clazz);
+ getDefaultListableBeanFactory().registerBeanDefinition(name, bd);
+ }
+
+ /**
+ * Register a singleton bean with the underlying bean factory.
+ *
For more advanced needs, register with the underlying BeanFactory directly.
+ * @see #getDefaultListableBeanFactory
+ */
+ public void registerSingleton(String name, Class clazz, MutablePropertyValues pvs) throws BeansException {
+ GenericBeanDefinition bd = new GenericBeanDefinition();
+ bd.setBeanClass(clazz);
+ bd.setPropertyValues(pvs);
+ getDefaultListableBeanFactory().registerBeanDefinition(name, bd);
+ }
+
+ /**
+ * Register a prototype bean with the underlying bean factory.
+ *
For more advanced needs, register with the underlying BeanFactory directly.
+ * @see #getDefaultListableBeanFactory
+ */
+ public void registerPrototype(String name, Class clazz) throws BeansException {
+ GenericBeanDefinition bd = new GenericBeanDefinition();
+ bd.setScope(GenericBeanDefinition.SCOPE_PROTOTYPE);
+ bd.setBeanClass(clazz);
+ getDefaultListableBeanFactory().registerBeanDefinition(name, bd);
+ }
+
+ /**
+ * Register a prototype bean with the underlying bean factory.
+ *
For more advanced needs, register with the underlying BeanFactory directly.
+ * @see #getDefaultListableBeanFactory
+ */
+ public void registerPrototype(String name, Class clazz, MutablePropertyValues pvs) throws BeansException {
+ GenericBeanDefinition bd = new GenericBeanDefinition();
+ bd.setScope(GenericBeanDefinition.SCOPE_PROTOTYPE);
+ bd.setBeanClass(clazz);
+ bd.setPropertyValues(pvs);
+ getDefaultListableBeanFactory().registerBeanDefinition(name, bd);
+ }
+
+ /**
+ * Associate the given message with the given code.
+ * @param code lookup code
+ * @param locale locale message should be found within
+ * @param defaultMessage message associated with this lookup code
+ * @see #getStaticMessageSource
+ */
+ public void addMessage(String code, Locale locale, String defaultMessage) {
+ getStaticMessageSource().addMessage(code, locale, defaultMessage);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/StaticMessageSource.java b/org.springframework.context/src/main/java/org/springframework/context/support/StaticMessageSource.java
new file mode 100644
index 00000000000..7f14893e249
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/StaticMessageSource.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.support;
+
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import org.springframework.util.Assert;
+
+/**
+ * Simple implementation of {@link org.springframework.context.MessageSource}
+ * which allows messages to be registered programmatically.
+ * This MessageSource supports basic internationalization.
+ *
+ *
Intended for testing rather than for use in production systems.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ */
+public class StaticMessageSource extends AbstractMessageSource {
+
+ /** Map from 'code + locale' keys to message Strings */
+ private final Map messages = new HashMap();
+
+
+ protected MessageFormat resolveCode(String code, Locale locale) {
+ return (MessageFormat) this.messages.get(code + "_" + locale.toString());
+ }
+
+ /**
+ * Associate the given message with the given code.
+ * @param code the lookup code
+ * @param locale the locale that the message should be found within
+ * @param msg the message associated with this lookup code
+ */
+ public void addMessage(String code, Locale locale, String msg) {
+ Assert.notNull(code, "Code must not be null");
+ Assert.notNull(locale, "Locale must not be null");
+ Assert.notNull(msg, "Message must not be null");
+ this.messages.put(code + "_" + locale.toString(), createMessageFormat(msg, locale));
+ if (logger.isDebugEnabled()) {
+ logger.debug("Added message [" + msg + "] for code [" + code + "] and Locale [" + locale + "]");
+ }
+ }
+
+ /**
+ * Associate the given message values with the given keys as codes.
+ * @param messages the messages to register, with messages codes
+ * as keys and message texts as values
+ * @param locale the locale that the messages should be found within
+ */
+ public void addMessages(Map messages, Locale locale) {
+ Assert.notNull(messages, "Messages Map must not be null");
+ for (Iterator it = messages.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ addMessage(entry.getKey().toString(), locale, entry.getValue().toString());
+ }
+ }
+
+
+ public String toString() {
+ return getClass().getName() + ": " + this.messages;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/support/package.html b/org.springframework.context/src/main/java/org/springframework/context/support/package.html
new file mode 100644
index 00000000000..cf7219e3d26
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/support/package.html
@@ -0,0 +1,9 @@
+
+
+
+Classes supporting the org.springframework.context package,
+such as abstract base classes for ApplicationContext
+implementations and a MessageSource implementation.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/context/weaving/AspectJWeavingEnabler.java b/org.springframework.context/src/main/java/org/springframework/context/weaving/AspectJWeavingEnabler.java
new file mode 100644
index 00000000000..b302988bf5e
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/weaving/AspectJWeavingEnabler.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.weaving;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.lang.instrument.IllegalClassFormatException;
+import java.security.ProtectionDomain;
+
+import org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.core.Ordered;
+import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
+import org.springframework.instrument.classloading.LoadTimeWeaver;
+
+/**
+ * Post-processor that registers AspectJ's
+ * {@link org.aspectj.weaver.loadtime.ClassPreProcessorAgentAdapter}
+ * with the Spring application context's default
+ * {@link org.springframework.instrument.classloading.LoadTimeWeaver}.
+ *
+ * @author Juergen Hoeller
+ * @author Ramnivas Laddad
+ * @since 2.5
+ */
+public class AspectJWeavingEnabler
+ implements BeanFactoryPostProcessor, BeanClassLoaderAware, LoadTimeWeaverAware, Ordered {
+
+ private ClassLoader beanClassLoader;
+
+ private LoadTimeWeaver loadTimeWeaver;
+
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.beanClassLoader = classLoader;
+ }
+
+ public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
+ this.loadTimeWeaver = loadTimeWeaver;
+ }
+
+ public int getOrder() {
+ return HIGHEST_PRECEDENCE;
+ }
+
+
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ LoadTimeWeaver weaverToUse = this.loadTimeWeaver;
+ if (weaverToUse == null && InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
+ weaverToUse = new InstrumentationLoadTimeWeaver(this.beanClassLoader);
+ }
+ weaverToUse.addTransformer(new AspectJClassBypassingClassFileTransformer(
+ new ClassPreProcessorAgentAdapter()));
+ }
+
+
+ /*
+ * Decorator to suppress processing AspectJ classes, hence avoiding potential LinkageErrors.
+ * OC4J and Tomcat (in Glassfish) definitely need such bypassing of AspectJ classes.
+ */
+ private static class AspectJClassBypassingClassFileTransformer implements ClassFileTransformer {
+
+ private final ClassFileTransformer delegate;
+
+ public AspectJClassBypassingClassFileTransformer(ClassFileTransformer delegate) {
+ this.delegate = delegate;
+ }
+
+ public byte[] transform(ClassLoader loader, String className, Class> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
+
+ if (className.startsWith("org.aspectj") || className.startsWith("org/aspectj")) {
+ return classfileBuffer;
+ }
+ return this.delegate.transform(loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java b/org.springframework.context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java
new file mode 100644
index 00000000000..e67116c484b
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.context.weaving;
+
+import java.lang.instrument.ClassFileTransformer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.instrument.InstrumentationSavingAgent;
+import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
+import org.springframework.instrument.classloading.LoadTimeWeaver;
+import org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver;
+import org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWeaver;
+import org.springframework.instrument.classloading.oc4j.OC4JLoadTimeWeaver;
+import org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeaver;
+
+/**
+ * Default {@link LoadTimeWeaver} bean for use in an application context,
+ * decorating an automatically detected internal LoadTimeWeaver.
+ *
+ *
Typically registered for the default bean name
+ * "loadTimeWeaver"; the most convenient way to achieve this is
+ * Spring's <context:load-time-weaver> XML tag.
+ *
+ *
This class implements a runtime environment check for obtaining the
+ * appropriate weaver implementation: As of Spring 2.5, it detects Sun's
+ * GlassFish, Oracle's OC4J, BEA's WebLogic 10,
+ * {@link InstrumentationSavingAgent Spring's VM agent} and any
+ * {@link ClassLoader} supported by Spring's {@link ReflectiveLoadTimeWeaver}
+ * (for example the
+ * {@link org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader}).
+ *
+ * @author Juergen Hoeller
+ * @author Ramnivas Laddad
+ * @since 2.5
+ * @see org.springframework.context.ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
+ */
+public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLoaderAware, DisposableBean {
+
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private LoadTimeWeaver loadTimeWeaver;
+
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);
+ if (serverSpecificLoadTimeWeaver != null) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Determined server-specific load-time weaver: " +
+ serverSpecificLoadTimeWeaver.getClass().getName());
+ }
+ this.loadTimeWeaver = serverSpecificLoadTimeWeaver;
+ }
+ else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
+ logger.info("Found Spring's JVM agent for instrumentation");
+ this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);
+ }
+ else {
+ try {
+ this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);
+ logger.info("Using a reflective load-time weaver for class loader: " +
+ this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());
+ }
+ catch (IllegalStateException ex) {
+ throw new IllegalStateException(ex.getMessage() + " Specify a custom LoadTimeWeaver " +
+ "or start your Java virtual machine with Spring's agent: -javaagent:spring-agent.jar");
+ }
+ }
+ }
+
+ /*
+ * This method never fails, allowing to try other possible ways to use an
+ * server-agnostic weaver. This non-failure logic is required since
+ * determining a load-time weaver based on the ClassLoader name alone may
+ * legitimately fail due to other mismatches. Specific case in point: the
+ * use of WebLogicLoadTimeWeaver works for WLS 10 but fails due to the lack
+ * of a specific method (addInstanceClassPreProcessor) for any earlier
+ * versions even though the ClassLoader name is the same.
+ */
+ protected LoadTimeWeaver createServerSpecificLoadTimeWeaver(ClassLoader classLoader) {
+ try {
+ if (classLoader.getClass().getName().startsWith("weblogic")) {
+ return new WebLogicLoadTimeWeaver(classLoader);
+ }
+ else if (classLoader.getClass().getName().startsWith("oracle")) {
+ return new OC4JLoadTimeWeaver(classLoader);
+ }
+ else if (classLoader.getClass().getName().startsWith("com.sun.enterprise")) {
+ return new GlassFishLoadTimeWeaver(classLoader);
+ }
+ }
+ catch (IllegalStateException ex) {
+ logger.info("Could not obtain server-specific LoadTimeWeaver: " + ex.getMessage());
+ }
+ return null;
+ }
+
+ public void destroy() {
+ if (this.loadTimeWeaver instanceof InstrumentationLoadTimeWeaver) {
+ logger.info("Removing all registered transformers for class loader: " +
+ this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());
+ ((InstrumentationLoadTimeWeaver) this.loadTimeWeaver).removeTransformers();
+ }
+ }
+
+
+ public void addTransformer(ClassFileTransformer transformer) {
+ this.loadTimeWeaver.addTransformer(transformer);
+ }
+
+ public ClassLoader getInstrumentableClassLoader() {
+ return this.loadTimeWeaver.getInstrumentableClassLoader();
+ }
+
+ public ClassLoader getThrowawayClassLoader() {
+ return this.loadTimeWeaver.getThrowawayClassLoader();
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/weaving/LoadTimeWeaverAware.java b/org.springframework.context/src/main/java/org/springframework/context/weaving/LoadTimeWeaverAware.java
new file mode 100644
index 00000000000..734251f0ded
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/weaving/LoadTimeWeaverAware.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.weaving;
+
+import org.springframework.instrument.classloading.LoadTimeWeaver;
+
+/**
+ * Interface to be implemented by any object that wishes to be notified
+ * of the application context's default {@link LoadTimeWeaver}.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see org.springframework.context.ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
+ */
+public interface LoadTimeWeaverAware {
+
+ /**
+ * Set the {@link LoadTimeWeaver} of this object's containing
+ * {@link org.springframework.context.ApplicationContext ApplicationContext}.
+ *
Invoked after the population of normal bean properties but before an
+ * initialization callback like
+ * {@link org.springframework.beans.factory.InitializingBean InitializingBean's}
+ * {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet() afterPropertiesSet()}
+ * or a custom init-method. Invoked after
+ * {@link org.springframework.context.ApplicationContextAware ApplicationContextAware's}
+ * {@link org.springframework.context.ApplicationContextAware#setApplicationContext setApplicationContext(..)}.
+ *
NOTE: This method will only be called if there actually is a
+ * LoadTimeWeaver available in the application context. If
+ * there is none, the method will simply not get invoked, assuming that the
+ * implementing object is able to activate its weaving dependency accordingly.
+ * @param loadTimeWeaver the LoadTimeWeaver instance (never null)
+ * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
+ * @see org.springframework.context.ApplicationContextAware#setApplicationContext
+ */
+ void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver);
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/weaving/LoadTimeWeaverAwareProcessor.java b/org.springframework.context/src/main/java/org/springframework/context/weaving/LoadTimeWeaverAwareProcessor.java
new file mode 100644
index 00000000000..aebfb201254
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/weaving/LoadTimeWeaverAwareProcessor.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.context.weaving;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.instrument.classloading.LoadTimeWeaver;
+import org.springframework.util.Assert;
+
+/**
+ * {@link org.springframework.beans.factory.config.BeanPostProcessor}
+ * implementation that passes the context's default {@link LoadTimeWeaver}
+ * to beans that implement the {@link LoadTimeWeaverAware} interface.
+ *
+ *
{@link org.springframework.context.ApplicationContext Application contexts}
+ * will automatically register this with their underlying
+ * {@link BeanFactory bean factory}, provided that a default
+ * LoadTimeWeaver is actually available.
+ *
+ *
Applications should not use this class directly.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see LoadTimeWeaverAware
+ * @see org.springframework.context.ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
+ */
+public class LoadTimeWeaverAwareProcessor implements BeanPostProcessor, BeanFactoryAware {
+
+ private LoadTimeWeaver loadTimeWeaver;
+
+ private BeanFactory beanFactory;
+
+
+ /**
+ * Create a new LoadTimeWeaverAwareProcessor that will
+ * auto-retrieve the {@link LoadTimeWeaver} from the containing
+ * {@link BeanFactory}, expecting a bean named
+ * {@link ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME "loadTimeWeaver"}.
+ */
+ public LoadTimeWeaverAwareProcessor() {
+ }
+
+ /**
+ * Create a new LoadTimeWeaverAwareProcessor for the given
+ * {@link LoadTimeWeaver}.
+ *
If the given loadTimeWeaver is null, then a
+ * LoadTimeWeaver will be auto-retrieved from the containing
+ * {@link BeanFactory}, expecting a bean named
+ * {@link ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME "loadTimeWeaver"}.
+ * @param loadTimeWeaver the specific LoadTimeWeaver that is to be used; can be null
+ */
+ public LoadTimeWeaverAwareProcessor(LoadTimeWeaver loadTimeWeaver) {
+ this.loadTimeWeaver = loadTimeWeaver;
+ }
+
+
+ public void setBeanFactory(BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ if (bean instanceof LoadTimeWeaverAware) {
+ LoadTimeWeaver ltw = this.loadTimeWeaver;
+ if (ltw == null) {
+ Assert.state(this.beanFactory != null,
+ "BeanFactory required if no LoadTimeWeaver explicitly specified");
+ ltw = (LoadTimeWeaver) this.beanFactory.getBean(
+ ConfigurableApplicationContext.LOAD_TIME_WEAVER_BEAN_NAME, LoadTimeWeaver.class);
+ }
+ ((LoadTimeWeaverAware) bean).setLoadTimeWeaver(ltw);
+ }
+ return bean;
+ }
+
+ public Object postProcessAfterInitialization(Object bean, String name) {
+ return bean;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/context/weaving/package.html b/org.springframework.context/src/main/java/org/springframework/context/weaving/package.html
new file mode 100644
index 00000000000..83020095e52
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/context/weaving/package.html
@@ -0,0 +1,8 @@
+
+
+
+Load-time weaving support for a Spring application context, building on Spring's
+{@link org.springframework.instrument.classloading.LoadTimeWeaver} abstraction.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/AbstractRemoteSlsbInvokerInterceptor.java b/org.springframework.context/src/main/java/org/springframework/ejb/access/AbstractRemoteSlsbInvokerInterceptor.java
new file mode 100644
index 00000000000..6a9271f2e2c
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/AbstractRemoteSlsbInvokerInterceptor.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.access;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.rmi.RemoteException;
+
+import javax.ejb.EJBHome;
+import javax.ejb.EJBObject;
+import javax.naming.NamingException;
+import javax.rmi.PortableRemoteObject;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.remoting.RemoteConnectFailureException;
+import org.springframework.remoting.RemoteLookupFailureException;
+import org.springframework.remoting.rmi.RmiClientInterceptorUtils;
+
+/**
+ * Base class for interceptors proxying remote Stateless Session Beans.
+ * Designed for EJB 2.x, but works for EJB 3 Session Beans as well.
+ *
+ *
Such an interceptor must be the last interceptor in the advice chain.
+ * In this case, there is no target object.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ */
+public abstract class AbstractRemoteSlsbInvokerInterceptor extends AbstractSlsbInvokerInterceptor {
+
+ private Class homeInterface;
+
+ private boolean refreshHomeOnConnectFailure = false;
+
+ private volatile boolean homeAsComponent = false;
+
+
+
+ /**
+ * Set a home interface that this invoker will narrow to before performing
+ * the parameterless SLSB create() call that returns the actual
+ * SLSB proxy.
+ *
Default is none, which will work on all J2EE servers that are not based
+ * on CORBA. A plain javax.ejb.EJBHome interface is known to be
+ * sufficient to make a WebSphere 5.0 Remote SLSB work. On other servers,
+ * the specific home interface for the target SLSB might be necessary.
+ */
+ public void setHomeInterface(Class homeInterface) {
+ if (homeInterface != null && !homeInterface.isInterface()) {
+ throw new IllegalArgumentException(
+ "Home interface class [" + homeInterface.getClass() + "] is not an interface");
+ }
+ this.homeInterface = homeInterface;
+ }
+
+ /**
+ * Set whether to refresh the EJB home on connect failure.
+ * Default is "false".
+ *
Can be turned on to allow for hot restart of the EJB server.
+ * If a cached EJB home throws an RMI exception that indicates a
+ * remote connect failure, a fresh home will be fetched and the
+ * invocation will be retried.
+ * @see java.rmi.ConnectException
+ * @see java.rmi.ConnectIOException
+ * @see java.rmi.NoSuchObjectException
+ */
+ public void setRefreshHomeOnConnectFailure(boolean refreshHomeOnConnectFailure) {
+ this.refreshHomeOnConnectFailure = refreshHomeOnConnectFailure;
+ }
+
+ protected boolean isHomeRefreshable() {
+ return this.refreshHomeOnConnectFailure;
+ }
+
+
+ /**
+ * This overridden lookup implementation performs a narrow operation
+ * after the JNDI lookup, provided that a home interface is specified.
+ * @see #setHomeInterface
+ * @see javax.rmi.PortableRemoteObject#narrow
+ */
+ protected Object lookup() throws NamingException {
+ Object homeObject = super.lookup();
+ if (this.homeInterface != null) {
+ try {
+ homeObject = PortableRemoteObject.narrow(homeObject, this.homeInterface);
+ }
+ catch (ClassCastException ex) {
+ throw new RemoteLookupFailureException(
+ "Could not narrow EJB home stub to home interface [" + this.homeInterface.getName() + "]", ex);
+ }
+ }
+ return homeObject;
+ }
+
+ /**
+ * Check for EJB3-style home object that serves as EJB component directly.
+ */
+ protected Method getCreateMethod(Object home) throws EjbAccessException {
+ if (this.homeAsComponent) {
+ return null;
+ }
+ if (!(home instanceof EJBHome)) {
+ // An EJB3 Session Bean...
+ this.homeAsComponent = true;
+ return null;
+ }
+ return super.getCreateMethod(home);
+ }
+
+
+ /**
+ * Fetches an EJB home object and delegates to doInvoke.
+ *
If configured to refresh on connect failure, it will call
+ * {@link #refreshAndRetry} on corresponding RMI exceptions.
+ * @see #getHome
+ * @see #doInvoke
+ * @see #refreshAndRetry
+ */
+ public Object invokeInContext(MethodInvocation invocation) throws Throwable {
+ try {
+ return doInvoke(invocation);
+ }
+ catch (RemoteConnectFailureException ex) {
+ return handleRemoteConnectFailure(invocation, ex);
+ }
+ catch (RemoteException ex) {
+ if (isConnectFailure(ex)) {
+ return handleRemoteConnectFailure(invocation, ex);
+ }
+ else {
+ throw ex;
+ }
+ }
+ }
+
+ /**
+ * Determine whether the given RMI exception indicates a connect failure.
+ *
The default implementation delegates to RmiClientInterceptorUtils.
+ * @param ex the RMI exception to check
+ * @return whether the exception should be treated as connect failure
+ * @see org.springframework.remoting.rmi.RmiClientInterceptorUtils#isConnectFailure
+ */
+ protected boolean isConnectFailure(RemoteException ex) {
+ return RmiClientInterceptorUtils.isConnectFailure(ex);
+ }
+
+ private Object handleRemoteConnectFailure(MethodInvocation invocation, Exception ex) throws Throwable {
+ if (this.refreshHomeOnConnectFailure) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Could not connect to remote EJB [" + getJndiName() + "] - retrying", ex);
+ }
+ else if (logger.isWarnEnabled()) {
+ logger.warn("Could not connect to remote EJB [" + getJndiName() + "] - retrying");
+ }
+ return refreshAndRetry(invocation);
+ }
+ else {
+ throw ex;
+ }
+ }
+
+ /**
+ * Refresh the EJB home object and retry the given invocation.
+ * Called by invoke on connect failure.
+ * @param invocation the AOP method invocation
+ * @return the invocation result, if any
+ * @throws Throwable in case of invocation failure
+ * @see #invoke
+ */
+ protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable {
+ try {
+ refreshHome();
+ }
+ catch (NamingException ex) {
+ throw new RemoteLookupFailureException("Failed to locate remote EJB [" + getJndiName() + "]", ex);
+ }
+ return doInvoke(invocation);
+ }
+
+
+ /**
+ * Perform the given invocation on the current EJB home.
+ * Template method to be implemented by subclasses.
+ * @param invocation the AOP method invocation
+ * @return the invocation result, if any
+ * @throws Throwable in case of invocation failure
+ * @see #getHome
+ * @see #newSessionBeanInstance
+ */
+ protected abstract Object doInvoke(MethodInvocation invocation) throws Throwable;
+
+
+ /**
+ * Return a new instance of the stateless session bean.
+ * To be invoked by concrete remote SLSB invoker subclasses.
+ *
Can be overridden to change the algorithm.
+ * @throws NamingException if thrown by JNDI
+ * @throws InvocationTargetException if thrown by the create method
+ * @see #create
+ */
+ protected Object newSessionBeanInstance() throws NamingException, InvocationTargetException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Trying to create reference to remote EJB");
+ }
+ Object ejbInstance = create();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Obtained reference to remote EJB: " + ejbInstance);
+ }
+ return ejbInstance;
+ }
+
+ /**
+ * Remove the given EJB instance.
+ * To be invoked by concrete remote SLSB invoker subclasses.
+ * @param ejb the EJB instance to remove
+ * @see javax.ejb.EJBObject#remove
+ */
+ protected void removeSessionBeanInstance(EJBObject ejb) {
+ if (ejb != null && !this.homeAsComponent) {
+ try {
+ ejb.remove();
+ }
+ catch (Throwable ex) {
+ logger.warn("Could not invoke 'remove' on remote EJB proxy", ex);
+ }
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/AbstractSlsbInvokerInterceptor.java b/org.springframework.context/src/main/java/org/springframework/ejb/access/AbstractSlsbInvokerInterceptor.java
new file mode 100644
index 00000000000..887f3903253
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/AbstractSlsbInvokerInterceptor.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.access;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.jndi.JndiObjectLocator;
+
+/**
+ * Base class for AOP interceptors invoking local or remote Stateless Session Beans.
+ * Designed for EJB 2.x, but works for EJB 3 Session Beans as well.
+ *
+ *
Such an interceptor must be the last interceptor in the advice chain.
+ * In this case, there is no direct target object: The call is handled in a
+ * special way, getting executed on an EJB instance retrieved via an EJB home.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ */
+public abstract class AbstractSlsbInvokerInterceptor extends JndiObjectLocator
+ implements MethodInterceptor {
+
+ private boolean lookupHomeOnStartup = true;
+
+ private boolean cacheHome = true;
+
+ private boolean exposeAccessContext = false;
+
+ /**
+ * The EJB's home object, potentially cached.
+ * The type must be Object as it could be either EJBHome or EJBLocalHome.
+ */
+ private Object cachedHome;
+
+ /**
+ * The no-arg create() method required on EJB homes, potentially cached.
+ */
+ private Method createMethod;
+
+ private final Object homeMonitor = new Object();
+
+
+ /**
+ * Set whether to look up the EJB home object on startup.
+ * Default is "true".
+ *
Can be turned off to allow for late start of the EJB server.
+ * In this case, the EJB home object will be fetched on first access.
+ * @see #setCacheHome
+ */
+ public void setLookupHomeOnStartup(boolean lookupHomeOnStartup) {
+ this.lookupHomeOnStartup = lookupHomeOnStartup;
+ }
+
+ /**
+ * Set whether to cache the EJB home object once it has been located.
+ * Default is "true".
+ *
Can be turned off to allow for hot restart of the EJB server.
+ * In this case, the EJB home object will be fetched for each invocation.
+ * @see #setLookupHomeOnStartup
+ */
+ public void setCacheHome(boolean cacheHome) {
+ this.cacheHome = cacheHome;
+ }
+
+ /**
+ * Set whether to expose the JNDI environment context for all access to the target
+ * EJB, i.e. for all method invocations on the exposed object reference.
+ *
Default is "false", i.e. to only expose the JNDI context for object lookup.
+ * Switch this flag to "true" in order to expose the JNDI environment (including
+ * the authorization context) for each EJB invocation, as needed by WebLogic
+ * for EJBs with authorization requirements.
+ */
+ public void setExposeAccessContext(boolean exposeAccessContext) {
+ this.exposeAccessContext = exposeAccessContext;
+ }
+
+
+ /**
+ * Fetches EJB home on startup, if necessary.
+ * @see #setLookupHomeOnStartup
+ * @see #refreshHome
+ */
+ public void afterPropertiesSet() throws NamingException {
+ super.afterPropertiesSet();
+ if (this.lookupHomeOnStartup) {
+ // look up EJB home and create method
+ refreshHome();
+ }
+ }
+
+ /**
+ * Refresh the cached home object, if applicable.
+ * Also caches the create method on the home object.
+ * @throws NamingException if thrown by the JNDI lookup
+ * @see #lookup
+ * @see #getCreateMethod
+ */
+ protected void refreshHome() throws NamingException {
+ synchronized (this.homeMonitor) {
+ Object home = lookup();
+ if (this.cacheHome) {
+ this.cachedHome = home;
+ this.createMethod = getCreateMethod(home);
+ }
+ }
+ }
+
+ /**
+ * Determine the create method of the given EJB home object.
+ * @param home the EJB home object
+ * @return the create method
+ * @throws EjbAccessException if the method couldn't be retrieved
+ */
+ protected Method getCreateMethod(Object home) throws EjbAccessException {
+ try {
+ // Cache the EJB create() method that must be declared on the home interface.
+ return home.getClass().getMethod("create", (Class[]) null);
+ }
+ catch (NoSuchMethodException ex) {
+ throw new EjbAccessException("EJB home [" + home + "] has no no-arg create() method");
+ }
+ }
+
+ /**
+ * Return the EJB home object to use. Called for each invocation.
+ *
Default implementation returns the home created on initialization,
+ * if any; else, it invokes lookup to get a new proxy for each invocation.
+ *
Can be overridden in subclasses, for example to cache a home object
+ * for a given amount of time before recreating it, or to test the home
+ * object whether it is still alive.
+ * @return the EJB home object to use for an invocation
+ * @throws NamingException if proxy creation failed
+ * @see #lookup
+ * @see #getCreateMethod
+ */
+ protected Object getHome() throws NamingException {
+ if (!this.cacheHome || (this.lookupHomeOnStartup && !isHomeRefreshable())) {
+ return (this.cachedHome != null ? this.cachedHome : lookup());
+ }
+ else {
+ synchronized (this.homeMonitor) {
+ if (this.cachedHome == null) {
+ this.cachedHome = lookup();
+ this.createMethod = getCreateMethod(this.cachedHome);
+ }
+ return this.cachedHome;
+ }
+ }
+ }
+
+ /**
+ * Return whether the cached EJB home object is potentially
+ * subject to on-demand refreshing. Default is "false".
+ */
+ protected boolean isHomeRefreshable() {
+ return false;
+ }
+
+
+ /**
+ * Prepares the thread context if necessar, and delegates to
+ * {@link #invokeInContext}.
+ */
+ public Object invoke(MethodInvocation invocation) throws Throwable {
+ Context ctx = (this.exposeAccessContext ? getJndiTemplate().getContext() : null);
+ try {
+ return invokeInContext(invocation);
+ }
+ finally {
+ getJndiTemplate().releaseContext(ctx);
+ }
+ }
+
+ /**
+ * Perform the given invocation on the current EJB home,
+ * within the thread context being prepared accordingly.
+ * Template method to be implemented by subclasses.
+ * @param invocation the AOP method invocation
+ * @return the invocation result, if any
+ * @throws Throwable in case of invocation failure
+ */
+ protected abstract Object invokeInContext(MethodInvocation invocation) throws Throwable;
+
+
+ /**
+ * Invokes the create() method on the cached EJB home object.
+ * @return a new EJBObject or EJBLocalObject
+ * @throws NamingException if thrown by JNDI
+ * @throws InvocationTargetException if thrown by the create method
+ */
+ protected Object create() throws NamingException, InvocationTargetException {
+ try {
+ Object home = getHome();
+ Method createMethodToUse = this.createMethod;
+ if (createMethodToUse == null) {
+ createMethodToUse = getCreateMethod(home);
+ }
+ if (createMethodToUse == null) {
+ return home;
+ }
+ // Invoke create() method on EJB home object.
+ return createMethodToUse.invoke(home, (Object[]) null);
+ }
+ catch (IllegalAccessException ex) {
+ throw new EjbAccessException("Could not access EJB home create() method", ex);
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/EjbAccessException.java b/org.springframework.context/src/main/java/org/springframework/ejb/access/EjbAccessException.java
new file mode 100644
index 00000000000..26d0a174ad6
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/EjbAccessException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2002-2006 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.access;
+
+import org.springframework.core.NestedRuntimeException;
+
+/**
+ * Exception that gets thrown when an EJB stub cannot be accessed properly.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public class EjbAccessException extends NestedRuntimeException {
+
+ /**
+ * Constructor for EjbAccessException.
+ * @param msg the detail message
+ */
+ public EjbAccessException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructor for EjbAccessException.
+ * @param msg the detail message
+ * @param cause the root cause
+ */
+ public EjbAccessException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/LocalSlsbInvokerInterceptor.java b/org.springframework.context/src/main/java/org/springframework/ejb/access/LocalSlsbInvokerInterceptor.java
new file mode 100644
index 00000000000..83adeef066c
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/LocalSlsbInvokerInterceptor.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.access;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.ejb.CreateException;
+import javax.ejb.EJBLocalHome;
+import javax.ejb.EJBLocalObject;
+import javax.naming.NamingException;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+/**
+ * Invoker for a local Stateless Session Bean.
+ * Designed for EJB 2.x, but works for EJB 3 Session Beans as well.
+ *
+ *
Caches the home object, since a local EJB home can never go stale.
+ * See {@link org.springframework.jndi.JndiObjectLocator} for info on
+ * how to specify the JNDI location of the target EJB.
+ *
+ *
In a bean container, this class is normally best used as a singleton. However,
+ * if that bean container pre-instantiates singletons (as do the XML ApplicationContext
+ * variants) you may have a problem if the bean container is loaded before the EJB
+ * container loads the target EJB. That is because by default the JNDI lookup will be
+ * performed in the init method of this class and cached, but the EJB will not have been
+ * bound at the target location yet. The best solution is to set the lookupHomeOnStartup
+ * property to false, in which case the home will be fetched on first access to the EJB.
+ * (This flag is only true by default for backwards compatibility reasons).
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup
+ * @see AbstractSlsbInvokerInterceptor#setCacheHome
+ */
+public class LocalSlsbInvokerInterceptor extends AbstractSlsbInvokerInterceptor {
+
+ private volatile boolean homeAsComponent = false;
+
+
+ /**
+ * This implementation "creates" a new EJB instance for each invocation.
+ * Can be overridden for custom invocation strategies.
+ *
Alternatively, override {@link #getSessionBeanInstance} and
+ * {@link #releaseSessionBeanInstance} to change EJB instance creation,
+ * for example to hold a single shared EJB instance.
+ */
+ public Object invokeInContext(MethodInvocation invocation) throws Throwable {
+ Object ejb = null;
+ try {
+ ejb = getSessionBeanInstance();
+ Method method = invocation.getMethod();
+ if (method.getDeclaringClass().isInstance(ejb)) {
+ // directly implemented
+ return method.invoke(ejb, invocation.getArguments());
+ }
+ else {
+ // not directly implemented
+ Method ejbMethod = ejb.getClass().getMethod(method.getName(), method.getParameterTypes());
+ return ejbMethod.invoke(ejb, invocation.getArguments());
+ }
+ }
+ catch (InvocationTargetException ex) {
+ Throwable targetEx = ex.getTargetException();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Method of local EJB [" + getJndiName() + "] threw exception", targetEx);
+ }
+ if (targetEx instanceof CreateException) {
+ throw new EjbAccessException("Could not create local EJB [" + getJndiName() + "]", targetEx);
+ }
+ else {
+ throw targetEx;
+ }
+ }
+ catch (NamingException ex) {
+ throw new EjbAccessException("Failed to locate local EJB [" + getJndiName() + "]", ex);
+ }
+ catch (IllegalAccessException ex) {
+ throw new EjbAccessException("Could not access method [" + invocation.getMethod().getName() +
+ "] of local EJB [" + getJndiName() + "]", ex);
+ }
+ finally {
+ if (ejb instanceof EJBLocalObject) {
+ releaseSessionBeanInstance((EJBLocalObject) ejb);
+ }
+ }
+ }
+
+ /**
+ * Check for EJB3-style home object that serves as EJB component directly.
+ */
+ protected Method getCreateMethod(Object home) throws EjbAccessException {
+ if (this.homeAsComponent) {
+ return null;
+ }
+ if (!(home instanceof EJBLocalHome)) {
+ // An EJB3 Session Bean...
+ this.homeAsComponent = true;
+ return null;
+ }
+ return super.getCreateMethod(home);
+ }
+
+ /**
+ * Return an EJB instance to delegate the call to.
+ * Default implementation delegates to newSessionBeanInstance.
+ * @throws NamingException if thrown by JNDI
+ * @throws InvocationTargetException if thrown by the create method
+ * @see #newSessionBeanInstance
+ */
+ protected Object getSessionBeanInstance() throws NamingException, InvocationTargetException {
+ return newSessionBeanInstance();
+ }
+
+ /**
+ * Release the given EJB instance.
+ * Default implementation delegates to removeSessionBeanInstance.
+ * @param ejb the EJB instance to release
+ * @see #removeSessionBeanInstance
+ */
+ protected void releaseSessionBeanInstance(EJBLocalObject ejb) {
+ removeSessionBeanInstance(ejb);
+ }
+
+ /**
+ * Return a new instance of the stateless session bean.
+ * Can be overridden to change the algorithm.
+ * @throws NamingException if thrown by JNDI
+ * @throws InvocationTargetException if thrown by the create method
+ * @see #create
+ */
+ protected Object newSessionBeanInstance() throws NamingException, InvocationTargetException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Trying to create reference to local EJB");
+ }
+ Object ejbInstance = create();
+ if (logger.isDebugEnabled()) {
+ logger.debug("Obtained reference to local EJB: " + ejbInstance);
+ }
+ return ejbInstance;
+ }
+
+ /**
+ * Remove the given EJB instance.
+ * @param ejb the EJB instance to remove
+ * @see javax.ejb.EJBLocalObject#remove()
+ */
+ protected void removeSessionBeanInstance(EJBLocalObject ejb) {
+ if (ejb != null && !this.homeAsComponent) {
+ try {
+ ejb.remove();
+ }
+ catch (Throwable ex) {
+ logger.warn("Could not invoke 'remove' on local EJB proxy", ex);
+ }
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/LocalStatelessSessionProxyFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/ejb/access/LocalStatelessSessionProxyFactoryBean.java
new file mode 100644
index 00000000000..8fb7f64e4e5
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/LocalStatelessSessionProxyFactoryBean.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.access;
+
+import javax.naming.NamingException;
+
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Convenient factory for local Stateless Session Bean (SLSB) proxies.
+ * Designed for EJB 2.x, but works for EJB 3 Session Beans as well.
+ *
+ *
See {@link org.springframework.jndi.JndiObjectLocator} for info on
+ * how to specify the JNDI location of the target EJB.
+ *
+ *
If you want control over interceptor chaining, use an AOP ProxyFactoryBean
+ * with LocalSlsbInvokerInterceptor rather than rely on this class.
+ *
+ *
In a bean container, this class is normally best used as a singleton. However,
+ * if that bean container pre-instantiates singletons (as do the XML ApplicationContext
+ * variants) you may have a problem if the bean container is loaded before the EJB
+ * container loads the target EJB. That is because by default the JNDI lookup will be
+ * performed in the init method of this class and cached, but the EJB will not have been
+ * bound at the target location yet. The best solution is to set the "lookupHomeOnStartup"
+ * property to "false", in which case the home will be fetched on first access to the EJB.
+ * (This flag is only true by default for backwards compatibility reasons).
+ *
+ * @author Rod Johnson
+ * @author Colin Sampaleanu
+ * @since 09.05.2003
+ * @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup
+ * @see AbstractSlsbInvokerInterceptor#setCacheHome
+ */
+public class LocalStatelessSessionProxyFactoryBean extends LocalSlsbInvokerInterceptor
+ implements FactoryBean, BeanClassLoaderAware {
+
+ /** The business interface of the EJB we're proxying */
+ private Class businessInterface;
+
+ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
+
+ /** EJBLocalObject */
+ private Object proxy;
+
+
+ /**
+ * Set the business interface of the EJB we're proxying.
+ * This will normally be a super-interface of the EJB local component interface.
+ * Using a business methods interface is a best practice when implementing EJBs.
+ * @param businessInterface set the business interface of the EJB
+ */
+ public void setBusinessInterface(Class businessInterface) {
+ this.businessInterface = businessInterface;
+ }
+
+ /**
+ * Return the business interface of the EJB we're proxying.
+ */
+ public Class getBusinessInterface() {
+ return this.businessInterface;
+ }
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.beanClassLoader = classLoader;
+ }
+
+ public void afterPropertiesSet() throws NamingException {
+ super.afterPropertiesSet();
+ if (this.businessInterface == null) {
+ throw new IllegalArgumentException("businessInterface is required");
+ }
+ this.proxy = new ProxyFactory(this.businessInterface, this).getProxy(this.beanClassLoader);
+ }
+
+
+ public Object getObject() {
+ return this.proxy;
+ }
+
+ public Class getObjectType() {
+ return this.businessInterface;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/SimpleRemoteSlsbInvokerInterceptor.java b/org.springframework.context/src/main/java/org/springframework/ejb/access/SimpleRemoteSlsbInvokerInterceptor.java
new file mode 100644
index 00000000000..6be3b0b997e
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/SimpleRemoteSlsbInvokerInterceptor.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.access;
+
+import java.lang.reflect.InvocationTargetException;
+import java.rmi.RemoteException;
+
+import javax.ejb.CreateException;
+import javax.ejb.EJBObject;
+import javax.naming.NamingException;
+
+import org.aopalliance.intercept.MethodInvocation;
+
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.remoting.RemoteLookupFailureException;
+import org.springframework.remoting.rmi.RmiClientInterceptorUtils;
+
+/**
+ * Basic invoker for a remote Stateless Session Bean.
+ * Designed for EJB 2.x, but works for EJB 3 Session Beans as well.
+ *
+ *
"Creates" a new EJB instance for each invocation, or caches the session
+ * bean instance for all invocations (see {@link #setCacheSessionBean}).
+ * See {@link org.springframework.jndi.JndiObjectLocator} for info on
+ * how to specify the JNDI location of the target EJB.
+ *
+ *
In a bean container, this class is normally best used as a singleton. However,
+ * if that bean container pre-instantiates singletons (as do the XML ApplicationContext
+ * variants) you may have a problem if the bean container is loaded before the EJB
+ * container loads the target EJB. That is because by default the JNDI lookup will be
+ * performed in the init method of this class and cached, but the EJB will not have been
+ * bound at the target location yet. The best solution is to set the "lookupHomeOnStartup"
+ * property to "false", in which case the home will be fetched on first access to the EJB.
+ * (This flag is only true by default for backwards compatibility reasons).
+ *
+ *
This invoker is typically used with an RMI business interface, which serves
+ * as super-interface of the EJB component interface. Alternatively, this invoker
+ * can also proxy a remote SLSB with a matching non-RMI business interface, i.e. an
+ * interface that mirrors the EJB business methods but does not declare RemoteExceptions.
+ * In the latter case, RemoteExceptions thrown by the EJB stub will automatically get
+ * converted to Spring's unchecked RemoteAccessException.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @since 09.05.2003
+ * @see org.springframework.remoting.RemoteAccessException
+ * @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup
+ * @see AbstractSlsbInvokerInterceptor#setCacheHome
+ * @see AbstractRemoteSlsbInvokerInterceptor#setRefreshHomeOnConnectFailure
+ */
+public class SimpleRemoteSlsbInvokerInterceptor extends AbstractRemoteSlsbInvokerInterceptor
+ implements DisposableBean {
+
+ private boolean cacheSessionBean = false;
+
+ private Object beanInstance;
+
+ private final Object beanInstanceMonitor = new Object();
+
+
+ /**
+ * Set whether to cache the actual session bean object.
+ *
Off by default for standard EJB compliance. Turn this flag
+ * on to optimize session bean access for servers that are
+ * known to allow for caching the actual session bean object.
+ * @see #setCacheHome
+ */
+ public void setCacheSessionBean(boolean cacheSessionBean) {
+ this.cacheSessionBean = cacheSessionBean;
+ }
+
+
+ /**
+ * This implementation "creates" a new EJB instance for each invocation.
+ * Can be overridden for custom invocation strategies.
+ *
Alternatively, override {@link #getSessionBeanInstance} and
+ * {@link #releaseSessionBeanInstance} to change EJB instance creation,
+ * for example to hold a single shared EJB component instance.
+ */
+ protected Object doInvoke(MethodInvocation invocation) throws Throwable {
+ Object ejb = null;
+ try {
+ ejb = getSessionBeanInstance();
+ return RmiClientInterceptorUtils.invokeRemoteMethod(invocation, ejb);
+ }
+ catch (NamingException ex) {
+ throw new RemoteLookupFailureException("Failed to locate remote EJB [" + getJndiName() + "]", ex);
+ }
+ catch (InvocationTargetException ex) {
+ Throwable targetEx = ex.getTargetException();
+ if (targetEx instanceof RemoteException) {
+ RemoteException rex = (RemoteException) targetEx;
+ throw RmiClientInterceptorUtils.convertRmiAccessException(
+ invocation.getMethod(), rex, isConnectFailure(rex), getJndiName());
+ }
+ else if (targetEx instanceof CreateException) {
+ throw RmiClientInterceptorUtils.convertRmiAccessException(
+ invocation.getMethod(), targetEx, "Could not create remote EJB [" + getJndiName() + "]");
+ }
+ throw targetEx;
+ }
+ finally {
+ if (ejb instanceof EJBObject) {
+ releaseSessionBeanInstance((EJBObject) ejb);
+ }
+ }
+ }
+
+ /**
+ * Return an EJB component instance to delegate the call to.
+ *
The default implementation delegates to {@link #newSessionBeanInstance}.
+ * @return the EJB component instance
+ * @throws NamingException if thrown by JNDI
+ * @throws InvocationTargetException if thrown by the create method
+ * @see #newSessionBeanInstance
+ */
+ protected Object getSessionBeanInstance() throws NamingException, InvocationTargetException {
+ if (this.cacheSessionBean) {
+ synchronized (this.beanInstanceMonitor) {
+ if (this.beanInstance == null) {
+ this.beanInstance = newSessionBeanInstance();
+ }
+ return this.beanInstance;
+ }
+ }
+ else {
+ return newSessionBeanInstance();
+ }
+ }
+
+ /**
+ * Release the given EJB instance.
+ *
The default implementation delegates to {@link #removeSessionBeanInstance}.
+ * @param ejb the EJB component instance to release
+ * @see #removeSessionBeanInstance
+ */
+ protected void releaseSessionBeanInstance(EJBObject ejb) {
+ if (!this.cacheSessionBean) {
+ removeSessionBeanInstance(ejb);
+ }
+ }
+
+ /**
+ * Reset the cached session bean instance, if necessary.
+ */
+ protected void refreshHome() throws NamingException {
+ super.refreshHome();
+ if (this.cacheSessionBean) {
+ synchronized (this.beanInstanceMonitor) {
+ this.beanInstance = null;
+ }
+ }
+ }
+
+ /**
+ * Remove the cached session bean instance, if necessary.
+ */
+ public void destroy() {
+ if (this.cacheSessionBean) {
+ synchronized (this.beanInstanceMonitor) {
+ if (this.beanInstance instanceof EJBObject) {
+ removeSessionBeanInstance((EJBObject) this.beanInstance);
+ }
+ }
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/SimpleRemoteStatelessSessionProxyFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/ejb/access/SimpleRemoteStatelessSessionProxyFactoryBean.java
new file mode 100644
index 00000000000..e7f3136011b
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/SimpleRemoteStatelessSessionProxyFactoryBean.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.access;
+
+import javax.naming.NamingException;
+
+import org.springframework.aop.framework.ProxyFactory;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Convenient factory for remote SLSB proxies.
+ * Designed for EJB 2.x, but works for EJB 3 Session Beans as well.
+ *
+ *
See {@link org.springframework.jndi.JndiObjectLocator} for info on
+ * how to specify the JNDI location of the target EJB.
+ *
+ *
If you want control over interceptor chaining, use an AOP ProxyFactoryBean
+ * with SimpleRemoteSlsbInvokerInterceptor rather than rely on this class.
+ *
+ *
In a bean container, this class is normally best used as a singleton. However,
+ * if that bean container pre-instantiates singletons (as do the XML ApplicationContext
+ * variants) you may have a problem if the bean container is loaded before the EJB
+ * container loads the target EJB. That is because by default the JNDI lookup will be
+ * performed in the init method of this class and cached, but the EJB will not have been
+ * bound at the target location yet. The best solution is to set the lookupHomeOnStartup
+ * property to false, in which case the home will be fetched on first access to the EJB.
+ * (This flag is only true by default for backwards compatibility reasons).
+ *
+ *
This proxy factory is typically used with an RMI business interface, which serves
+ * as super-interface of the EJB component interface. Alternatively, this factory
+ * can also proxy a remote SLSB with a matching non-RMI business interface, i.e. an
+ * interface that mirrors the EJB business methods but does not declare RemoteExceptions.
+ * In the latter case, RemoteExceptions thrown by the EJB stub will automatically get
+ * converted to Spring's unchecked RemoteAccessException.
+ *
+ * @author Rod Johnson
+ * @author Colin Sampaleanu
+ * @author Juergen Hoeller
+ * @since 09.05.2003
+ * @see org.springframework.remoting.RemoteAccessException
+ * @see AbstractSlsbInvokerInterceptor#setLookupHomeOnStartup
+ * @see AbstractSlsbInvokerInterceptor#setCacheHome
+ * @see AbstractRemoteSlsbInvokerInterceptor#setRefreshHomeOnConnectFailure
+ */
+public class SimpleRemoteStatelessSessionProxyFactoryBean extends SimpleRemoteSlsbInvokerInterceptor
+ implements FactoryBean, BeanClassLoaderAware {
+
+ /** The business interface of the EJB we're proxying */
+ private Class businessInterface;
+
+ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
+
+ /** EJBObject */
+ private Object proxy;
+
+
+ /**
+ * Set the business interface of the EJB we're proxying.
+ * This will normally be a super-interface of the EJB remote component interface.
+ * Using a business methods interface is a best practice when implementing EJBs.
+ *
You can also specify a matching non-RMI business interface, i.e. an interface
+ * that mirrors the EJB business methods but does not declare RemoteExceptions.
+ * In this case, RemoteExceptions thrown by the EJB stub will automatically get
+ * converted to Spring's generic RemoteAccessException.
+ * @param businessInterface the business interface of the EJB
+ */
+ public void setBusinessInterface(Class businessInterface) {
+ this.businessInterface = businessInterface;
+ }
+
+ /**
+ * Return the business interface of the EJB we're proxying.
+ */
+ public Class getBusinessInterface() {
+ return this.businessInterface;
+ }
+
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.beanClassLoader = classLoader;
+ }
+
+ public void afterPropertiesSet() throws NamingException {
+ super.afterPropertiesSet();
+ if (this.businessInterface == null) {
+ throw new IllegalArgumentException("businessInterface is required");
+ }
+ this.proxy = new ProxyFactory(this.businessInterface, this).getProxy(this.beanClassLoader);
+ }
+
+
+ public Object getObject() {
+ return this.proxy;
+ }
+
+ public Class getObjectType() {
+ return this.businessInterface;
+ }
+
+ public boolean isSingleton() {
+ return true;
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/access/package.html b/org.springframework.context/src/main/java/org/springframework/ejb/access/package.html
new file mode 100644
index 00000000000..5bc5538d1d0
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/access/package.html
@@ -0,0 +1,25 @@
+
+
+
+This package contains classes that allow easy access to EJBs.
+The basis are AOP interceptors run before and after the EJB invocation.
+In particular, the classes in this package allow transparent access
+to stateless session beans (SLSBs) with local interfaces, avoiding
+the need for application code using them to use EJB-specific APIs
+and JNDI lookups, and work with business interfaces that could be
+implemented without using EJB. This provides a valuable decoupling
+of client (such as web components) and business objects (which may
+or may not be EJBs). This gives us the choice of introducing EJB
+into an application (or removing EJB from an application) without
+affecting code using business objects.
+
+
However, the implementation and naming of classes in this package has changed.
+It now uses FactoryBeans and AOP, rather than the custom bean definitions described in
+Expert One-on-One J2EE.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/AbstractJndiLocatingBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/ejb/config/AbstractJndiLocatingBeanDefinitionParser.java
new file mode 100644
index 00000000000..8c96809d668
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/AbstractJndiLocatingBeanDefinitionParser.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
+import org.springframework.util.StringUtils;
+import org.springframework.util.xml.DomUtils;
+
+/**
+ * Abstract base class for BeanDefinitionParsers which build
+ * JNDI-locating beans, supporting an optional "jndiEnvironment"
+ * bean property, populated from an "environment" XML sub-element.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+abstract class AbstractJndiLocatingBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser {
+
+ public static final String ENVIRONMENT = "environment";
+
+ public static final String ENVIRONMENT_REF = "environment-ref";
+
+ public static final String JNDI_ENVIRONMENT = "jndiEnvironment";
+
+
+ protected boolean isEligibleAttribute(String attributeName) {
+ return (super.isEligibleAttribute(attributeName) && !ENVIRONMENT_REF.equals(attributeName));
+ }
+
+ protected void postProcess(BeanDefinitionBuilder definitionBuilder, Element element) {
+ Object envValue = DomUtils.getChildElementValueByTagName(element, ENVIRONMENT);
+ if (envValue != null) {
+ // Specific environment settings defined, overriding any shared properties.
+ definitionBuilder.addPropertyValue(JNDI_ENVIRONMENT, envValue);
+ }
+ else {
+ // Check whether there is a reference to shared environment properties...
+ String envRef = element.getAttribute(ENVIRONMENT_REF);
+ if (StringUtils.hasLength(envRef)) {
+ definitionBuilder.addPropertyValue(JNDI_ENVIRONMENT, new RuntimeBeanReference(envRef));
+ }
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/JeeNamespaceHandler.java b/org.springframework.context/src/main/java/org/springframework/ejb/config/JeeNamespaceHandler.java
new file mode 100644
index 00000000000..8a8e6d204a2
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/JeeNamespaceHandler.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.config;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+/**
+ * {@link org.springframework.beans.factory.xml.NamespaceHandler}
+ * for the 'jee' namespace.
+ *
+ * @author Rob Harrop
+ * @since 2.0
+ */
+public class JeeNamespaceHandler extends NamespaceHandlerSupport {
+
+ public void init() {
+ registerBeanDefinitionParser("jndi-lookup", new JndiLookupBeanDefinitionParser());
+ registerBeanDefinitionParser("local-slsb", new LocalStatelessSessionBeanDefinitionParser());
+ registerBeanDefinitionParser("remote-slsb", new RemoteStatelessSessionBeanDefinitionParser());
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/JndiLookupBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/ejb/config/JndiLookupBeanDefinitionParser.java
new file mode 100644
index 00000000000..9c0c78eae19
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/JndiLookupBeanDefinitionParser.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.jndi.JndiObjectFactoryBean;
+import org.springframework.util.StringUtils;
+
+/**
+ * Simple {@link org.springframework.beans.factory.xml.BeanDefinitionParser} implementation that
+ * translates jndi-lookup tag into {@link JndiObjectFactoryBean} definitions.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see JndiObjectFactoryBean
+ */
+class JndiLookupBeanDefinitionParser extends AbstractJndiLocatingBeanDefinitionParser {
+
+ public static final String DEFAULT_VALUE = "default-value";
+
+ public static final String DEFAULT_REF = "default-ref";
+
+ public static final String DEFAULT_OBJECT = "defaultObject";
+
+
+ protected Class getBeanClass(Element element) {
+ return JndiObjectFactoryBean.class;
+ }
+
+ protected boolean isEligibleAttribute(String attributeName) {
+ return (super.isEligibleAttribute(attributeName) &&
+ !DEFAULT_VALUE.equals(attributeName) && !DEFAULT_REF.equals(attributeName));
+ }
+
+ protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ super.doParse(element, parserContext, builder);
+
+ String defaultValue = element.getAttribute(DEFAULT_VALUE);
+ String defaultRef = element.getAttribute(DEFAULT_REF);
+ if (StringUtils.hasLength(defaultValue)) {
+ if (StringUtils.hasLength(defaultRef)) {
+ parserContext.getReaderContext().error(" element is only allowed to contain either " +
+ "'default-value' attribute OR 'default-ref' attribute, not both", element);
+ }
+ builder.addPropertyValue(DEFAULT_OBJECT, defaultValue);
+ }
+ else if (StringUtils.hasLength(defaultRef)) {
+ builder.addPropertyValue(DEFAULT_OBJECT, new RuntimeBeanReference(defaultRef));
+ }
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/LocalStatelessSessionBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/ejb/config/LocalStatelessSessionBeanDefinitionParser.java
new file mode 100644
index 00000000000..e921ad41aea
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/LocalStatelessSessionBeanDefinitionParser.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean;
+
+/**
+ * {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
+ * implementation for parsing 'local-slsb' tags and
+ * creating {@link LocalStatelessSessionProxyFactoryBean} definitions.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+class LocalStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBeanDefinitionParser {
+
+ protected String getBeanClassName(Element element) {
+ return "org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean";
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/RemoteStatelessSessionBeanDefinitionParser.java b/org.springframework.context/src/main/java/org/springframework/ejb/config/RemoteStatelessSessionBeanDefinitionParser.java
new file mode 100644
index 00000000000..ba73ade9cda
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/RemoteStatelessSessionBeanDefinitionParser.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.config;
+
+import org.w3c.dom.Element;
+
+import org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean;
+
+/**
+ * {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
+ * implementation for parsing 'remote-slsb' tags and
+ * creating {@link SimpleRemoteStatelessSessionProxyFactoryBean} definitions.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+class RemoteStatelessSessionBeanDefinitionParser extends AbstractJndiLocatingBeanDefinitionParser {
+
+ protected String getBeanClassName(Element element) {
+ return "org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean";
+ }
+
+}
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/package.html b/org.springframework.context/src/main/java/org/springframework/ejb/config/package.html
new file mode 100644
index 00000000000..a70e503ecaa
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/package.html
@@ -0,0 +1,8 @@
+
+
+
+Support package for EJB/J2EE-related configuration,
+with XML schema being the primary configuration format.
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/spring-jee-2.0.xsd b/org.springframework.context/src/main/java/org/springframework/ejb/config/spring-jee-2.0.xsd
new file mode 100644
index 00000000000..016bf17a547
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/spring-jee-2.0.xsd
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/config/spring-jee-2.5.xsd b/org.springframework.context/src/main/java/org/springframework/ejb/config/spring-jee-2.5.xsd
new file mode 100644
index 00000000000..09acaf39afe
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/config/spring-jee-2.5.xsd
@@ -0,0 +1,248 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.context/src/main/java/org/springframework/ejb/interceptor/SpringBeanAutowiringInterceptor.java b/org.springframework.context/src/main/java/org/springframework/ejb/interceptor/SpringBeanAutowiringInterceptor.java
new file mode 100644
index 00000000000..993a3ffc755
--- /dev/null
+++ b/org.springframework.context/src/main/java/org/springframework/ejb/interceptor/SpringBeanAutowiringInterceptor.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2002-2008 the original author or authors.
+ *
+ * Licensed 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.springframework.ejb.interceptor;
+
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.ejb.PostActivate;
+import javax.ejb.PrePassivate;
+import javax.interceptor.InvocationContext;
+
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.access.BeanFactoryLocator;
+import org.springframework.beans.factory.access.BeanFactoryReference;
+import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
+
+/**
+ * EJB3-compliant interceptor class that injects Spring beans into
+ * fields and methods which are annotated with @Autowired.
+ * Performs injection after construction as well as after activation
+ * of a passivated bean.
+ *
+ *
To be applied through an @Interceptors annotation in
+ * the EJB Session Bean or Message-Driven Bean class, or through an
+ * interceptor-binding XML element in the EJB deployment descriptor.
+ *
+ *
Delegates to Spring's {@link AutowiredAnnotationBeanPostProcessor}
+ * underneath, allowing for customization of its specific settings through
+ * overriding the {@link #configureBeanPostProcessor} template method.
+ *
+ *
The actual BeanFactory to obtain Spring beans from is determined
+ * by the {@link #getBeanFactory} template method. The default implementation
+ * obtains the Spring {@link ContextSingletonBeanFactoryLocator}, initialized
+ * from the default resource location classpath*:beanRefContext.xml,
+ * and obtains the single ApplicationContext defined there.
+ *
+ *
NOTE: If you have more than one shared ApplicationContext definition available
+ * in your EJB class loader, you need to override the {@link #getBeanFactoryLocatorKey}
+ * method and provide a specific locator key for each autowired EJB.
+ * Alternatively, override the {@link #getBeanFactory} template method and
+ * obtain the target factory explicitly.
+ *
+ *
WARNING: Do not define the same bean as Spring-managed bean and as
+ * EJB3 session bean in the same deployment unit. In particular, be
+ * careful when using the <context:component-scan> feature
+ * in combination with the deployment of Spring-based EJB3 session beans:
+ * Make sure that the EJB3 session beans are not autodetected as
+ * Spring-managed beans as well, using appropriate package restrictions.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5.1
+ * @see org.springframework.beans.factory.annotation.Autowired
+ * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
+ * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
+ * @see #getBeanFactoryLocatorKey
+ * @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocator
+ * @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocatorKey
+ */
+public class SpringBeanAutowiringInterceptor {
+
+ /*
+ * We're keeping the BeanFactoryReference per target object in order to
+ * allow for using a shared interceptor instance on pooled target beans.
+ * This is not strictly necessary for EJB3 Session Beans and Message-Driven
+ * Beans, where interceptor instances get created per target bean instance.
+ * It simply protects against future usage of the interceptor in a shared scenario.
+ */
+ private final Map