Initial version of context module

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@118 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Arjen Poutsma 2008-10-23 10:19:23 +00:00
parent dd285fd71c
commit 0803d7d0e2
350 changed files with 40462 additions and 0 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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() + "'.");
}
}

View File

@ -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);
}

View File

@ -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();
}
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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 -&gt; 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);
}
}

View File

@ -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 &lt;context:annotation-config/&gt; 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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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">
* &lt;bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/&gt;</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">
* &lt;bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"&gt;
* &lt;property name="alwaysUseJndiLookup" value="true"/&gt;
* &lt;/bean&gt;</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;
}
}
}

View File

@ -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 &lt;context:component-scan/&gt; 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;
}
}

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Annotation support for context configuration,
including classpath scanning for autowire candidates.
</body>
</html>

View File

@ -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 &lt;context:property-.../&gt; 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);
}
}

View File

@ -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);
}
}

View File

@ -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 &lt;context:load-time-weaver/&gt; 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);
}
}

View File

@ -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 &lt;context:mbean-export/&gt; 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();
}
}

View File

@ -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 &lt;context:mbean-server/&gt; 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;
}
}
}

View File

@ -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 &lt;context:property-override/&gt; element.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
class PropertyOverrideBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return PropertyOverrideConfigurer.class;
}
}

View File

@ -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 &lt;context:property-placeholder/&gt; element.
*
* @author Juergen Hoeller
* @since 2.5
*/
class PropertyPlaceholderBeanDefinitionParser extends AbstractPropertyLoadingBeanDefinitionParser {
protected Class getBeanClass(Element element) {
return PropertyPlaceholderConfigurer.class;
}
}

View File

@ -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>&lt;context:spring-configured/&gt;</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;
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Support package for advanced application context configuration,
with XML schema being the primary configuration format.
</body>
</html>

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
});
}
}
}

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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();
}

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Abstraction for determining the current Locale,
plus global holder that exposes a thread-bound Locale.
</body>
</html>

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}
}

View File

@ -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) + "]";
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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);
}
}
}

View File

@ -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>&lt;context:load-time-weaver&gt;</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();
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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));
}
}
}
}

View File

@ -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());
}
}

View File

@ -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));
}
}
}

View File

@ -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