Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright (C) 2012 Ness Computing, Inc.
*
* 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 com.nesscomputing.jms.activemq;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import net.sf.cglib.proxy.Enhancer;
import org.apache.activemq.transport.Transport;
import org.apache.activemq.transport.TransportFactory;
import org.apache.activemq.transport.TransportServer;
import org.apache.activemq.util.URISupport;
import org.apache.activemq.wireformat.WireFormat;
import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.Maps;
import com.nesscomputing.logging.Log;
import com.nesscomputing.service.discovery.client.ReadOnlyDiscoveryClient;
import com.nesscomputing.service.discovery.client.ServiceInformation;
/**
* Handle srvc:// URIs by doing discovery and turning them into a failover transport.
*
* For example if you connect to srvc://activemq?discoveryId=x&serviceType=y this transport will look
* up the injector whose discoveryId is "x". It will then proceed to discover all services with
* name "activemq" and type "y". They all must advertise URIs, which will then get built into a
* failover:(x,y) transport.
*
* This code uses a static map to coordinate DiscoveryClients since the ActiveMQ TransportFactory
* abstraction is static in nature. A number of possibilities were examined but it seems impossible
* to register a transport in a way that is truly IoC-friendly.
*/
public class ServiceDiscoveryTransportFactory extends TransportFactory {
private static final Log LOG = Log.findLog();
private static final ConcurrentMap DISCO_CLIENTS = Maps.newConcurrentMap();
private static final ConcurrentMap CONFIGS = Maps.newConcurrentMap();
/**
* Register a discoveryClient under a unique id. This works around the TransportFactory's inherent staticness
* so that we may use the correct discovery client even in the presence of multiple injectors in the same JVM.
*/
static void registerDiscoveryClient(UUID injectorId, ReadOnlyDiscoveryClient discoveryClient, DiscoveryJmsConfig config) {
DISCO_CLIENTS.put(injectorId, discoveryClient);
CONFIGS.put(injectorId, config);
LOG.info("Registered discovery client %s as %s", injectorId, discoveryClient);
}
@Override
public TransportServer doBind(URI location) throws IOException {
throw new IOException("The srvc transport is inappropriate for specifying a server location: " + location);
}
@Override
protected Transport createTransport(URI location, WireFormat wf)
throws MalformedURLException, UnknownHostException, IOException {
final Map params = findParameters(location);
final List services = getServiceInformations(location, params);
return buildTransport(params, services);
}
/**
* Extract the query string from a connection URI into a Map
*/
private Map findParameters(URI location) throws MalformedURLException {
final Map params;
try {
params = URISupport.parseParameters(location);
} catch (final URISyntaxException e) {
throw new MalformedURLException(e.getMessage());
}
return params;
}
/**
* Find and validate the discoveryId given in a connection string
*/
private UUID getDiscoveryId(final Map params) throws IOException {
final String discoveryId = params.get("discoveryId");
if (discoveryId == null) {
throw new IOException("srvc transport did not get a discoveryId parameter. Refusing to create.");
}
return UUID.fromString(discoveryId);
}
/**
* Locate the appropriate DiscoveryClient for a given discoveryId
*/
private ReadOnlyDiscoveryClient getDiscoveryClient(Map params) throws IOException {
final UUID discoveryId = getDiscoveryId(params);
final ReadOnlyDiscoveryClient discoveryClient = DISCO_CLIENTS.get(discoveryId);
if (discoveryClient == null) {
throw new IOException("No discovery client registered for id " + discoveryId);
}
return discoveryClient;
}
private DiscoveryJmsConfig getConfig(Map params) throws IOException {
return CONFIGS.get(getDiscoveryId(params));
}
/**
* Find and return applicable services for a given connection
*/
private List getServiceInformations(URI location, final Map params)
throws IOException {
final ReadOnlyDiscoveryClient discoveryClient = getDiscoveryClient(params);
final String serviceType = params.get("serviceType");
final List services;
if (serviceType != null) {
services = discoveryClient.findAllServiceInformation(location.getHost(), serviceType);
} else {
services = discoveryClient.findAllServiceInformation(location.getHost());
}
return services;
}
/**
* From a list of ServiceInformation, build a failover transport that balances between the brokers.
*/
private Transport buildTransport(Map params, final List services) throws IOException {
final String configPostfix = getConfig(params).getServiceConfigurationPostfix();
final StringBuilder uriBuilder = new StringBuilder();
uriBuilder.append("failover:(");
uriBuilder.append(Joiner.on(',').join(Collections2.transform(services, SERVICE_TO_URI)));
uriBuilder.append(")");
if (!StringUtils.isBlank(configPostfix)) {
uriBuilder.append("?");
uriBuilder.append(configPostfix);
}
try {
final URI uri = URI.create(uriBuilder.toString());
LOG.debug("Service discovery transport discovered %s", uri);
return interceptPropertySetters(TransportFactory.compositeConnect(uri));
} catch (final Exception e) {
Throwables.propagateIfPossible(e, IOException.class);
throw new IOException("Could not create failover transport", e);
}
}
/**
* ActiveMQ expects to reflectively call setX for every property x specified in the connection string.
* Since we are actually constructing a failover transport, these properties are obviously not expected
* and ActiveMQ complains that we are specifying invalid parameters. So create a CGLIB proxy that
* intercepts and ignores appropriate setter calls.
*/
private Transport interceptPropertySetters(Transport transport) {
final Enhancer e = new Enhancer();
e.setInterfaces(new Class>[] {Transport.class, ServiceTransportBeanSetters.class});
final TransportDelegationFilter filter = new TransportDelegationFilter(transport, ServiceTransportBeanSetters.class);
e.setCallbackFilter(filter);
e.setCallbacks(filter.getCallbacks());
return (Transport) e.create();
}
private static final Function SERVICE_TO_URI = new Function() {
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings("NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE")
public String apply(ServiceInformation input) {
final String uri = input.getProperty("uri");
Preconditions.checkArgument(!StringUtils.isBlank(uri), "service did not advertise a connect uri");
return uri;
}
};
}