View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.audit.handlers.jms;
18  
19  import javax.jms.ConnectionFactory;
20  import javax.jms.Topic;
21  import javax.naming.InitialContext;
22  import javax.naming.NamingException;
23  import java.util.Hashtable;
24  
25  import org.forgerock.audit.handlers.jms.JmsAuditEventHandlerConfiguration.JndiConfiguration;
26  import org.forgerock.json.resource.InternalServerErrorException;
27  import org.forgerock.json.resource.ResourceException;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  /**
32   * Uses Jndi to get the JMS {@link Topic topic} and {@link ConnectionFactory connection factory}.
33   */
34  class JndiJmsContextManager implements JmsContextManager {
35  
36      private static final Logger logger = LoggerFactory.getLogger(JndiJmsContextManager.class);
37  
38      private Topic topic;
39      private ConnectionFactory connectionFactory;
40      private final InitialContext context;
41      private final JndiConfiguration jndiConfiguration;
42  
43      /**
44       * Given the configuration, this builds a JMS InitialContext. The classloader of this class will be used as
45       * the context classloader {@link Thread#setContextClassLoader(ClassLoader)}.
46       *
47       * @param configuration The {@link JndiConfiguration JNDI configuration}.
48       * @throws InternalServerErrorException If unable to create the {@link InitialContext JNDI context}
49       */
50      JndiJmsContextManager(JndiConfiguration configuration) throws ResourceException {
51          ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
52          try {
53              Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
54              jndiConfiguration = configuration;
55              context = new InitialContext(new Hashtable<>(configuration.getContextProperties()));
56          } catch (NamingException e) {
57              throw new InternalServerErrorException("Encountered issue building initial JNDI context", e);
58          } finally {
59              Thread.currentThread().setContextClassLoader(originalContextClassLoader);
60          }
61      }
62  
63      /**
64       * Returns the {@link Topic JMS topic} to use for JMS publish/subscribe functionality.
65       *
66       * @return The {@link Topic JMS topic} to use for JMS publish/subscribe functionality.
67       * @throws InternalServerErrorException If unable to retrieve the {@link Topic JMS topic}.
68       */
69      public Topic getTopic() throws InternalServerErrorException {
70          try {
71              if (topic == null) {
72                  topic = getObject(jndiConfiguration.getTopicName(), Topic.class);
73              }
74              return topic;
75          } catch (NamingException e) {
76              throw new InternalServerErrorException(e.getMessage(), e);
77          }
78      }
79  
80      /**
81       * Returns the {@link ConnectionFactory JMS connection factory} to use to connect to JMS services.
82       * @return the {@link ConnectionFactory JMS connection factory} to use to connect to JMS services.
83       * @throws InternalServerErrorException If unable to retrieve the {@link ConnectionFactory JMS connection factory}.
84       */
85      public ConnectionFactory getConnectionFactory() throws InternalServerErrorException {
86          try {
87              if (connectionFactory == null) {
88                  connectionFactory = getObject(jndiConfiguration.getConnectionFactoryName(), ConnectionFactory.class);
89              }
90              return connectionFactory;
91          } catch (NamingException e) {
92              throw new InternalServerErrorException(e.getMessage(), e);
93          }
94      }
95  
96      private <T> T getObject(final String jndiName, final Class<T> clazz)
97              throws NamingException, InternalServerErrorException {
98  
99          ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
100         try {
101             Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
102             final Object object = context.lookup(jndiName);
103             if (clazz.isInstance(object)) {
104                 return (T) object;
105             }
106             final String error;
107             if (null == object) {
108                 error = String.format("No Object was found at JNDI name '%s'", jndiName);
109             } else {
110                 error = String.format("JNDI lookup('%s') did not return a '%s'. It returned a '%s'='%s'",
111                         jndiName, clazz.getCanonicalName(), object.getClass().getCanonicalName(), object.toString());
112             }
113             logger.error(error);
114             throw new InternalServerErrorException(error);
115         } finally {
116             Thread.currentThread().setContextClassLoader(originalContextClassLoader);
117         }
118     }
119 }