/*
* 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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import won.cryptography.service.CryptographyService;
import won.cryptography.service.KeyStoreService;
import won.cryptography.service.RegistrationClient;
import won.cryptography.service.RegistrationRestClientHttps;
import won.cryptography.ssl.AliasFromFingerprintGenerator;
import won.cryptography.ssl.AliasGenerator;
import won.protocol.exception.CamelConfigurationFailedException;
import won.protocol.exception.NoSuchConnectionException;
import won.protocol.jms.*;
import won.protocol.model.Connection;
import won.protocol.model.Need;
import won.protocol.model.WonNode;
import won.protocol.repository.ConnectionRepository;
import won.protocol.repository.NeedRepository;
import won.protocol.repository.WonNodeRepository;
import won.protocol.util.DataAccessUtils;
import java.net.URI;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* User: syim
* Date: 27.01.14
*/
public class OwnerProtocolCommunicationServiceImpl implements OwnerProtocolCommunicationService
{
@Autowired
private OwnerProtocolCamelConfiguratorImpl ownerProtocolCamelConfigurator;
private ActiveMQService activeMQService;
@Autowired
private NeedRepository needRepository;
@Autowired
private ConnectionRepository connectionRepository;
@Autowired
private WonNodeRepository wonNodeRepository;
@Autowired
private CryptographyService cryptographyService;
@Autowired
private KeyStoreService keyStoreService;
private AliasGenerator aliasGenerator = new AliasFromFingerprintGenerator();
//can also be autowired
private RegistrationClient registrationClient;
Logger logger = LoggerFactory.getLogger(this.getClass());
public void setRegistrationClient(final RegistrationRestClientHttps registrationClient) {
this.registrationClient = registrationClient;
}
/**
* Registers the owner application at a won node. Owner Id is typically his Key ID (lower 64 bits of the owner public
* key fingerprint). Unless there is a collision of owner ids on the node - then the owner can assign another id...
*
* @return ownerApplicationId
* @throws Exception
*/
public synchronized void register(URI wonNodeURI, MessagingService messagingService) throws Exception {
CamelConfiguration camelConfiguration = null;
logger.debug("setting up communication with won node: " + wonNodeURI);
String ownerApplicationId = calculateOwnerApplicationIdFromOwnerCertificate();
WonNode wonNode = wonNodeRepository.findOneByWonNodeURIAndOwnerApplicationID(wonNodeURI, ownerApplicationId);
if (wonNode != null) {
logger.debug("we're already registered. Connecting with WoN node: " + wonNodeURI);
configureCamelEndpoint(wonNodeURI, ownerApplicationId);
configureRemoteEndpointsForOwnerApplication(ownerApplicationId, getProtocolCamelConfigurator().getEndpoint
(wonNodeURI), messagingService);
logger.debug("connected with WoN node: " + wonNodeURI);
} else {
logger.info("we're not yet registered. Registering with WoN node:" + wonNodeURI);
String nodeGeneratedOwnerApplicationId = registrationClient.register(wonNodeURI.toString());
if (!ownerApplicationId.equals(nodeGeneratedOwnerApplicationId) ){
throw new java.lang.IllegalStateException("WoN node " + wonNodeURI +" generated an ownerApplicationId that differs from" +
" ours. Node generated: " + nodeGeneratedOwnerApplicationId + ", we " +
"generated: " + ownerApplicationId);
}
logger.debug("registered with WoN node: " + wonNodeURI +", ownerappID: " + ownerApplicationId);
camelConfiguration = configureCamelEndpoint(wonNodeURI, ownerApplicationId);
storeWonNode(ownerApplicationId, camelConfiguration, wonNodeURI);
configureRemoteEndpointsForOwnerApplication(ownerApplicationId, getProtocolCamelConfigurator().getEndpoint
(wonNodeURI), messagingService);
logger.info("connected with WoN node: : " + wonNodeURI);
}
}
private String calculateOwnerApplicationIdFromOwnerCertificate() throws CertificateException {
Certificate cert = keyStoreService.getCertificate(cryptographyService.getDefaultPrivateKeyAlias());
return aliasGenerator.generateAlias((X509Certificate) cert);
}
// TODO this is messy, has to be improved, maybe endpoints should be obtained in the same step as registration,
// e.g. the register call returns not only application id, but also the endpoints...
private void configureRemoteEndpointsForOwnerApplication(String ownerApplicationID, String remoteEndpoint, MessagingService messagingService)
throws CamelConfigurationFailedException, ExecutionException, InterruptedException {
Map<String, Object> headerMap = new HashMap<>();
headerMap.put("ownerApplicationID", ownerApplicationID);
headerMap.put("methodName", "getEndpoints");
headerMap.put("remoteBrokerEndpoint", remoteEndpoint);
Future<List<String>> futureResults = messagingService
.sendInOutMessageGeneric(headerMap, headerMap, null, "seda:outgoingMessages");
List<String> endpoints = futureResults.get();
getProtocolCamelConfigurator().addRemoteQueueListeners(endpoints, URI.create(remoteEndpoint));
//TODO: some checks needed to assure that the application is configured correctly.
//todo this method should return routes
}
/**
* Stores the won node information, possibly overwriting existing data.
*
* @param ownerApplicationId
* @param camelConfiguration
* @param wonNodeURI
* @return
* @throws NoSuchConnectionException
*/
public WonNode storeWonNode(String ownerApplicationId, CamelConfiguration camelConfiguration, URI wonNodeURI)
throws NoSuchConnectionException {
WonNode wonNode = DataAccessUtils.loadWonNode(wonNodeRepository, wonNodeURI);
if (wonNode == null) {
wonNode = new WonNode();
}
wonNode.setOwnerApplicationID(ownerApplicationId);
wonNode.setOwnerProtocolEndpoint(camelConfiguration.getEndpoint());
wonNode.setWonNodeURI(wonNodeURI);
wonNode.setBrokerURI(getBrokerUri(wonNodeURI));
wonNode.setBrokerComponent(camelConfiguration.getBrokerComponentName());
wonNode.setStartingComponent(getProtocolCamelConfigurator().getStartingEndpoint(wonNodeURI));
wonNodeRepository.save(wonNode);
logger.debug("setting starting component {}", wonNode.getStartingComponent());
return wonNode;
}
public final synchronized CamelConfiguration configureCamelEndpoint(URI wonNodeUri, String ownerId) throws
Exception {
CamelConfiguration camelConfiguration = new CamelConfiguration();
URI brokerURI = activeMQService.getBrokerEndpoint(wonNodeUri);
String ownerProtocolQueueName;
List<WonNode> wonNodeList = wonNodeRepository.findByWonNodeURI(wonNodeUri);
//OwnerProtocolCamelConfigurator ownerProtocolCamelConfigurator = camelConfiguratorFactory.createCamelConfigurator(methodName);
logger.debug("configuring camel endpoint");
if (ownerProtocolCamelConfigurator.getBrokerComponentName(brokerURI)!=null &&
ownerProtocolCamelConfigurator
.getEndpoint(wonNodeUri)!=null){
logger.debug("wonNode known");
WonNode wonNode = wonNodeList.get(0);
//brokerURI = wonNode.getBrokerURI();
camelConfiguration.setEndpoint(wonNode.getOwnerProtocolEndpoint());
if (ownerProtocolCamelConfigurator.getCamelContext().getComponent(wonNodeList.get(0).getBrokerComponent())==null){
//camelConfiguration.setBrokerComponentName(ownerProtocolCamelConfigurator.addCamelComponentForWonNodeBroker
// (wonNode.getWonNodeURI(),brokerURI,wonNode.getOwnerApplicationID()));
ownerProtocolQueueName = activeMQService.getProtocolQueueNameWithResource(wonNodeUri);
String endpoint = ownerProtocolCamelConfigurator.configureCamelEndpointForNodeURI(wonNodeUri, brokerURI,
ownerProtocolQueueName);
camelConfiguration.setBrokerComponentName(ownerProtocolCamelConfigurator.getBrokerComponentName(brokerURI));
ownerProtocolCamelConfigurator.getCamelContext().getComponent(camelConfiguration.getBrokerComponentName()).createEndpoint(camelConfiguration.getEndpoint());
if(ownerProtocolCamelConfigurator.getCamelContext().getRoute(wonNode.getStartingComponent())==null)
ownerProtocolCamelConfigurator.addRouteForEndpoint(null, wonNode.getWonNodeURI());
}
} else { //if unknown wonNode
logger.debug("wonNode unknown");
//TODO: brokerURI gets the node information already. so requesting node information again for queuename would be duplicate
ownerProtocolQueueName = activeMQService.getProtocolQueueNameWithResource(wonNodeUri);
String endpoint = ownerProtocolCamelConfigurator.configureCamelEndpointForNodeURI(wonNodeUri, brokerURI,
ownerProtocolQueueName);
camelConfiguration.setEndpoint(endpoint);
camelConfiguration.setBrokerComponentName(ownerProtocolCamelConfigurator.getBrokerComponentName(brokerURI));
ownerProtocolCamelConfigurator.addRouteForEndpoint(null, wonNodeUri);
}
return camelConfiguration;
}
@Override
public synchronized URI getWonNodeUriWithConnectionUri(URI connectionUri) throws NoSuchConnectionException {
//TODO: make this more efficient
Connection con = DataAccessUtils.loadConnection(connectionRepository, connectionUri);
URI needURI = con.getNeedURI();
Need need = needRepository.findByNeedURI(needURI).get(0);
return need.getWonNodeURI();
}
@Override
public synchronized URI getWonNodeUriWithNeedUri(URI needUri) throws NoSuchConnectionException {
Need need = needRepository.findByNeedURI(needUri).get(0);
return need.getWonNodeURI();
}
@Override
public URI getBrokerUri(URI resourceUri) throws NoSuchConnectionException {
return activeMQService.getBrokerEndpoint(resourceUri);
}
@Override
public ActiveMQService getActiveMQService() {
return activeMQService;
}
@Override
public void setActiveMQService(ActiveMQService activeMQService) {
this.activeMQService = activeMQService;
}
@Override
public OwnerProtocolCamelConfigurator getProtocolCamelConfigurator() {
return ownerProtocolCamelConfigurator;
}
public void setCryptographyService(final CryptographyService cryptographyService) {
this.cryptographyService = cryptographyService;
}
}