Skip to content

Jndi support feature #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ for communicating with Amazon Simple Queue Service. This project builds on top o
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>amazon-sqs-java-messaging-lib</artifactId>
<version>1.0.8</version>
<version>1.1.0</version>
<type>jar</type>
</dependency>
```
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.amazonaws</groupId>
<artifactId>amazon-sqs-java-messaging-lib</artifactId>
<version>1.0.8</version>
<version>1.1.0</version>
<packaging>jar</packaging>
<name>Amazon SQS Java Messaging Library</name>
<description>The Amazon SQS Java Messaging Library holds the Java Message Service compatible classes, that are used
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.amazon.sqs.javamessaging.jndi;

import java.util.HashSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.jms.IllegalStateException;
import javax.jms.JMSException;
import javax.naming.NamingException;
import javax.naming.directory.InvalidAttributeValueException;

import com.amazon.sqs.javamessaging.SQSConnection;
import com.amazon.sqs.javamessaging.SQSConnectionFactory;

/**
* Manage the use of {@link SQSConnection connections} and their closings
* through an {@link SQSConnectionFactory} instance.
*
* @author krloss
* @since 1.1.0
*/
public class ConnectionsManager {
/**
* Set of connection configuration parameters.<br>
* Externally visible information.
*/
protected final SQSConnectionFactory connectionFactory;

private final HashSet<Callable<Boolean>> closeableConnections = new HashSet<>();
private SQSConnection defaultConnection;

private final Object stateLock = new Object(); // Used for interactions with connection state.

/**
* Public constructor that requires {@link SQSConnectionFactory} parameter.
*
* @param connectionFactory - set of connection configuration parameters.
* @throws NamingException
*/
public ConnectionsManager(final SQSConnectionFactory connectionFactory) throws InvalidAttributeValueException {
if(connectionFactory == null ) throw new InvalidAttributeValueException("ConnectionsManager Requires SQSConnectionFactory.");

this.connectionFactory = connectionFactory;
}

private static final Callable<Boolean> createCloseableConnection(final SQSConnection connection) {
return (new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
connection.close();
return true;
}
});
}

/**
* Creates and returns a new connection.
*
* @return {@link SQSConnection}
* @throws JMSException
*/
public SQSConnection createConnection() throws JMSException {
SQSConnection connection = connectionFactory.createConnection();

synchronized(stateLock) {
closeableConnections.add(createCloseableConnection(connection));
}

return connection;
}

/**
* Get default connection lazily.
*
* @return {@link SQSConnection}
* @throws JMSException
*/
public synchronized SQSConnection getLazyDefaultConnection() throws JMSException {
if(defaultConnection == null) defaultConnection = createConnection();

return defaultConnection;
}

private void close(ExecutorService executor) throws InterruptedException {
synchronized(stateLock) {
defaultConnection = null;
executor.invokeAll(closeableConnections);
closeableConnections.clear();
}
}

/**
* Manage the closing of {@link SQSConnection connections} through asynchronous tasks using a thread pool.
*
* @throws JMSException
* @see Executors#newCachedThreadPool()
*/
public synchronized void close() throws JMSException {
ExecutorService executor = Executors.newCachedThreadPool();

try {
close(executor);
}
catch(InterruptedException ie) {
throw new IllegalStateException(ie.getMessage());
}
finally {
executor.shutdown();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.amazon.sqs.javamessaging.jndi;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;

/**
* Simple implementation of {@link AWSStaticCredentialsProvider} with {@link BasicAWSCredentials}
* that use {@link javax.naming.Context#SECURITY_PRINCIPAL identity} as an AWS <b>access key</b>
* and {@link javax.naming.Context#SECURITY_CREDENTIALS credentials} as an AWS <b>secret access key</b>.
*
* @author krloss
* @since 1.1.0
*/
public class CredentialsProvider extends AWSStaticCredentialsProvider {
// Prevents incorrect startup.
private CredentialsProvider(String accessKey, String secretKey) {
super(new BasicAWSCredentials(accessKey.trim(),secretKey.trim()));

getCredentials(); // Initialize
}

private static boolean assertNotEmpty(String accessKey, String secretKey) {
try { if(accessKey.trim().isEmpty() || secretKey.trim().isEmpty()) return false; }
catch(NullPointerException npe) { return false; }

return true;
}

/**
* Public method that create a {@link CredentialsProvider} instance.
*
* @param securityPrincipal - {@link javax.naming.Context#SECURITY_PRINCIPAL identity}
* as an AWS <i>access key</i>
*
* @param securityCredentials - {@link javax.naming.Context#SECURITY_CREDENTIALS credentials}
* as an AWS <i>secret access key</i>
*
* @return {@link CredentialsProvider}
*/
public static CredentialsProvider create(String securityPrincipal, String securityCredentials) {
if(assertNotEmpty(securityPrincipal,securityCredentials))
return new CredentialsProvider(securityPrincipal,securityCredentials);

return null;
}

/**
* Public method that create a {@link CredentialsProvider} instance.
*
* @param securityPrincipal - {@link javax.naming.Context#SECURITY_PRINCIPAL identity}
* as an AWS <i>access key</i>
*
* @param securityCredentials - {@link javax.naming.Context#SECURITY_CREDENTIALS credentials}
* as an AWS <i>secret access key</i>
*
* @return {@link CredentialsProvider}
*/
public static CredentialsProvider create(Object securityPrincipal, Object securityCredentials) {
return create((String)securityPrincipal,(String)securityCredentials);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.amazon.sqs.javamessaging.jndi;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Session;
import javax.naming.directory.InvalidAttributeValueException;

import com.amazon.sqs.javamessaging.SQSConnection;

/**
* Breaks the <b>description string</b> with information about the {@link ResourceType resource type}
* and {@link com.amazon.sqs.javamessaging.SQSQueueDestination#getQueueName() destination name}
* to make it an instance of {@link Destination} that encapsulates a specific provider address.
* <p><ul>
* <b>Format: </b><q><i>ResourceTypeName<b> : </b>DestinationName</i></q>.<br>
* <b>Example: </b><q><i>SA:SQS_Queue-Name_v10</i></q>.
* </ul>
*
* @author krloss
* @since 1.1.0
*/
public class DestinationResource {
private static final Pattern RESOURCE_PATTERN = Pattern.compile("^\\s*([CS][ACDU])\\s*:\\s*([-\\w]+)\\s*$");

protected final ResourceType type;
protected final String name;

/**
* Public constructor that requires <i>description</i> parameter.
* <p>
* <b>Format: </b><q><i>ResourceTypeName<b> : </b>DestinationName</i></q>.
*
* @param description - string with information about the {@link ResourceType resource type}
* and {@link com.amazon.sqs.javamessaging.SQSQueueDestination#getQueueName() destination name}.
*
* @throws InvalidAttributeValueException
*/
public DestinationResource(String description) throws InvalidAttributeValueException {
Matcher matcher;

try {
matcher = RESOURCE_PATTERN.matcher(description);
}
catch(NullPointerException npe) {
throw new InvalidAttributeValueException("DestinationResource Requires Description.");
}

if(!matcher.matches()) throw new InvalidAttributeValueException("DestinationResource Pattern Not Acceptable.");

this.name = matcher.group(2);
this.type = ResourceType.valueOf(matcher.group(1));
}

/**
* Gets the <b>connection</b> according to the <i>pooling type</i> and<br>
* creates <b>session</b> according to the <i>acknowledgment mode</i>.
*/
private Session createSession(final ConnectionsManager connectionsManager) throws JMSException {
SQSConnection connection = type.isSessionPooling ?
connectionsManager.getLazyDefaultConnection() : connectionsManager.createConnection();

return connection.createSession(false,type.acknowledgeMode);
}

/**
* Makes this object in an instance of {@link Destination}.
*
* @param connectionsManager - object that manages connections.
* @return Destination - JMS administered object that encapsulates a specific provider address.
* @throws InvalidAttributeValueException
* @throws JMSException
* @see ConnectionsManager
*/
public Destination getDestination(final ConnectionsManager connectionsManager) throws InvalidAttributeValueException, JMSException {
if(connectionsManager == null) throw new InvalidAttributeValueException("GetConnection Requires ResourceType.");

return createSession(connectionsManager).createQueue(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.amazon.sqs.javamessaging.jndi;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.directory.InvalidAttributeValueException;

import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration;

/**
* Breaks the <b>configuration string</b> to make it an instance of {@link EndpointConfiguration} that
* enables the use of public IPs on the Internet or VPC endpoints that are powered by AWS PrivateLink.
* <p><ul>
* <b>Format: </b><q><i>Region@EndpointURL</i></q>.<br>
* <b>Example: </b><q><i>us-east-2@https://sqs.us-east-2.amazonaws.com/</i></q>.
* </ul>
*
* @author krloss
* @since 1.1.0
* @see javax.naming.Context#PROVIDER_URL
*/
public class ProviderEndpointConfiguration {
private static final Pattern CONFIGURATION_PATTERN = Pattern.compile("^\\s*+(.+?)\\s*+@\\s*+(.+?)\\s*+$");

private final String serviceEndpoint;
private final String signingRegion;

/**
* Public constructor that requires <i>configuration</i> parameter.
* <p>
* <b>Format: </b><q><i>Region@EndpointURL</i></q>.
*
* @param configuration - information for the service provider;
* @throws InvalidAttributeValueException
*/
public ProviderEndpointConfiguration(String configuration) throws InvalidAttributeValueException {
Matcher matcher;

try {
matcher = CONFIGURATION_PATTERN.matcher(configuration);
}
catch(NullPointerException npe) {
throw new InvalidAttributeValueException("ProviderEndpointConfiguration Requires Configuration String.");
}

if(!matcher.matches()) throw new InvalidAttributeValueException("ProviderEndpointConfiguration Pattern Not Acceptable.");

this.serviceEndpoint = matcher.group(2);
this.signingRegion = matcher.group(1);
}

/**
* Public constructor that requires <i>configuration</i> parameter.
* <p>
* <b>Format: </b><q><i>Region@EndpointURL</i></q>.
*
* @param configuration - information for the service provider;
* @throws InvalidAttributeValueException
*/
public ProviderEndpointConfiguration(Object configuration) throws InvalidAttributeValueException {
this((String)configuration);
}

/**
* Makes this object in an instance of {@link EndpointConfiguration}.
*
* @return EndpointConfiguration - a container for configuration required to submit requests to an AWS service.
*/
public EndpointConfiguration createConfiguration() {
return new EndpointConfiguration(serviceEndpoint,signingRegion);
}
}

Loading