Announcement Announcement Module
Collapse
No announcement yet.
UriEndpointMapping with Ant patterns Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • UriEndpointMapping with Ant patterns

    Hi !

    To use spring-ws with spring-integration and redirect multiple WS to one gateway I needed to match URI with an Ant pattern instead of a complete URI. I needed too to remove the context of URI.
    The AbstractMapBasedEndpointMapping class doesn't allow access to the endpointMap, so I can't herit it, nor UriEndpointMapping.
    The result is this class :

    Code:
    package org.springframework.ws.server.endpoint.mapping;
    
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import java.util.Properties;
    import java.util.regex.Pattern;
    
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContextException;
    import org.springframework.util.AntPathMatcher;
    import org.springframework.util.PathMatcher;
    import org.springframework.util.StringUtils;
    import org.springframework.ws.context.MessageContext;
    import org.springframework.ws.transport.WebServiceConnection;
    import org.springframework.ws.transport.context.TransportContext;
    import org.springframework.ws.transport.context.TransportContextHolder;
    
    public class AntUriEndpointMapping extends AbstractEndpointMapping {
    
    	private boolean lazyInitEndpoints = false;
    
    	private boolean registerBeanNames = false;
    
    	private PathMatcher pathMatcher = new AntPathMatcher();
    
    	private Pattern contextPattern = Pattern.compile("^/[^/]*");
    
    	private final Map<String, Object> endpointMap = new LinkedHashMap<String, Object>();
    
    	// holds mappings set via setEndpointMap and setMappings
    	private Map<String, Object> temporaryEndpointMap = new LinkedHashMap<String, Object>();
    
    	private boolean usePath = false;
    
    	private boolean omitContext = false;
    
    	/**
    	 * Set whether to lazily initialize endpoints. Only applicable to singleton endpoints, as prototypes are always lazily initialized. Default is
    	 * <code>false</code>, as eager initialization allows for more efficiency through referencing the controller objects directly.
    	 * <p/>
    	 * If you want to allow your endpoints to be lazily initialized, make them "lazy-init" and set this flag to <code>true</code>. Just making them
    	 * "lazy-init" will not work, as they are initialized through the references from the endpoint mapping in this case.
    	 */
    	public void setLazyInitEndpoints(boolean lazyInitEndpoints) {
    		this.lazyInitEndpoints = lazyInitEndpoints;
    	}
    
    	/**
    	 * Set whether to register bean names found in the application context. Setting this to <code>true</code> will register all beans found in the
    	 * application context under their name. Default is <code>false</code>.
    	 */
    	public final void setRegisterBeanNames(boolean registerBeanNames) {
    		this.registerBeanNames = registerBeanNames;
    	}
    
    	/**
    	 * Sets a Map with keys and endpoint beans as values. The nature of the keys in the given map depends on the exact subclass used. They can be
    	 * qualified names, for instance, or mime headers.
    	 * 
    	 * @throws IllegalArgumentException
    	 *             if the endpoint is invalid
    	 */
    	public final void setEndpointMap(Map<String, Object> endpointMap) {
    		temporaryEndpointMap.putAll(endpointMap);
    	}
    
    	/**
    	 * Maps keys to endpoint bean names. The nature of the property names depends on the exact subclass used. They can be qualified names, for
    	 * instance, or mime headers.
    	 */
    	public void setMappings(Properties mappings) {
    		for (Map.Entry<Object, Object> entry : mappings.entrySet()) {
    			if (entry.getKey() instanceof String) {
    				temporaryEndpointMap.put((String) entry.getKey(), entry.getValue());
    			}
    		}
    	}
    
    	/**
    	 * Indicates whether the path should be used instead of the full URI. Default is {@code false}.
    	 * 
    	 * @since 2.1.1
    	 */
    	public void setUsePath(boolean usePath) {
    		this.usePath = usePath;
    	}
    
    	public void setOmitContext(boolean omitContext) {
    		this.omitContext = omitContext;
    		if (omitContext) {
    			// omitContext=true implies usePath=true
    			setUsePath(true);
    		}
    	}
    
    	/** Validates the given endpoint key. Should return <code>true</code> is the given string is valid. */
    	protected boolean validateLookupKey(String key) {
    		try {
    			new URI(key);
    			return true;
    		} catch (URISyntaxException e) {
    			return false;
    		}
    	}
    
    	/**
    	 * Returns the the endpoint key for the given message context. Returns <code>null</code> if a key cannot be found.
    	 * 
    	 * @return the registration key; or <code>null</code>
    	 */
    	protected String getLookupKeyForMessage(MessageContext messageContext) throws Exception {
    		TransportContext transportContext = TransportContextHolder.getTransportContext();
    		if (transportContext != null) {
    			WebServiceConnection connection = transportContext.getConnection();
    			if (connection != null) {
    				URI connectionUri = connection.getUri();
    				if (usePath) {
    					String path = connectionUri.getPath();
    					if (omitContext) {
    						return contextPattern.matcher(path).replaceFirst("");
    					} else {
    						return path;
    					}
    				} else {
    					return connectionUri.toString();
    				}
    			}
    		}
    		return null;
    	}
    
    	/**
    	 * Lookup an endpoint for the given message. The extraction of the endpoint key is delegated to the concrete subclass.
    	 * 
    	 * @return the looked up endpoint, or <code>null</code>
    	 */
    	@Override
    	protected Object getEndpointInternal(MessageContext messageContext) throws Exception {
    		String key = getLookupKeyForMessage(messageContext);
    		if (!StringUtils.hasLength(key)) {
    			return null;
    		}
    		if (logger.isDebugEnabled()) {
    			logger.debug("Looking up endpoint for [" + key + "]");
    		}
    		return lookupEndpoint(key);
    	}
    
    	/**
    	 * Looks up an endpoint instance for the given keys. All keys are tried in order.
    	 * 
    	 * @param key
    	 *            key the beans are mapped to
    	 * @return the associated endpoint instance, or <code>null</code> if not found
    	 */
    	protected Object lookupEndpoint(String key) {
    		for (Map.Entry<String, Object> endpointEntry : endpointMap.entrySet()) {
    			if (pathMatcher.match(endpointEntry.getKey(), key)) {
    				return endpointEntry.getValue();
    			}
    		}
    		return null;
    	}
    
    	/**
    	 * Register the given endpoint instance under the registration key.
    	 * 
    	 * @param key
    	 *            the string representation of the registration key
    	 * @param endpoint
    	 *            the endpoint instance
    	 * @throws org.springframework.beans.BeansException
    	 *             if the endpoint could not be registered
    	 */
    	protected void registerEndpoint(String key, Object endpoint) throws BeansException {
    		Object mappedEndpoint = endpointMap.get(key);
    		if (mappedEndpoint != null) {
    			throw new ApplicationContextException("Cannot map endpoint [" + endpoint + "] on registration key [" + key
    					+ "]: there's already endpoint [" + mappedEndpoint + "] mapped");
    		}
    		if (!lazyInitEndpoints && endpoint instanceof String) {
    			String endpointName = (String) endpoint;
    			endpoint = resolveStringEndpoint(endpointName);
    		}
    		if (endpoint == null) {
    			throw new ApplicationContextException("Could not find endpoint for key [" + key + "]");
    		}
    		endpointMap.put(key, endpoint);
    		if (logger.isDebugEnabled()) {
    			logger.debug("Mapped key [" + key + "] onto endpoint [" + endpoint + "]");
    		}
    	}
    
    	/**
    	 * Registers annd checks the set endpoints. Checks the beans set through <code>setEndpointMap</code> and <code>setMappings</code>, and registers
    	 * the bean names found in the application context, if <code>registerBeanNames</code> is set to <code>true</code>.
    	 * 
    	 * @throws ApplicationContextException
    	 *             if either of the endpoints defined via <code>setEndpointMap</code> or <code>setMappings</code> is invalid
    	 * @see #setEndpointMap(java.util.Map)
    	 * @see #setMappings(java.util.Properties)
    	 * @see #setRegisterBeanNames(boolean)
    	 */
    	@Override
    	protected final void initApplicationContext() throws BeansException {
    		super.initApplicationContext();
    		for (String key : temporaryEndpointMap.keySet()) {
    			Object endpoint = temporaryEndpointMap.get(key);
    			if (!validateLookupKey(key)) {
    				throw new ApplicationContextException("Invalid key [" + key + "] for endpoint [" + endpoint + "]");
    			}
    			registerEndpoint(key, endpoint);
    		}
    		temporaryEndpointMap = null;
    		if (registerBeanNames) {
    			if (logger.isDebugEnabled()) {
    				logger.debug("Looking for endpoint mappings in application context: [" + getApplicationContext() + "]");
    			}
    			String[] beanNames = getApplicationContext().getBeanDefinitionNames();
    			for (String beanName : beanNames) {
    				if (validateLookupKey(beanName)) {
    					registerEndpoint(beanName, beanName);
    				}
    				String[] aliases = getApplicationContext().getAliases(beanName);
    				for (String aliase : aliases) {
    					if (validateLookupKey(aliase)) {
    						registerEndpoint(aliase, beanName);
    					}
    				}
    			}
    		}
    	}
    
    }
    I replace the HashMap's by LinkedHashMap's to ensure the order (like precised in the javadoc )

    hope it will be usefull for someone
    Gabe
Working...
X