View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j.db;
18  
19  import java.sql.Connection;
20  import java.sql.SQLException;
21  
22  import javax.naming.Context;
23  import javax.naming.InitialContext;
24  import javax.naming.NamingException;
25  
26  // PortableRemoteObject was introduced in JDK 1.3. We won't use it.
27  // import javax.rmi.PortableRemoteObject;
28  import javax.sql.DataSource;
29  
30  
31  /**
32   *  The {@code JNDIConnectionSource} is an implementation of
33   *  {@link ConnectionSource} that obtains a {@link javax.sql.DataSource} from a
34   *  JNDI provider and uses it to obtain a {@link java.sql.Connection}.  It is
35   *  primarily designed to be used inside of J2EE application servers or
36   *  application server clients, assuming the application server supports remote
37   *  access of {@link javax.sql.DataSource}s.  In this way one can take
38   *  advantage of  connection pooling and whatever other goodies the application
39   *  server provides.
40   *  <p>
41   *  Sample configuration:<br>
42   *  <pre>
43   *    &lt;connectionSource class="org.apache.log4j.jdbc.JNDIConnectionSource"&gt;
44   *        &lt;param name="jndiLocation" value="jdbc/MySQLDS" /&gt;
45   *    &lt;/connectionSource&gt;
46   *  </pre>
47   *  <p>
48   *  Sample configuration (with username and password):<br>
49   *  <pre>
50   *    &lt;connectionSource class="org.apache.log4j.jdbc.JNDIConnectionSource"&gt;
51   *        &lt;param name="jndiLocation" value="jdbc/MySQLDS" /&gt;
52   *        &lt;param name="username" value="myUser" /&gt;
53   *        &lt;param name="password" value="myPassword" /&gt;
54   *    &lt;/connectionSource&gt;
55   *  </pre>
56   *  <p>
57   *  Note that this class will obtain an {@link javax.naming.InitialContext}
58   *  using the no-argument constructor.  This will usually work when executing
59   *  within a J2EE environment.  When outside the J2EE environment, make sure
60   *  that you provide a jndi.properties file as described by your JNDI
61   *  provider's documentation.
62   *
63   *  @author <a href="mailto:rdecampo@twcny.rr.com">Ray DeCampo</a>
64   */
65  public class JNDIConnectionSource
66         extends ConnectionSourceSkeleton {
67    private String jndiLocation = null;
68    private DataSource dataSource = null;
69  
70    /**
71     * @see org.apache.log4j.spi.OptionHandler#activateOptions()
72     */
73    public void activateOptions() {
74      if (jndiLocation == null) {
75        getLogger().error("No JNDI location specified for JNDIConnectionSource.");
76      }
77      
78      discoverConnnectionProperties();
79  
80    }
81    
82    /**
83     * @see org.apache.log4j.db.ConnectionSource#getConnection()
84     */
85    public Connection getConnection()
86           throws SQLException {
87      Connection conn = null;
88      try {
89  
90        if(dataSource == null) {
91          dataSource = lookupDataSource();
92        }
93        if (getUser() == null) {
94          conn = dataSource.getConnection();
95        } else {
96          conn = dataSource.getConnection(getUser(), getPassword());
97        }
98      } catch (final NamingException ne) {
99           getLogger().error("Error while getting data source", ne);
100       throw new SQLException("NamingException while looking up DataSource: " + ne.getMessage());
101     } catch (final ClassCastException cce) {
102       getLogger().error("ClassCastException while looking up DataSource.", cce);
103       throw new SQLException("ClassCastException while looking up DataSource: " + cce.getMessage());
104     }
105 
106     return conn;
107   }
108 
109   /**
110    * Returns the jndiLocation.
111    * @return String
112    */
113   public String getJndiLocation() {
114     return jndiLocation;
115   }
116 
117 
118   /**
119    * Sets the jndiLocation.
120    * @param jndiLocation The jndiLocation to set
121    */
122   public void setJndiLocation(String jndiLocation) {
123     this.jndiLocation = jndiLocation;
124   }
125 
126 
127   private DataSource lookupDataSource()
128          throws NamingException, SQLException {
129     DataSource ds;
130     Context ctx = new InitialContext();
131     Object obj = ctx.lookup(jndiLocation);
132 
133     // PortableRemoteObject was introduced in JDK 1.3. We won't use it.
134     //ds = (DataSource)PortableRemoteObject.narrow(obj, DataSource.class);
135     ds = (DataSource) obj;
136 
137     if (ds == null) {
138       throw new SQLException("Failed to obtain data source from JNDI location " + jndiLocation);
139     } else {
140       return ds;
141     }
142   }
143 }