/*
* Copyright 2012 Research Studios Austria Forschungsges.m.b.H.
*
* 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 won.owner.messaging;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import org.apache.activemq.camel.component.ActiveMQComponent;
import org.apache.camel.CamelContext;
import org.apache.camel.RoutesBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import won.cryptography.ssl.MessagingContext;
import won.owner.camel.routes.OwnerApplicationListenerRouteBuilder;
import won.owner.camel.routes.OwnerProtocolDynamicRoutes;
import won.protocol.exception.CamelConfigurationFailedException;
import won.protocol.jms.BrokerComponentFactory;
import won.protocol.jms.OwnerProtocolCamelConfigurator;
import won.protocol.model.MessagingType;
import won.protocol.repository.ConnectionRepository;
import won.protocol.repository.NeedRepository;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* User: LEIH-NB
* Date: 28.01.14
*/
public class OwnerProtocolCamelConfiguratorImpl implements OwnerProtocolCamelConfigurator
{
private CamelContext camelContext;
private MessagingContext messagingContext;
@Autowired
private NeedRepository needRepository;
@Autowired
private ConnectionRepository connectionRepository;
@Autowired
private BrokerComponentFactory brokerComponentFactory;
private Logger logger = LoggerFactory.getLogger(getClass());
private BiMap<URI,String> endpointMap = HashBiMap.create();
private Map<URI,String> startingComponentMap = new HashMap<>();
private BiMap<URI, String> brokerComponentMap = HashBiMap.create();
private String startingComponent;
private String componentName;
private String defaultNodeURI;
protected OwnerProtocolCamelConfiguratorImpl() {
}
//TODO duplicate - see if can be mergerd with needbased - very similar code...
@Override
public synchronized final String configureCamelEndpointForNodeURI(URI wonNodeURI, URI brokerURI, String ownerProtocolQueueName) throws CamelConfigurationFailedException {
//TODO: the linked data description of the won node must be at [NODE-URI]/resource
// according to this code. This should be explicitly defined somewhere
String brokerComponentName = setupBrokerComponentName(brokerURI);
//addCamelComponentForWonNodeBroker(wonNodeURI,brokerURI,null);
addCamelComponentForWonNodeBroker(brokerURI, brokerComponentName);
//TODO: make this configurable
String endpoint = brokerComponentName + ":queue:"+ownerProtocolQueueName;
endpointMap.put(wonNodeURI,endpoint);
List<String> endpointList = new ArrayList<>();
endpointList.add(endpoint);
logger.info("endpoint of wonNodeURI {} is {}",wonNodeURI,endpointMap.get(wonNodeURI));
return endpointList.get(0);
}
@Override
public synchronized void addRemoteQueueListeners(List<String> endpoints, URI remoteEndpoint) throws CamelConfigurationFailedException {
List<String> customSchemeEndpoints = adjustSchemeToRemoteEndpoint(endpoints, remoteEndpoint);
//remove endpoints already configured in the camel context
customSchemeEndpoints = customSchemeEndpoints.stream().filter(
x -> camelContext.hasEndpoint(x) == null
)
.collect(Collectors.toList());
if (customSchemeEndpoints.size() > 0) {
logger.debug(
"Adding route for listening to remote queue {} ", remoteEndpoint);
OwnerApplicationListenerRouteBuilder ownerApplicationListenerRouteBuilder = new OwnerApplicationListenerRouteBuilder(
camelContext, customSchemeEndpoints, remoteEndpoint);
try {
camelContext.addRoutes(ownerApplicationListenerRouteBuilder);
} catch (Exception e) {
logger.debug("adding route to camel context failed", e);
throw new CamelConfigurationFailedException("adding route to camel context failed", e);
}
} else {
logger.debug("route for listening to remote queue {} already configured", remoteEndpoint);
}
}
/**
* Scheme of the remote endpoint for which camel component has already bean configured, should correspond to
* the scheme of the endpoints for which listeners are being added. In this case, our component name can contain
* part specific to a particular remote broker, so that they can connect to different brokers without overriding
* each other.
* @param endpoints
* @param remoteEndpoint
* @return
*/
private List<String> adjustSchemeToRemoteEndpoint(final List<String> endpoints, final URI remoteEndpoint) {
String remoteScheme = remoteEndpoint.getScheme();
List<String> customSchemeEndpoints = new ArrayList<>(endpoints.size());
for (String ep : endpoints) {
String epScheme = URI.create(ep).getScheme();
ep = ep.replace(epScheme, remoteScheme);
customSchemeEndpoints.add(ep);
}
return customSchemeEndpoints;
}
// todo: the method is activemq specific. refactor it to support other brokers.
// TODO some duplicate code between here and NeedBasedCamelConfiguratorImpl (setup broker name) - i.e.
// this method can probably be shared and owner's configurator can probably extend needbased...
public synchronized void addCamelComponentForWonNodeBroker(URI brokerURI, String brokerComponentName){
if(camelContext.getComponent(brokerComponentName,false)==null){
ActiveMQComponent activeMQComponent = (ActiveMQComponent) brokerComponentFactory.getBrokerComponent
(brokerURI, MessagingType.Queue, messagingContext);
camelContext.addComponent(brokerComponentName, activeMQComponent);
logger.info("adding component with component name {}",brokerComponentName);
if (!brokerComponentMap.containsKey(brokerURI))
brokerComponentMap.put(brokerURI, brokerComponentName);
}
}
@Override
public synchronized void addRouteForEndpoint(String startingEndpoint, final URI wonNodeURI) throws CamelConfigurationFailedException {
/**
* there can be only one route per endpoint. Thus, consuming endpoint of each route shall be unique.
*/
//todo: using replaceAll might result in security issues. change this.
String tempStartingComponentName = startingComponent;
tempStartingComponentName = tempStartingComponentName + endpointMap.get(wonNodeURI).replaceAll(":","_");
setStartingEndpoint(wonNodeURI, tempStartingComponentName);
if (camelContext.getComponent(tempStartingComponentName)==null||camelContext.getRoute(endpointMap.get(wonNodeURI))==null){
//OwnerProtocolDynamicRoutes ownerProtocolRouteBuilder = new OwnerProtocolDynamicRoutes(camelContext, tempStartingComponentName);
RoutesBuilder ownerProtocolRouteBuilder = createRoutesBuilder(tempStartingComponentName, wonNodeURI);
try {
camelContext.addRoutes(ownerProtocolRouteBuilder);
} catch (Exception e) {
throw new CamelConfigurationFailedException("adding route to camel context failed",e);
}
}
}
protected RoutesBuilder createRoutesBuilder(final String startingComponent, final URI brokerUri) {
return new OwnerProtocolDynamicRoutes(camelContext, startingComponent);
}
@Override
public String getStartingEndpoint(URI wonNodeURI){
return startingComponentMap.get(wonNodeURI);
}
@Override
public void setStartingEndpoint(URI wonNodeURI, String startingEndpoint) {
startingComponentMap.put(wonNodeURI,startingEndpoint);
}
@Override
public void setCamelContext(CamelContext camelContext) {
this.camelContext = camelContext;
}
@Override
public CamelContext getCamelContext() {
return camelContext;
}
@Override
public void setMessagingContext(MessagingContext messagingContext) {
this.messagingContext = messagingContext;
}
@Override
public String getEndpoint(URI wonNodeUri){
return endpointMap.get(wonNodeUri);
}
//TODO: duplicate with needbasedcamelconfigimpl...
@Override
public String setupBrokerComponentName(URI brokerUri) {
return this.componentName+brokerUri.toString().replaceAll("[/:]","");
}
@Override
public void setStartingComponent(String startingComponent) {
this.startingComponent = startingComponent;
}
@Override
public String getBrokerComponentName(URI brokerUri) {
return brokerComponentMap.get(brokerUri);
}
@Override
public void setComponentName(String componentName) {
this.componentName = componentName;
}
@Override
public void setDefaultNodeURI(String defaultNodeURI) {
this.defaultNodeURI = defaultNodeURI;
}
}