blob: a5d4e835816a75f6a59b381e4ceb8ca8849df175 [file] [log] [blame]
/*
* 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.qpid.jms.jndi;
import static org.apache.qpid.jms.JmsConnectionFactory.REMOTE_URI_PROP;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.jms.Queue;
import javax.jms.Topic;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import org.apache.qpid.jms.JmsConnectionFactory;
import org.apache.qpid.jms.JmsQueue;
import org.apache.qpid.jms.JmsTopic;
import org.apache.qpid.jms.provider.ProviderFactory;
import org.apache.qpid.jms.util.VariableExpansion;
public class JmsInitialContextFactory implements InitialContextFactory {
static final String[] DEFAULT_CONNECTION_FACTORY_NAMES = {
"ConnectionFactory", "QueueConnectionFactory", "TopicConnectionFactory" };
static final String CONNECTION_FACTORY_KEY_PREFIX = "connectionfactory.";
static final String QUEUE_KEY_PREFIX = "queue.";
static final String TOPIC_KEY_PREFIX = "topic.";
static final String CONNECTION_FACTORY_DEFAULT_KEY_PREFIX = "default." + CONNECTION_FACTORY_KEY_PREFIX;
static final String CONNECTION_FACTORY_PROPERTY_KEY_PREFIX = "property." + CONNECTION_FACTORY_KEY_PREFIX;
static final String DYNAMIC_QUEUES = "dynamicQueues";
static final String DYNAMIC_TOPICS = "dynamicTopics";
@Override
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
// Copy the environment to ensure we don't modify/reference it, it belongs to the caller.
Hashtable<Object, Object> environmentCopy = new Hashtable<Object, Object>();
environmentCopy.putAll(environment);
String location = null;
if (environmentCopy.containsKey(Context.PROVIDER_URL)) {
location = (String) environment.get(Context.PROVIDER_URL);
} else {
location = System.getProperty(Context.PROVIDER_URL);
}
// If present, check if PROVIDER_URL is a client URI, by seeing if we find a factory for it.
// If we do, set it as the URI to be used for the default connection factories.
boolean providerUri = false;
if (location != null) {
try {
String expanded = expand(location, environmentCopy);
if (ProviderFactory.findProviderFactory(new URI(expanded)) != null) {
environmentCopy.put(CONNECTION_FACTORY_DEFAULT_KEY_PREFIX + REMOTE_URI_PROP, expanded);
providerUri = true;
}
} catch (IOException | URISyntaxException e) {
// Not a valid URI or didnt find a client factory for it.
}
}
try {
// If it wasnt a client URI, check for *optional* properties file to augment given environment
if (!providerUri && location != null) {
BufferedInputStream inputStream;
try {
URL fileURL = new URL(location);
inputStream = new BufferedInputStream(fileURL.openStream());
} catch (MalformedURLException e) {
inputStream = new BufferedInputStream(new FileInputStream(location));
}
Properties p = new Properties();
try {
p.load(inputStream);
} finally {
inputStream.close();
}
for (Map.Entry<Object, Object> entry : p.entrySet()) {
String key = String.valueOf(entry.getKey());
String value = String.valueOf(entry.getValue());
environmentCopy.put(key, value);
}
}
} catch (IOException ioe) {
NamingException ne = new NamingException("Unable to load property file: " + location + ".");
ne.initCause(ioe);
throw ne;
}
// Now inspect the environment and create the bindings for the context
Map<String, Object> bindings = new ConcurrentHashMap<String, Object>();
createConnectionFactories(environmentCopy, bindings);
createQueues(environmentCopy, bindings);
createTopics(environmentCopy, bindings);
// Add sub-contexts for dynamic creation on lookup.
// "dynamicQueues/<queue-name>"
bindings.put(DYNAMIC_QUEUES, new LazyCreateContext() {
private static final long serialVersionUID = 6503881346214855588L;
@Override
protected Object createEntry(String name) {
return new JmsQueue(name);
}
});
// "dynamicTopics/<topic-name>"
bindings.put(DYNAMIC_TOPICS, new LazyCreateContext() {
private static final long serialVersionUID = 2019166796234979615L;
@Override
protected Object createEntry(String name) {
return new JmsTopic(name);
}
});
return createContext(environmentCopy, bindings);
}
private void createConnectionFactories(Hashtable<Object, Object> environment, Map<String, Object> bindings) throws NamingException {
Map<String, String> factories = getConnectionFactoryNamesAndURIs(environment);
Map<String, String> defaults = getConnectionFactoryDefaults(environment);
for (Entry<String, String> entry : factories.entrySet()) {
String name = entry.getKey();
String uri = entry.getValue();
JmsConnectionFactory factory = null;
try {
factory = createConnectionFactory(name, uri, defaults, environment);
} catch (Exception e) {
NamingException ne = new NamingException("Exception while creating ConnectionFactory '" + name + "'.");
ne.initCause(e);
throw ne;
}
bindings.put(name, factory);
}
}
// Implementation methods
// -------------------------------------------------------------------------
protected ReadOnlyContext createContext(Hashtable<Object, Object> environment, Map<String, Object> bindings) {
return new ReadOnlyContext(environment, bindings);
}
protected JmsConnectionFactory createConnectionFactory(String name, String uri, Map<String, String> defaults, Hashtable<Object, Object> environment) throws URISyntaxException {
Map<String, String> props = new LinkedHashMap<String, String>();
// Add the defaults which apply to all connection factories
props.putAll(defaults);
// Add any URI entry for this specific factory name
if (uri != null && !uri.trim().isEmpty()) {
props.put(JmsConnectionFactory.REMOTE_URI_PROP, uri);
}
// Add any factory-specific additional properties
props.putAll(getConnectionFactoryProperties(name, environment));
return createConnectionFactory(props);
}
protected Map<String, String> getConnectionFactoryNamesAndURIs(Map<Object, Object> environment) {
Map<String, String> factories = new LinkedHashMap<String, String>();
for (Iterator<Entry<Object, Object>> iter = environment.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Object, Object> entry = iter.next();
String key = String.valueOf(entry.getKey());
if (key.toLowerCase().startsWith(CONNECTION_FACTORY_KEY_PREFIX)) {
String factoryName = key.substring(CONNECTION_FACTORY_KEY_PREFIX.length());
String value = null;
if(entry.getValue() != null) {
value = String.valueOf(entry.getValue());
}
factories.put(factoryName, expand(value, environment));
}
}
if (factories.isEmpty()) {
for (int i = 0; i < DEFAULT_CONNECTION_FACTORY_NAMES.length; i++) {
factories.put(DEFAULT_CONNECTION_FACTORY_NAMES[i], null);
}
}
return factories;
}
protected Map<String, String> getConnectionFactoryDefaults(Map<Object, Object> environment) {
Map<String, String> map = new LinkedHashMap<String, String>();
map.put(JmsConnectionFactory.REMOTE_URI_PROP, JmsConnectionFactory.getDefaultRemoteAddress());
for (Iterator<Entry<Object, Object>> iter = environment.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Object, Object> entry = iter.next();
String key = String.valueOf(entry.getKey());
if (key.toLowerCase().startsWith(CONNECTION_FACTORY_DEFAULT_KEY_PREFIX)) {
String jndiName = key.substring(CONNECTION_FACTORY_DEFAULT_KEY_PREFIX.length());
String value = String.valueOf(entry.getValue());
map.put(jndiName, expand(value, environment));
}
}
return Collections.unmodifiableMap(map);
}
protected Map<String, String> getConnectionFactoryProperties(String factoryName, Map<Object, Object> environment) {
Map<String, String> map = new LinkedHashMap<String, String>();
final String factoryNameSuffix = factoryName + ".";
for (Iterator<Entry<Object, Object>> iter = environment.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Object, Object> entry = iter.next();
String key = String.valueOf(entry.getKey());
if (key.toLowerCase().startsWith(CONNECTION_FACTORY_PROPERTY_KEY_PREFIX)) {
if(key.substring(CONNECTION_FACTORY_PROPERTY_KEY_PREFIX.length()).startsWith(factoryNameSuffix)) {
String propertyName = key.substring(CONNECTION_FACTORY_PROPERTY_KEY_PREFIX.length() + factoryNameSuffix.length());
String value = String.valueOf(entry.getValue());
map.put(propertyName, expand(value, environment));
}
}
}
return map;
}
protected void createQueues(Hashtable<Object, Object> environment, Map<String, Object> bindings) {
for (Iterator<Entry<Object, Object>> iter = environment.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Object, Object> entry = iter.next();
String key = entry.getKey().toString();
if (key.startsWith(QUEUE_KEY_PREFIX)) {
String jndiName = key.substring(QUEUE_KEY_PREFIX.length());
String value = expand(entry.getValue().toString(), environment);
bindings.put(jndiName, createQueue(value));
}
}
}
protected void createTopics(Hashtable<Object, Object> environment, Map<String, Object> bindings) {
for (Iterator<Entry<Object, Object>> iter = environment.entrySet().iterator(); iter.hasNext();) {
Map.Entry<Object, Object> entry = iter.next();
String key = entry.getKey().toString();
if (key.startsWith(TOPIC_KEY_PREFIX)) {
String jndiName = key.substring(TOPIC_KEY_PREFIX.length());
String value = expand(entry.getValue().toString(), environment);
bindings.put(jndiName, createTopic(value));
}
}
}
/**
* Factory method to create new Queue instances
*/
protected Queue createQueue(String name) {
return new JmsQueue(name);
}
/**
* Factory method to create new Topic instances
*/
protected Topic createTopic(String name) {
return new JmsTopic(name);
}
/**
* Factory method to create a new connection factory using the given properties
*/
protected JmsConnectionFactory createConnectionFactory(Map<String, String> properties) {
JmsConnectionFactory factory = new JmsConnectionFactory();
Map<String, String> unused = factory.setProperties(properties);
if (!unused.isEmpty()) {
String msg =
" Not all properties could be set on the ConnectionFactory."
+ " Check the properties are spelled correctly."
+ " Unused properties=[" + unused + "].";
throw new IllegalArgumentException(msg);
}
return factory;
}
protected static String expand(String input, Map<Object, Object> environment) {
return VariableExpansion.expand(input, variable -> {
String resolve = VariableExpansion.SYS_PROP_RESOLVER.resolve(variable);
if (resolve == null) {
resolve = VariableExpansion.ENV_VAR_RESOLVER.resolve(variable);
if (resolve == null) {
Object o = environment.get(variable);
if (o != null) {
resolve = String.valueOf(o);
}
}
}
return resolve;
});
}
}