/*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.server.endpoint.mapping;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.core.Ordered;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.EndpointInvocationChain;
import org.springframework.ws.server.EndpointMapping;
import org.springframework.ws.server.SmartEndpointInterceptor;
/**
* Abstract base class for EndpointMapping implementations. Supports a default endpoint, and endpoint interceptors.
*
* @author Arjen Poutsma
* @see #getEndpointInternal(org.springframework.ws.context.MessageContext)
* @see org.springframework.ws.server.EndpointInterceptor
* @since 1.0.0
*/
public abstract class AbstractEndpointMapping extends ApplicationObjectSupport implements EndpointMapping, Ordered {
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
private Object defaultEndpoint;
private EndpointInterceptor[] interceptors;
private SmartEndpointInterceptor[] smartInterceptors;
/**
* Returns the endpoint interceptors to apply to all endpoints mapped by this endpoint mapping.
*
* @return array of endpoint interceptors, or {@code null} if none
*/
public EndpointInterceptor[] getInterceptors() {
return interceptors;
}
/**
* Sets the endpoint interceptors to apply to all endpoints mapped by this endpoint mapping.
*
* @param interceptors array of endpoint interceptors, or {@code null} if none
*/
public final void setInterceptors(EndpointInterceptor[] interceptors) {
this.interceptors = interceptors;
}
@Override
public final int getOrder() {
return order;
}
/**
* Specify the order value for this mapping.
*
* <p>Default value is {@link Integer#MAX_VALUE}, meaning that it's non-ordered.
*
* @see org.springframework.core.Ordered#getOrder()
*/
public final void setOrder(int order) {
this.order = order;
}
/**
* Initializes the interceptors.
*
* @see #initInterceptors()
*/
@Override
protected void initApplicationContext() throws BeansException {
initInterceptors();
}
/**
* Initialize the specified interceptors, adapting them where necessary.
*
* @see #setInterceptors
*/
protected void initInterceptors() {
Map<String, SmartEndpointInterceptor> smartInterceptors = BeanFactoryUtils
.beansOfTypeIncludingAncestors(getApplicationContext(), SmartEndpointInterceptor.class, true, false);
if (!smartInterceptors.isEmpty()) {
this.smartInterceptors =
smartInterceptors.values().toArray(new SmartEndpointInterceptor[smartInterceptors.size()]);
}
}
/**
* Look up an endpoint for the given message context, falling back to the default endpoint if no specific one is
* found.
*
* @return the looked up endpoint instance, or the default endpoint
* @see #getEndpointInternal(org.springframework.ws.context.MessageContext)
*/
@Override
public final EndpointInvocationChain getEndpoint(MessageContext messageContext) throws Exception {
Object endpoint = getEndpointInternal(messageContext);
if (endpoint == null) {
endpoint = defaultEndpoint;
}
if (endpoint == null) {
return null;
}
if (endpoint instanceof String) {
String endpointName = (String) endpoint;
endpoint = resolveStringEndpoint(endpointName);
if (endpoint == null) {
return null;
}
}
List<EndpointInterceptor> interceptors = new ArrayList<EndpointInterceptor>();
if (this.interceptors != null) {
interceptors.addAll(Arrays.asList(this.interceptors));
}
if (this.smartInterceptors != null) {
for (SmartEndpointInterceptor smartInterceptor : smartInterceptors) {
if (smartInterceptor.shouldIntercept(messageContext, endpoint)) {
interceptors.add(smartInterceptor);
}
}
}
return createEndpointInvocationChain(messageContext, endpoint,
interceptors.toArray(new EndpointInterceptor[interceptors.size()]));
}
/**
* Creates a new {@code EndpointInvocationChain} based on the given message context, endpoint, and
* interceptors. Default implementation creates a simple {@code EndpointInvocationChain} based on the set
* interceptors.
*
* @param endpoint the endpoint
* @param interceptors the endpoint interceptors
* @return the created invocation chain
* @see #setInterceptors(org.springframework.ws.server.EndpointInterceptor[])
*/
protected EndpointInvocationChain createEndpointInvocationChain(MessageContext messageContext,
Object endpoint,
EndpointInterceptor[] interceptors) {
return new EndpointInvocationChain(endpoint, interceptors);
}
/**
* Returns the default endpoint for this endpoint mapping.
*
* @return the default endpoint mapping, or null if none
*/
protected final Object getDefaultEndpoint() {
return defaultEndpoint;
}
/**
* Sets the default endpoint for this endpoint mapping. This endpoint will be returned if no specific mapping was
* found.
*
* <p>Default is {@code null}, indicating no default endpoint.
*
* @param defaultEndpoint the default endpoint, or null if none
*/
public final void setDefaultEndpoint(Object defaultEndpoint) {
this.defaultEndpoint = defaultEndpoint;
}
/**
* Resolves an endpoint string. If the given string can is a bean name, it is resolved using the application
* context.
*
* @param endpointName the endpoint name
* @return the resolved endpoint, or {@code null} if the name could not be resolved
*/
protected Object resolveStringEndpoint(String endpointName) {
if (getApplicationContext().containsBean(endpointName)) {
return getApplicationContext().getBean(endpointName);
}
else {
return null;
}
}
/**
* Lookup an endpoint for the given request, returning {@code null} if no specific one is found. This template
* method is called by getEndpoint, a {@code null} return value will lead to the default handler, if one is
* set.
*
* <p>The returned endpoint can be a string, in which case it is resolved as a bean name. Also, it can take the form
* {@code beanName#method}, in which case the method is resolved.
*
* @return the looked up endpoint instance, or null
* @throws Exception if there is an error
*/
protected abstract Object getEndpointInternal(MessageContext messageContext) throws Exception;
}