/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.synapse.config.xml.endpoints;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.util.UIDGenerator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.synapse.SynapseConstants;
import org.apache.synapse.SynapseException;
import org.apache.synapse.config.XMLToObjectMapper;
import org.apache.synapse.config.xml.XMLConfigConstants;
import org.apache.synapse.config.xml.MediatorPropertyFactory;
import org.apache.synapse.endpoints.Endpoint;
import org.apache.synapse.endpoints.IndirectEndpoint;
import org.apache.synapse.endpoints.EndpointDefinition;
import org.apache.synapse.PropertyInclude;
import org.apache.synapse.mediators.MediatorProperty;
import org.apache.synapse.endpoints.AbstractEndpoint;
import javax.xml.namespace.QName;
import java.util.*;
/**
* All endpoint factories should extend from this abstract class. Use EndpointFactory to obtain the
* correct endpoint for particular endpoint configuration. As endpoints can be nested inside
* each other, EndpointFactory implementations may call other EndpointFactory implementations
* recursively to obtain the required endpoint hierarchy.
* <p/>
* This also serves as the {@link XMLToObjectMapper} implementation for specific endpoint
* implementations. If the endpoint type is not known use {@link XMLToEndpointMapper} as the
* generic {@link XMLToObjectMapper} for all endpoints.
*/
public abstract class EndpointFactory implements XMLToObjectMapper {
static Log log;
private DefinitionFactory customDefnFactory = null;
protected EndpointFactory() {
log = LogFactory.getLog(this.getClass());
}
private static final String ENDPOINT_NAME_PREFIX = "endpoint_";
public static final QName ON_FAULT_Q = new QName(XMLConfigConstants.NULL_NAMESPACE, "onError");
private static final QName DESCRIPTION_Q
= new QName(SynapseConstants.SYNAPSE_NAMESPACE, "description");
/**
* Core method which is exposed for the external use, and this will find the proper
* {@link EndpointFactory} and create the endpoint which is of the format {@link Endpoint}.
*
* @param elem XML from which the endpoint will be built
* @param isAnonymous whether this is an anonymous endpoint or not
* @param properties bag of properties to pass in any information to the factory
* @return created endpoint
*/
public static Endpoint getEndpointFromElement(OMElement elem, boolean isAnonymous,
Properties properties) {
return getEndpointFactory(elem).createEndpointWithName(elem, isAnonymous, properties);
}
/**
* Core method which is exposed for the external use, and this will find the proper
* {@link EndpointFactory} and create the endpoint which is of the format {@link Endpoint}.However
* defintion for this endpoint will be built using a custom Endpoint Defn factory.
*
* @param elem XML from which the endpoint will be built
* @param factory custom definition factory which this endpoint will be used to build
* @param isAnonymous whether this is an anonymous endpoint or not
* @param properties bag of properties to pass in any information to the factory
* @return created endpoint
*/
public static Endpoint getEndpointFromElement(OMElement elem,DefinitionFactory factory,
boolean isAnonymous,
Properties properties) {
EndpointFactory fac = getEndpointFactory(elem);
fac.setEndpointDefinitionFactory(factory);
return fac.createEndpointWithName(elem, isAnonymous, properties);
}
/**
* Creates the {@link Endpoint} object from the provided {@link OMNode}
*
* @param om XML node from which the endpoint will be built
* @param properties bag of properties to pass in any information to the factory
* @return created endpoint as an {@link Object}
*/
public Object getObjectFromOMNode(OMNode om, Properties properties) {
if (om instanceof OMElement) {
return createEndpointWithName((OMElement) om, false, properties);
} else {
handleException("Invalid XML configuration for an Endpoint. OMElement expected");
}
return null;
}
/**
* Creates the Endpoint implementation for the given XML endpoint configuration. If the endpoint
* configuration is an inline one, it should be an anonymous endpoint. If it is defined as an
* immediate child element of the definitions tag it should have a name, which is used as the
* key in local registry.
*
* @param epConfig OMElement containing the endpoint configuration.
* @param anonymousEndpoint false if the endpoint has a name. true otherwise.
* @param properties bag of properties to pass in any information to the factory
* @return Endpoint implementation for the given configuration.
*/
protected abstract Endpoint createEndpoint(OMElement epConfig, boolean anonymousEndpoint,
Properties properties);
/**
* Make sure that the endpoints created by the factory has a name
*
* @param epConfig OMElement containing the endpoint configuration.
* @param anonymousEndpoint false if the endpoint has a name. true otherwise.
* @param properties bag of properties to pass in any information to the factory
* @return Endpoint implementation for the given configuration.
*/
private Endpoint createEndpointWithName(OMElement epConfig, boolean anonymousEndpoint,
Properties properties) {
Endpoint ep = createEndpoint(epConfig, anonymousEndpoint, properties);
OMElement descriptionElem = epConfig.getFirstChildWithName(DESCRIPTION_Q);
if (descriptionElem != null) {
ep.setDescription(descriptionElem.getText());
}
// if the endpoint doesn't have a name we will generate a unique name.
if (anonymousEndpoint && ep.getName() == null) {
String uuid = UIDGenerator.generateUID();
uuid = uuid.replace(':', '_');
ep.setName(ENDPOINT_NAME_PREFIX + uuid);
if (ep instanceof AbstractEndpoint) {
((AbstractEndpoint) ep).setAnonymous(true);
}
}
OMAttribute onFaultAtt = epConfig.getAttribute(ON_FAULT_Q);
if (onFaultAtt != null) {
ep.setErrorHandler(onFaultAtt.getAttributeValue());
}
return ep;
}
protected void extractSpecificEndpointProperties(EndpointDefinition definition,
OMElement elem) {
// overridden by the Factories which has specific building
}
/**
* Returns the EndpointFactory implementation for given endpoint configuration. Throws a
* SynapseException, if there is no EndpointFactory for given configuration.
*
* @param configElement Endpoint configuration.
* @return EndpointFactory implementation.
*/
private static EndpointFactory getEndpointFactory(OMElement configElement) {
if (configElement.getAttribute(new QName("key")) != null) {
return IndirectEndpointFactory.getInstance();
}
if (configElement.getAttribute(new QName("key-expression")) != null) {
return ResolvingEndpointFactory.getInstance();
}
if (configElement.getAttribute(new QName("template")) != null) {
return new TemplateEndpointFactory();
}
OMElement addressElement = configElement.getFirstChildWithName(
new QName(SynapseConstants.SYNAPSE_NAMESPACE, "address"));
if (addressElement != null) {
return AddressEndpointFactory.getInstance();
}
OMElement wsdlElement = configElement.getFirstChildWithName(
new QName(SynapseConstants.SYNAPSE_NAMESPACE, "wsdl"));
if (wsdlElement != null) {
return WSDLEndpointFactory.getInstance();
}
OMElement defaultElement = configElement.getFirstChildWithName(
new QName(SynapseConstants.SYNAPSE_NAMESPACE, "default"));
if (defaultElement != null) {
return DefaultEndpointFactory.getInstance();
}
OMElement lbElement = configElement.getFirstChildWithName
(new QName(SynapseConstants.SYNAPSE_NAMESPACE, "loadbalance"));
if (lbElement != null) {
OMElement sessionElement = configElement.
getFirstChildWithName(new QName(SynapseConstants.SYNAPSE_NAMESPACE, "session"));
if (sessionElement != null) {
return SALoadbalanceEndpointFactory.getInstance();
} else {
return LoadbalanceEndpointFactory.getInstance();
}
}
OMElement dlbElement = configElement.getFirstChildWithName
(new QName(SynapseConstants.SYNAPSE_NAMESPACE, "dynamicLoadbalance"));
if (dlbElement != null) {
//TODO: Handle Session affinity & failover
return DynamicLoadbalanceEndpointFactory.getInstance();
}
OMElement foElement = configElement.getFirstChildWithName
(new QName(SynapseConstants.SYNAPSE_NAMESPACE, "failover"));
if (foElement != null) {
return FailoverEndpointFactory.getInstance();
}
handleException("Invalid endpoint configuration.");
// just to make the compiler happy : never executes
return null;
}
/**
* Helper method to construct children endpoints
*
* @param listEndpointElement OMElement representing the children endpoints
* @param parent Parent endpoint
* @param properties bag of properties to pass in any information to the factory
* @return List of children endpoints
*/
protected ArrayList<Endpoint> getEndpoints(OMElement listEndpointElement, Endpoint parent,
Properties properties) {
ArrayList<Endpoint> endpoints = new ArrayList<Endpoint>();
ArrayList<String> keys = new ArrayList<String>();
Iterator iter = listEndpointElement.getChildrenWithName(XMLConfigConstants.ENDPOINT_ELT);
while (iter.hasNext()) {
OMElement endptElem = (OMElement) iter.next();
Endpoint endpoint = EndpointFactory.getEndpointFromElement(endptElem, true, properties);
if (endpoint instanceof IndirectEndpoint) {
String key = ((IndirectEndpoint) endpoint).getKey();
if (!keys.contains(key)) {
keys.add(key);
} else {
handleException("Same endpoint definition cannot be used with in the siblings");
}
}
endpoint.setParentEndpoint(parent);
endpoints.add(endpoint);
}
return endpoints;
}
/**
* provide a custom Endpoint definition factory
* @param factory
*/
public void setEndpointDefinitionFactory(DefinitionFactory factory){
customDefnFactory = factory;
}
/**
* return current factory for building this endpoint definition
* @return
*/
public DefinitionFactory getEndpointDefinitionFactory(){
return customDefnFactory;
}
/**
* Helper method to extract endpoint properties.
*
* @param endpoint actual endpoint to set the properties
* @param endpointElement actual endpoint element
*/
protected void processProperties(PropertyInclude endpoint, OMElement endpointElement) {
List<MediatorProperty> properties =
MediatorPropertyFactory.getMediatorProperties(endpointElement);
if (properties != null && properties.size() > 0) {
endpoint.addProperties(properties);
}
}
protected static void handleException(String msg) {
log.error(msg);
throw new SynapseException(msg);
}
protected static void handleException(String msg, Exception e) {
log.error(msg, e);
throw new SynapseException(msg, e);
}
}