Initial version of context module
This commit is contained in:
parent
d151c40333
commit
edf0d8ebbe
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>An ApplicationContext provides:
|
||||||
|
* <ul>
|
||||||
|
* <li>Bean factory methods for accessing application components.
|
||||||
|
* Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
|
||||||
|
* <li>The ability to load file resources in a generic fashion.
|
||||||
|
* Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
|
||||||
|
* <li>The ability to publish events to registered listeners.
|
||||||
|
* Inherited from the {@link ApplicationEventPublisher} interface.
|
||||||
|
* <li>The ability to resolve messages, supporting internationalization.
|
||||||
|
* Inherited from the {@link MessageSource} interface.
|
||||||
|
* <li>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.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>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 <code>null</code> if there is no parent
|
||||||
|
* and this is the root of the context hierarchy.
|
||||||
|
* @return the parent context, or <code>null</code> if there is no parent
|
||||||
|
*/
|
||||||
|
ApplicationContext getParent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose AutowireCapableBeanFactory functionality for this context.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 <code>refresh()</code> has never been called)
|
||||||
|
* @see ConfigurableApplicationContext#refresh()
|
||||||
|
* @see ConfigurableApplicationContext#getBeanFactory()
|
||||||
|
*/
|
||||||
|
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>This interface can also be implemented if an object needs access to file
|
||||||
|
* resources, i.e. wants to call <code>getResource</code>, 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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
|
||||||
|
* convenience base class for application objects, implementing this interface.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>ApplicationContextException</code>
|
||||||
|
* with the specified detail message and no root cause.
|
||||||
|
* @param msg the detail message
|
||||||
|
*/
|
||||||
|
public ApplicationContextException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new <code>ApplicationContextException</code>
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>null</code>)
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
* <p>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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>java.util.EventListener</code> 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>Note: Does <i>not</i> invoke <code>close</code> on a parent context;
|
||||||
|
* parent contexts have their own, independent lifecycle.
|
||||||
|
* <p>This method can be called multiple times without side effects: Subsequent
|
||||||
|
* <code>close</code> 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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>null</code>, in which case no further resolution is possible.
|
||||||
|
*/
|
||||||
|
void setParentMessageSource(MessageSource parent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the parent of this MessageSource, or <code>null</code> if none.
|
||||||
|
*/
|
||||||
|
MessageSource getParentMessageSource();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>In the case of a container, this will return <code>true</code>
|
||||||
|
* only if <i>all</i> components that apply are currently running.
|
||||||
|
* @return whether the component is currently running
|
||||||
|
*/
|
||||||
|
boolean isRunning();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>Spring provides two out-of-the-box implementations for production:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link org.springframework.context.support.ResourceBundleMessageSource},
|
||||||
|
* built on top of the standard {@link java.util.ResourceBundle}
|
||||||
|
* <li>{@link org.springframework.context.support.ReloadableResourceBundleMessageSource},
|
||||||
|
* being able to reload message definitions without restarting the VM
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @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 <code>null</code> 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 <code>null</code> 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
|
||||||
|
* <code>MessageSourceResolvable</code> argument that was passed in.
|
||||||
|
* <p>NOTE: We must throw a <code>NoSuchMessageException</code> on this method
|
||||||
|
* since at the time of calling this method we aren't able to determine if the
|
||||||
|
* <code>defaultMessage</code> 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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}.
|
||||||
|
*
|
||||||
|
* <p>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 <code>null</code> if no default
|
||||||
|
*/
|
||||||
|
public String getDefaultMessage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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() + "'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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 <b>ResourceLoader</b> (typically the ApplicationContext) that it runs in.
|
||||||
|
* This is an alternative to a full ApplicationContext dependency via the
|
||||||
|
* ApplicationContextAware interface.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>A passed-in ResourceLoader can also be checked for the
|
||||||
|
* <b>ResourcePatternResolver</b> 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 <code>ResourcePatternUtils.getResourcePatternResolver</code> method.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>This might be a ResourcePatternResolver, which can be checked
|
||||||
|
* through <code>instanceof ResourcePatternResolver</code>. See also the
|
||||||
|
* <code>ResourcePatternUtils.getResourcePatternResolver</code> method.
|
||||||
|
* <p>Invoked after population of normal bean properties but before an init callback
|
||||||
|
* like InitializingBean's <code>afterPropertiesSet</code> or a custom init-method.
|
||||||
|
* Invoked before ApplicationContextAware's <code>setApplicationContext</code>.
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>As per BeanFactoryReference contract, <code>release</code> may be called
|
||||||
|
* more than once, with subsequent calls not doing anything. However, calling
|
||||||
|
* <code>getFactory</code> after a <code>release</code> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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 <code>java:comp/env/ejb/BeanFactoryPath</code>. The
|
||||||
|
* contents of this JNDI location must be a string containing one or more
|
||||||
|
* classpath resource names (separated by any of the delimiters '<code>,; \t\n</code>'
|
||||||
|
* 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.
|
||||||
|
* <p>Delegates to <code>createApplicationContext</code> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p><strong>Note:</strong> This class uses <strong>classpath*:beanRefContext.xml</strong>
|
||||||
|
* 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 <code>getResources</code> 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 <code>getResources</code> 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.
|
||||||
|
* <p>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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Helper infrastructure to locate and access shared application contexts.
|
||||||
|
|
||||||
|
<p><b>Note: This package is only relevant for special sharing of application
|
||||||
|
contexts, for example behind EJB facades. It is <i>not</i> used in a typical
|
||||||
|
web application or standalone application.</b>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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}.
|
||||||
|
*
|
||||||
|
* <p>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:
|
||||||
|
*
|
||||||
|
* <pre class="code">com.xyz.FooServiceImpl -> fooServiceImpl</pre>
|
||||||
|
*
|
||||||
|
* @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 <code>null</code> if none is found
|
||||||
|
*/
|
||||||
|
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
|
||||||
|
AnnotationMetadata amd = annotatedDef.getMetadata();
|
||||||
|
Set<String> types = amd.getAnnotationTypes();
|
||||||
|
String beanName = null;
|
||||||
|
for (String type : types) {
|
||||||
|
Map<String, Object> 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 <code>value()</code>.
|
||||||
|
* @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<String> metaAnnotationTypes, Map<String, Object> 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.
|
||||||
|
* <p>The default implementation simply builds a decapitalized version
|
||||||
|
* of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".
|
||||||
|
* <p>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 <code>null</code>)
|
||||||
|
*/
|
||||||
|
protected String buildDefaultBeanName(BeanDefinition definition) {
|
||||||
|
String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
|
||||||
|
return Introspector.decapitalize(shortClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<BeanDefinitionHolder> processorDefinitions =
|
||||||
|
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
|
||||||
|
|
||||||
|
// Register component for the surrounding <context:annotation-config> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>null</code>.
|
||||||
|
* @return a Set of BeanDefinitionHolders, containing all bean definitions
|
||||||
|
* that have actually been registered by this call
|
||||||
|
*/
|
||||||
|
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
|
||||||
|
BeanDefinitionRegistry registry, Object source) {
|
||||||
|
|
||||||
|
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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 <code>AnnotationScopeMetadataResolver</code> class.
|
||||||
|
* @see #AnnotationScopeMetadataResolver(ScopedProxyMode)
|
||||||
|
* @see ScopedProxyMode#NO
|
||||||
|
*/
|
||||||
|
public AnnotationScopeMetadataResolver() {
|
||||||
|
this(ScopedProxyMode.NO);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> 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<String, Object> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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).
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>This method does <i>not</i> 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<BeanDefinitionHolder> doScan(String... basePackages) {
|
||||||
|
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
|
||||||
|
for (int i = 0; i < basePackages.length; i++) {
|
||||||
|
Set<BeanDefinition> 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.
|
||||||
|
* <p>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 <code>true</code> if the bean can be registered as-is;
|
||||||
|
* <code>false</code> 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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
|
||||||
|
|
||||||
|
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* <p>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 <i>end</i> of the inclusion list.
|
||||||
|
*/
|
||||||
|
public void addIncludeFilter(TypeFilter includeFilter) {
|
||||||
|
this.includeFilters.add(includeFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an exclude type filter to the <i>front</i> 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<BeanDefinition> findCandidateComponents(String basePackage) {
|
||||||
|
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
|
||||||
|
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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>javax.annotation</code> 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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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 <code>mappedName</code> references resolved in JNDI.
|
||||||
|
* The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups
|
||||||
|
* equivalent to standard Java EE 5 resource injection for <code>name</code> references
|
||||||
|
* and default names as well. The target beans can be simple POJOs, with no special
|
||||||
|
* requirements other than the type having to match.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p>For default usage, resolving resource names as Spring bean names,
|
||||||
|
* simply define the following in your application context:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/></pre>
|
||||||
|
*
|
||||||
|
* For direct JNDI access, resolving resource names as JNDI resource references
|
||||||
|
* within the Java EE application's "java:comp/env/" namespace, use the following:
|
||||||
|
*
|
||||||
|
* <pre class="code">
|
||||||
|
* <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
|
||||||
|
* <property name="alwaysUseJndiLookup" value="true"/>
|
||||||
|
* </bean></pre>
|
||||||
|
*
|
||||||
|
* <code>mappedName</code> references will always be resolved in JNDI,
|
||||||
|
* allowing for global JNDI names (including "java:" prefix) as well. The
|
||||||
|
* "alwaysUseJndiLookup" flag just affects <code>name</code> references and
|
||||||
|
* default names (inferred from the field name / property name).
|
||||||
|
*
|
||||||
|
* <p><b>NOTE:</b> 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<String> ignoredResourceTypes = new HashSet<String>(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<Class<?>, InjectionMetadata> injectionMetadataCache =
|
||||||
|
new ConcurrentHashMap<Class<?>, 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 <code>@Resource</code>
|
||||||
|
* annotations.
|
||||||
|
* <p>By default, the <code>javax.xml.ws.WebServiceContext</code> 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".
|
||||||
|
* <p>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, <b>even for <code>name</code> attributes and default names</b>.
|
||||||
|
* <p>Default is "false": Resource names are used for Spring bean lookups in the
|
||||||
|
* containing BeanFactory; only <code>mappedName</code> attributes point directly
|
||||||
|
* into JNDI. Switch this flag to "true" for enforcing Java EE style JNDI lookups
|
||||||
|
* in any case, even for <code>name</code> 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 <code>@Resource</code> /
|
||||||
|
* <code>@WebServiceRef</code> / <code>@EJB</code> annotated fields and setter methods,
|
||||||
|
* <b>for <code>mappedName</code> attributes that point directly into JNDI</b>.
|
||||||
|
* This factory will also be used if "alwaysUseJndiLookup" is set to "true" in order
|
||||||
|
* to enforce JNDI lookups even for <code>name</code> attributes and default names.
|
||||||
|
* <p>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 <code>@Resource</code> /
|
||||||
|
* <code>@WebServiceRef</code> / <code>@EJB</code> annotated fields and setter methods,
|
||||||
|
* <b>for <code>name</code> attributes and default names</b>.
|
||||||
|
* <p>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.
|
||||||
|
* <p>Specifying Spring's {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
|
||||||
|
* leads to JNDI lookup behavior equivalent to standard Java EE 5 resource injection,
|
||||||
|
* even for <code>name</code> 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 <code>null</code>)
|
||||||
|
* @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 <code>null</code>)
|
||||||
|
* @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<String> autowiredBeanNames = null;
|
||||||
|
String name = element.name;
|
||||||
|
|
||||||
|
if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
|
||||||
|
factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
|
||||||
|
autowiredBeanNames = new LinkedHashSet<String>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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<BeanDefinitionHolder> 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<BeanDefinitionHolder> 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<BeanDefinitionHolder> 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 <component-scan> 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<Annotation>) 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>This class does <i>not</i> load the bean <code>Class</code> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>In this context, scope means the lifecycle of an instance, such as
|
||||||
|
* '<code>singleton</code>', '<code>prototype</code>', 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>The default scope is "singleton", and the default is to <i>not</i> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>definition</code>.
|
||||||
|
* <p>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 <code>definition</code>, or to use metadata present in the
|
||||||
|
* {@link BeanDefinition#attributeNames()} of the supplied <code>definition</code>.
|
||||||
|
* @param definition the target bean definition
|
||||||
|
* @return the relevant scope metadata; never <code>null</code>
|
||||||
|
*/
|
||||||
|
ScopeMetadata resolveScopeMetadata(BeanDefinition definition);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 <i>all</i> interfaces exposed by
|
||||||
|
* the class of the target object.
|
||||||
|
*/
|
||||||
|
INTERFACES,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a class-based proxy (requires CGLIB).
|
||||||
|
*/
|
||||||
|
TARGET_CLASS
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Annotation support for context configuration,
|
||||||
|
including classpath scanning for autowire candidates.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 '<code>context</code>' 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
* <code><context:spring-configured/></code> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Support package for advanced application context configuration,
|
||||||
|
with XML schema being the primary configuration format.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,423 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<xsd:schema xmlns="http://www.springframework.org/schema/context" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||||
|
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||||
|
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||||
|
targetNamespace="http://www.springframework.org/schema/context" elementFormDefault="qualified"
|
||||||
|
attributeFormDefault="unqualified">
|
||||||
|
|
||||||
|
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||||
|
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
|
||||||
|
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Defines the configuration elements for the Spring Framework's application
|
||||||
|
context support. Effects the activation of various configuration styles
|
||||||
|
for the containing Spring ApplicationContext.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
|
||||||
|
<xsd:element name="property-placeholder">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Activates replacement of ${...} placeholders, resolved against the specified properties file or Properties object
|
||||||
|
(if any). Falls back to resolving placeholders against JVM system properties.
|
||||||
|
Alternatively, define a parameterized PropertyPlaceholderConfigurer bean in the context.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:exports type="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="location" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The location of the properties file to resolve placeholders against, as a Spring
|
||||||
|
resource location: a URL, a "classpath:" pseudo URL, or a relative file path.
|
||||||
|
Multiple locations may be specified, separated by commas. If neither location nor properties-ref is
|
||||||
|
specified, placeholders will be resolved against system properties.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="properties-ref" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:java.util.Properties"><![CDATA[
|
||||||
|
The bean name of a Java Properties object that will be used for property substitution.
|
||||||
|
If neither location nor properties-ref is specified, placeholders will be resolved against system properties.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="property-override">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Activates pushing of override values into bean properties, based on configuration
|
||||||
|
lines of the following format: beanName.property=value
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:exports type="org.springframework.beans.factory.config.PropertyOverrideConfigurer"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="location" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The location of the properties file to read property overrides from, as a Spring
|
||||||
|
resource location: a URL, a "classpath:" pseudo URL, or a relative file path.
|
||||||
|
Multiple locations may be specified, separated by commas.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="properties-ref" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:java.util.Properties"><![CDATA[
|
||||||
|
The bean name of a Java Properties object that will be used for property overrides.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="annotation-config">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Activates various annotations to be detected in bean classes: Spring's @Required and
|
||||||
|
@Autowired, as well as JSR 250's @PostConstruct, @PreDestroy and @Resource (if available),
|
||||||
|
JAX-WS's @WebServiceRef (if available), EJB3's @EJB (if available), and JPA's
|
||||||
|
@PersistenceContext and @PersistenceUnit (if available). Alternatively, you may
|
||||||
|
choose to activate the individual BeanPostProcessors for those annotations.
|
||||||
|
|
||||||
|
Note: This tag does not activate processing of Spring's @Transactional or EJB3's
|
||||||
|
@TransactionAttribute annotation. Consider the use of the <tx:annotation-driven>
|
||||||
|
tag for that purpose.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="component-scan">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Scans the classpath for annotated components that will be auto-registered as
|
||||||
|
Spring beans. By default, the Spring-provided @Component, @Repository,
|
||||||
|
@Service, and @Controller stereotypes will be detected.
|
||||||
|
|
||||||
|
Note: This tag implies the effects of the 'annotation-config' tag, activating @Required,
|
||||||
|
@Autowired, @PostConstruct, @PreDestroy, @Resource, @PersistenceContext and @PersistenceUnit
|
||||||
|
annotations in the component classes, which is usually desired for autodetected components
|
||||||
|
(without external configuration). Turn off the 'annotation-config' attribute to deactivate
|
||||||
|
this default behavior, for example in order to use custom BeanPostProcessor definitions
|
||||||
|
for handling those annotations.
|
||||||
|
|
||||||
|
Note: You may use placeholders in package paths, but only resolved against system
|
||||||
|
properties (analogous to resource paths). A component scan results in new bean definition
|
||||||
|
being registered; Spring's PropertyPlaceholderConfigurer will apply to those bean
|
||||||
|
definitions just like to regular bean definitions, but it won't apply to the component
|
||||||
|
scan settings themselves.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="include-filter" type="filterType" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Controls which eligible types to include for component scanning.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="exclude-filter" type="filterType" minOccurs="0" maxOccurs="unbounded">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Controls which eligible types to exclude for component scanning.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="base-package" type="xsd:string" use="required">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The comma-separated list of packages to scan for annotated components.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="resource-pattern" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Controls the class files eligible for component detection. Defaults to "**/*.class", the recommended value.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="use-default-filters" type="xsd:boolean" default="true">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Indicates whether automatic detection of classes annotated with @Component, @Repository, @Service, or @Controller
|
||||||
|
should be enabled. Default is "true".
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="annotation-config" type="xsd:boolean" default="true">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Indicates whether the implicit AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor should
|
||||||
|
be enabled. Default is "true".
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="name-generator" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The fully-qualified classname of the BeanNameGenerator to be used for naming detected components.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:expected-type type="java.lang.Class"/>
|
||||||
|
<tool:assignable-to type="org.springframework.beans.factory.support.BeanNameGenerator"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="scope-resolver" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The fully-qualified class name of the ScopeMetadataResolver to be used for resolving the scope of detected components.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:expected-type type="java.lang.Class"/>
|
||||||
|
<tool:assignable-to type="org.springframework.context.annotation.ScopeMetadataResolver"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="scoped-proxy">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Indicates whether proxies should be generated for detected components, which may be necessary when using certain
|
||||||
|
non-singleton scopes in a proxy-style fashion. Default is to generate no such proxies.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:simpleType>
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="no"/>
|
||||||
|
<xsd:enumeration value="interfaces"/>
|
||||||
|
<xsd:enumeration value="targetClass"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="load-time-weaver">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Activates a Spring LoadTimeWeaver for this application context, available as
|
||||||
|
a bean with the name "loadTimeWeaver". Any bean that implements the
|
||||||
|
LoadTimeWeaverAware interface will then receive the LoadTimeWeaver reference
|
||||||
|
automatically; for example, Spring's JPA bootstrap support.
|
||||||
|
|
||||||
|
The default weaver is determined automatically. As of Spring 2.5: detecting
|
||||||
|
Sun's GlassFish, Oracle's OC4J, Spring's VM agent and any ClassLoader
|
||||||
|
supported by Spring's ReflectiveLoadTimeWeaver (for example, the
|
||||||
|
TomcatInstrumentableClassLoader).
|
||||||
|
|
||||||
|
The activation of AspectJ load-time weaving is specified via a simple flag
|
||||||
|
(the 'aspectj-weaving' attribute), with the AspectJ class transformer
|
||||||
|
registered through Spring's LoadTimeWeaver. AspectJ weaving will be activated
|
||||||
|
by default if a "META-INF/aop.xml" resource is present in the classpath.
|
||||||
|
|
||||||
|
This also activates the current application context for applying dependency
|
||||||
|
injection to non-managed classes that are instantiated outside of the Spring
|
||||||
|
bean factory (typically classes annotated with the @Configurable annotation).
|
||||||
|
This will only happen if the AnnotationBeanConfigurerAspect is on the classpath
|
||||||
|
(i.e. spring-aspects.jar), effectively activating "spring-configured" by default.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:exports type="org.springframework.instrument.classloading.LoadTimeWeaver"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="weaver-class" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The fully-qualified classname of the LoadTimeWeaver that is to be activated.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:expected-type type="java.lang.Class"/>
|
||||||
|
<tool:assignable-to type="org.springframework.instrument.classloading.LoadTimeWeaver"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="aspectj-weaving" default="autodetect">
|
||||||
|
<xsd:simpleType>
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="on">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Switches Spring-based AspectJ load-time weaving on.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:enumeration>
|
||||||
|
<xsd:enumeration value="off">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Switches Spring-based AspectJ load-time weaving off.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:enumeration>
|
||||||
|
<xsd:enumeration value="autodetect">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Switches AspectJ load-time weaving on if a "META-INF/aop.xml" resource
|
||||||
|
is present in the classpath. If there is no such resource, then AspectJ
|
||||||
|
load-time weaving will be switched off.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:enumeration>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="spring-configured">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">
|
||||||
|
<![CDATA[
|
||||||
|
Signals the current application context to apply dependency injection
|
||||||
|
to non-managed classes that are instantiated outside of the Spring bean
|
||||||
|
factory (typically classes annotated with the @Configurable annotation).
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:simpleType>
|
||||||
|
<xsd:restriction base="xsd:string"/>
|
||||||
|
</xsd:simpleType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="mbean-export">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:org.springframework.jmx.export.annotation.AnnotationMBeanExporter"><![CDATA[
|
||||||
|
Activates default exporting of MBeans by detecting standard MBeans in the Spring
|
||||||
|
context as well as @ManagedResource annotations on Spring-defined beans.
|
||||||
|
|
||||||
|
The resulting MBeanExporter bean is defined under the name "mbeanExporter".
|
||||||
|
Alternatively, consider defining a custom AnnotationMBeanExporter bean explicitly.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:exports type="org.springframework.jmx.export.annotation.MBeanExporter"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="default-domain" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The default domain to use when generating JMX ObjectNames.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="server" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The bean name of the MBeanServer to which MBeans should be exported.
|
||||||
|
Default is to use the platform's default MBeanServer (autodetecting
|
||||||
|
WebLogic 9+, WebSphere 5.1+ and the JDK 1.5+ platform MBeanServer).
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="registration">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The registration behavior, indicating how to deal with existing MBeans
|
||||||
|
of the same name: fail with an exception, ignore and keep the existing
|
||||||
|
MBean, or replace the existing one with the new MBean.
|
||||||
|
|
||||||
|
Default is to fail with an exception.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:simpleType>
|
||||||
|
<xsd:restriction base="xsd:NMTOKEN">
|
||||||
|
<xsd:enumeration value="failOnExisting"/>
|
||||||
|
<xsd:enumeration value="ignoreExisting"/>
|
||||||
|
<xsd:enumeration value="replaceExisting"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:element name="mbean-server">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation source="java:org.springframework.jmx.support.MBeanServerFactoryBean"><![CDATA[
|
||||||
|
Exposes a default MBeanServer for the current platform.
|
||||||
|
Autodetects WebLogic 9+, WebSphere 5.1+ and the JDK 1.5+ platform MBeanServer.
|
||||||
|
|
||||||
|
The default bean name for the exposed MBeanServer is "mbeanServer".
|
||||||
|
This may be customized through specifying the "id" attribute.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
<xsd:appinfo>
|
||||||
|
<tool:annotation>
|
||||||
|
<tool:exports type="javax.management.MBeanServer"/>
|
||||||
|
</tool:annotation>
|
||||||
|
</xsd:appinfo>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:complexContent>
|
||||||
|
<xsd:extension base="beans:identifiedType">
|
||||||
|
<xsd:attribute name="agent-id" type="xsd:string">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
The agent id of the target MBeanServer, if any.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:extension>
|
||||||
|
</xsd:complexContent>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
|
||||||
|
<xsd:complexType name="filterType">
|
||||||
|
<xsd:attribute name="type" use="required">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Controls the type of filtering to apply to the expression.
|
||||||
|
|
||||||
|
"annotation" indicates an annotation to be present at the type level in target components;
|
||||||
|
"assignable" indicates a class (or interface) that the target components are assignable to (extend/implement);
|
||||||
|
"aspectj" indicates an AspectJ type expression to be matched by the target components;
|
||||||
|
"regex" indicates a regex expression to be matched by the target components' class names;
|
||||||
|
"custom" indicates a custom implementation of the org.springframework.core.type.TypeFilter interface.
|
||||||
|
|
||||||
|
Note: This attribute will not be inherited by child bean definitions.
|
||||||
|
Hence, it needs to be specified per concrete bean definition.
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:simpleType>
|
||||||
|
<xsd:restriction base="xsd:string">
|
||||||
|
<xsd:enumeration value="annotation"/>
|
||||||
|
<xsd:enumeration value="assignable"/>
|
||||||
|
<xsd:enumeration value="aspectj"/>
|
||||||
|
<xsd:enumeration value="regex"/>
|
||||||
|
<xsd:enumeration value="custom"/>
|
||||||
|
</xsd:restriction>
|
||||||
|
</xsd:simpleType>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="expression" type="xsd:string" use="required">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation><![CDATA[
|
||||||
|
Indicates the filter expression, the type of which is indicated by "type".
|
||||||
|
]]></xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
|
||||||
|
</xsd:schema>
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>ApplicationContext</code>.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.5
|
||||||
|
*/
|
||||||
|
public abstract class ApplicationContextEvent extends ApplicationEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ContextStartedEvent.
|
||||||
|
* @param source the <code>ApplicationContext</code> that the event is raised for
|
||||||
|
* (must not be <code>null</code>)
|
||||||
|
*/
|
||||||
|
public ApplicationContextEvent(ApplicationContext source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the <code>ApplicationContext</code> that the event was raised for.
|
||||||
|
*/
|
||||||
|
public final ApplicationContext getApplicationContext() {
|
||||||
|
return (ApplicationContext) getSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>ApplicationContext</code> gets closed.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 12.08.2003
|
||||||
|
* @see ContextRefreshedEvent
|
||||||
|
*/
|
||||||
|
public class ContextClosedEvent extends ApplicationContextEvent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ContextClosedEvent.
|
||||||
|
* @param source the <code>ApplicationContext</code> that has been closed
|
||||||
|
* (must not be <code>null</code>)
|
||||||
|
*/
|
||||||
|
public ContextClosedEvent(ApplicationContext source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>ApplicationContext</code> 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 <code>ApplicationContext</code> that has been initialized
|
||||||
|
* or refreshed (must not be <code>null</code>)
|
||||||
|
*/
|
||||||
|
public ContextRefreshedEvent(ApplicationContext source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>ApplicationContext</code> 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 <code>ApplicationContext</code> that has been started
|
||||||
|
* (must not be <code>null</code>)
|
||||||
|
*/
|
||||||
|
public ContextStartedEvent(ApplicationContext source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>ApplicationContext</code> 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 <code>ApplicationContext</code> that has been stopped
|
||||||
|
* (must not be <code>null</code>)
|
||||||
|
*/
|
||||||
|
public ContextStoppedEvent(ApplicationContext source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
* <code>ApplicationEvent</code> to all <code>ApplicationListeners</code>
|
||||||
|
* registered with an <code>ApplicationEventPublisher</code> after each
|
||||||
|
* <i>successful</i> method invocation.
|
||||||
|
*
|
||||||
|
* <p>Note that this interceptor is only capable of publishing <i>stateless</i>
|
||||||
|
* 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.
|
||||||
|
* <p>The event class <b>must</b> have a constructor with a single
|
||||||
|
* <code>Object</code> argument for the event source. The interceptor
|
||||||
|
* will pass in the invoked object.
|
||||||
|
* @throws IllegalArgumentException if the supplied <code>Class</code> is
|
||||||
|
* <code>null</code> or if it is not an <code>ApplicationEvent</code> subclass or
|
||||||
|
* if it does not expose a constructor that takes a single <code>Object</code> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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 <code>instanceof</code>
|
||||||
|
* checks on the passed-in event object.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>Default is a SyncTaskExecutor, executing the listeners synchronously
|
||||||
|
* in the calling thread.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Support classes for application events, like standard context events.
|
||||||
|
To be supported by all major application context implementations.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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,
|
||||||
|
* <i>not</i> exposing it as inheritable for child threads.
|
||||||
|
* @param localeContext the current LocaleContext, or <code>null</code> 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 <code>null</code> 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 <code>null</code> 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.
|
||||||
|
* <p>Will implicitly create a LocaleContext for the given Locale,
|
||||||
|
* <i>not</i> exposing it as inheritable for child threads.
|
||||||
|
* @param locale the current Locale, or <code>null</code> 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.
|
||||||
|
* <p>Will implicitly create a LocaleContext for the given Locale.
|
||||||
|
* @param locale the current Locale, or <code>null</code> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>Locale</code>.
|
||||||
|
*
|
||||||
|
* @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 <code>getLocale()</code> 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Abstraction for determining the current Locale,
|
||||||
|
plus global holder that exposes a thread-bound Locale.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<p>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.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> 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".
|
||||||
|
*
|
||||||
|
* <p>Supports not only MessageSourceResolvables as primary messages
|
||||||
|
* but also resolution of message arguments that are in turn
|
||||||
|
* MessageSourceResolvables themselves.
|
||||||
|
*
|
||||||
|
* <p>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".
|
||||||
|
* <p>Note: In case of a MessageSourceResolvable with multiple codes
|
||||||
|
* (like a FieldError) and a MessageSource that has a parent MessageSource,
|
||||||
|
* do <i>not</i> activate "useCodeAsDefaultMessage" in the <i>parent</i>:
|
||||||
|
* Else, you'll get the first code returned as message by the parent,
|
||||||
|
* without attempts to check further codes.
|
||||||
|
* <p>To be able to work with "useCodeAsDefaultMessage" turned on in the parent,
|
||||||
|
* AbstractMessageSource and AbstractApplicationContext contain special checks
|
||||||
|
* to delegate to the internal <code>getMessageInternal</code> 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".
|
||||||
|
* <p>Alternatively, consider overriding the <code>getDefaultMessage</code>
|
||||||
|
* 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 <code>null</code> if not found. Does <i>not</i> fall back to
|
||||||
|
* the code as default message. Invoked by <code>getMessage</code> 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 <code>null</code> 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 <code>null</code> 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.
|
||||||
|
* <p>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
|
||||||
|
* <code>getMessage</code>.
|
||||||
|
* @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 <code>null</code> 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.
|
||||||
|
* <p>The default implementation passes the String to <code>formatMessage</code>,
|
||||||
|
* 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 <code>null</code> 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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>The default implementation <i>does</i> use MessageFormat,
|
||||||
|
* through delegating to the <code>resolveCode</code> method.
|
||||||
|
* Subclasses are encouraged to replace this with optimized resolution.
|
||||||
|
* <p>Unfortunately, <code>java.text.MessageFormat</code> 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 <code>null</code> 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.
|
||||||
|
* <p>Returns a MessageFormat instance rather than a message String,
|
||||||
|
* to allow for appropriate caching of MessageFormats in subclasses.
|
||||||
|
* <p><b>Subclasses are encouraged to provide optimized resolution
|
||||||
|
* for messages without arguments, not involving MessageFormat.</b>
|
||||||
|
* See <code>resolveCodeWithoutArguments</code> 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 <code>null</code> if not found
|
||||||
|
* @see #resolveCodeWithoutArguments(String, java.util.Locale)
|
||||||
|
*/
|
||||||
|
protected abstract MessageFormat resolveCode(String code, Locale locale);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p><b>Note that there is a similar base class for WebApplicationContexts.</b>
|
||||||
|
* {@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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>The default implementation returns <code>null</code>. Subclasses can override
|
||||||
|
* this to provide a set of resource locations to load bean definitions from.
|
||||||
|
* @return an array of resource locations, or <code>null</code> 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.
|
||||||
|
* <p>The default implementation returns <code>null</code>,
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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}.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>The default implementation returns <code>null</code>. Subclasses can override
|
||||||
|
* this to provide pre-built Resource objects rather than location Strings.
|
||||||
|
* @return an array of Resource objects, or <code>null</code> if none
|
||||||
|
* @see #getConfigLocations()
|
||||||
|
*/
|
||||||
|
protected Resource[] getConfigResources() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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
|
||||||
|
* <code>setApplicationContext</code> 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 <code>setApplicationContext</code> after setting the context instance.
|
||||||
|
* <p>Note: Does </i>not</i> get called on reinitialization of the context
|
||||||
|
* but rather just on first initialization of this object's context reference.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p><b>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.</b>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>loadClass</code> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>resolvableToString()</code>.
|
||||||
|
* @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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p><b>NOTE:</b> 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.)
|
||||||
|
* <b>Use an explicit "file:" prefix to enforce an absolute file path.</b>
|
||||||
|
*
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p><b>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.</b>
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>Usage example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* GenericApplicationContext ctx = new GenericApplicationContext();
|
||||||
|
* XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
|
||||||
|
* xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
|
||||||
|
* PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
|
||||||
|
* propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
|
||||||
|
* ctx.refresh();
|
||||||
|
*
|
||||||
|
* MyBean myBean = (MyBean) ctx.getBean("myBean");
|
||||||
|
* ...</pre>
|
||||||
|
*
|
||||||
|
* 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}.
|
||||||
|
*
|
||||||
|
* <p>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 <code>getResource</code> calls to the given ResourceLoader.
|
||||||
|
* If not set, default resource loading will apply.
|
||||||
|
* <p>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.
|
||||||
|
* <p>You can also pass in a full ResourcePatternResolver, which will
|
||||||
|
* be autodetected by the context and used for <code>getResources</code>
|
||||||
|
* 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.
|
||||||
|
* <p><b>NOTE:</b> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 <code>null</code> 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 <code>null</code> 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 <code>null</code> 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 <code>null</code> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>null</code> 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 <code>null</code>, 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 <code>ResourceBundle.getLocale()</code> method.
|
||||||
|
*/
|
||||||
|
public Locale getLocale() {
|
||||||
|
return this.locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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}.
|
||||||
|
*
|
||||||
|
* <p>{@link AbstractMessageSource} derives from this class, providing concrete
|
||||||
|
* <code>getMessage</code> 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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 <code>null</code> 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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>The default implementation passes the String to <code>formatMessage</code>,
|
||||||
|
* 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 <code>null</code> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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 <i>not</i> be reflected in the application.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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 <i>do not</i> make a significant difference.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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+.
|
||||||
|
* <p>The associated resource bundles will be checked sequentially
|
||||||
|
* when resolving a message code. Note that message definitions in a
|
||||||
|
* <i>previous</i> 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.
|
||||||
|
* <p>Default is none, using the <code>java.util.Properties</code>
|
||||||
|
* default encoding.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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").
|
||||||
|
* <p>Falling back to the system Locale is the default behavior of
|
||||||
|
* <code>java.util.ResourceBundle</code>. 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.
|
||||||
|
* <ul>
|
||||||
|
* <li>Default is "-1", indicating to cache forever (just like
|
||||||
|
* <code>java.util.ResourceBundle</code>).
|
||||||
|
* <li>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.
|
||||||
|
* <li>A value of "0" will check the last-modified timestamp of the file on
|
||||||
|
* every message access. <b>Do not use this in a production environment!</b>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public void setCacheSeconds(int cacheSeconds) {
|
||||||
|
this.cacheMillis = (cacheSeconds * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the PropertiesPersister to use for parsing properties files.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 <code>null</code> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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}.
|
||||||
|
*
|
||||||
|
* <p>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 <code>java.util.ResourceBundle</code> class.
|
||||||
|
*
|
||||||
|
* <p>Unfortunately, <code>java.util.ResourceBundle</code> caches loaded bundles
|
||||||
|
* forever: Reloading a bundle during VM execution is <i>not</i> 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 <code>org.mypackage</code>), it will be resolved
|
||||||
|
* from the classpath root.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 <code>java.util.ResourceBundle</code> 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 <code>org.mypackage</code>),
|
||||||
|
* it will be resolved from the classpath root.
|
||||||
|
* <p>The associated resource bundles will be checked sequentially
|
||||||
|
* when resolving a message code. Note that message definitions in a
|
||||||
|
* <i>previous</i> resource bundle will override ones in a later bundle,
|
||||||
|
* due to the sequential lookup.
|
||||||
|
* <p>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 <code>java.util.ResourceBundle</code> 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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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 <code>null</code> 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 <code>null</code> 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) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>Extends PropertiesFactoryBean to inherit the capability of defining
|
||||||
|
* local properties and loading from properties files.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Classes supporting the org.springframework.context package,
|
||||||
|
such as abstract base classes for ApplicationContext
|
||||||
|
implementations and a MessageSource implementation.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>LoadTimeWeaver</code>.
|
||||||
|
*
|
||||||
|
* <p>Typically registered for the default bean name
|
||||||
|
* "<code>loadTimeWeaver</code>"; the most convenient way to achieve this is
|
||||||
|
* Spring's <code><context:load-time-weaver></code> XML tag.
|
||||||
|
*
|
||||||
|
* <p>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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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}.
|
||||||
|
* <p>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(..)}.
|
||||||
|
* <p><b>NOTE:</b> This method will only be called if there actually is a
|
||||||
|
* <code>LoadTimeWeaver</code> 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 <code>LoadTimeWeaver</code> instance (never <code>null</code>)
|
||||||
|
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
|
||||||
|
* @see org.springframework.context.ApplicationContextAware#setApplicationContext
|
||||||
|
*/
|
||||||
|
void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>{@link org.springframework.context.ApplicationContext Application contexts}
|
||||||
|
* will automatically register this with their underlying
|
||||||
|
* {@link BeanFactory bean factory}, provided that a default
|
||||||
|
* <code>LoadTimeWeaver</code> is actually available.
|
||||||
|
*
|
||||||
|
* <p>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 <code>LoadTimeWeaverAwareProcessor</code> 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 <code>LoadTimeWeaverAwareProcessor</code> for the given
|
||||||
|
* {@link LoadTimeWeaver}.
|
||||||
|
* <p>If the given <code>loadTimeWeaver</code> is <code>null</code>, then a
|
||||||
|
* <code>LoadTimeWeaver</code> 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 <code>LoadTimeWeaver</code> that is to be used; can be <code>null</code>
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Load-time weaving support for a Spring application context, building on Spring's
|
||||||
|
{@link org.springframework.instrument.classloading.LoadTimeWeaver} abstraction.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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 <code>create()</code> call that returns the actual
|
||||||
|
* SLSB proxy.
|
||||||
|
* <p>Default is none, which will work on all J2EE servers that are not based
|
||||||
|
* on CORBA. A plain <code>javax.ejb.EJBHome</code> 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".
|
||||||
|
* <p>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 <code>doInvoke</code>.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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".
|
||||||
|
* <p>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".
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>Default implementation returns the home created on initialization,
|
||||||
|
* if any; else, it invokes lookup to get a new proxy for each invocation.
|
||||||
|
* <p>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 <code>create()</code> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>See {@link org.springframework.jndi.JndiObjectLocator} for info on
|
||||||
|
* how to specify the JNDI location of the target EJB.
|
||||||
|
*
|
||||||
|
* <p>If you want control over interceptor chaining, use an AOP ProxyFactoryBean
|
||||||
|
* with LocalSlsbInvokerInterceptor rather than rely on this class.
|
||||||
|
*
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>"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.
|
||||||
|
*
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
* <p>See {@link org.springframework.jndi.JndiObjectLocator} for info on
|
||||||
|
* how to specify the JNDI location of the target EJB.
|
||||||
|
*
|
||||||
|
* <p>If you want control over interceptor chaining, use an AOP ProxyFactoryBean
|
||||||
|
* with SimpleRemoteSlsbInvokerInterceptor rather than rely on this class.
|
||||||
|
*
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
* <p>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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
<p>The motivation for the classes in this package are discussed in Chapter 11 of
|
||||||
|
<a href="http://www.amazon.com/exec/obidos/tg/detail/-/0764543857/">Expert One-On-One J2EE Design and Development</a>
|
||||||
|
by Rod Johnson (Wrox, 2002).
|
||||||
|
|
||||||
|
<p>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
|
||||||
|
<i>Expert One-on-One J2EE</i>.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 '<code>jee</code>' 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 <code>jndi-lookup</code> 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("<jndi-lookup> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 '<code>local-slsb</code>' 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue