001    package org.apache.ojb.broker.metadata;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import org.apache.commons.lang.SerializationUtils;
019    import org.apache.commons.lang.SystemUtils;
020    import org.apache.ojb.broker.PBKey;
021    import org.apache.ojb.broker.util.logging.Logger;
022    import org.apache.ojb.broker.util.logging.LoggerFactory;
023    
024    import java.io.Serializable;
025    import java.util.ArrayList;
026    import java.util.HashMap;
027    import java.util.Hashtable;
028    import java.util.Iterator;
029    import java.util.List;
030    
031    import javax.sql.DataSource;
032    
033    public class ConnectionRepository implements Serializable, XmlCapable
034    {
035            private static final long serialVersionUID = -5581126412817848887L;
036        private static Logger log = LoggerFactory.getLogger(ConnectionRepository.class);
037    
038        private HashMap jcdMap;
039        private Hashtable jcdAliasToPBKeyMap;
040        private JdbcMetadataUtils utils;
041    
042        public ConnectionRepository()
043        {
044            jcdMap             = new HashMap();
045            jcdAliasToPBKeyMap = new Hashtable();
046            utils              = new JdbcMetadataUtils();
047        }
048    
049        /**
050         * Returns the matching {@link JdbcConnectionDescriptor}
051         * or <code>null</code> if no descriptor could be found. The user name
052         * and pass word will be set to match the supplied </code>PBKey</code>
053         * object. If the original user name and pass word are desired, the PBKey
054         * should be obtained with {@link #getStandardPBKeyForJcdAlias(String)}.
055         */ 
056        public JdbcConnectionDescriptor getDescriptor(PBKey pbKey)
057        {
058            JdbcConnectionDescriptor result = (JdbcConnectionDescriptor) jcdMap.get(pbKey);
059            if (result == null)
060            {
061                result = deepCopyOfFirstFound(pbKey.getAlias());
062                if (result != null)
063                {
064                    result.setUserName(pbKey.getUser());
065                    result.setPassWord(pbKey.getPassword());
066                    // this build connection descriptor could not be the default connection
067                    result.setDefaultConnection(false);
068                    log.info("Automatic create of new jdbc-connection-descriptor for PBKey " + pbKey);
069                    addDescriptor(result);
070                }
071                else
072                {
073                    log.info("Could not find " + JdbcConnectionDescriptor.class.getName() + " for PBKey " + pbKey);
074                }
075            }
076            return result;
077        }
078    
079        /**
080         * Returns a deep copy of the first found connection descriptor
081         * with the given <code>jcdAlias</code> name or <code>null</code>
082         * if none found.
083         */
084        private JdbcConnectionDescriptor deepCopyOfFirstFound(String jcdAlias)
085        {
086            Iterator it = jcdMap.values().iterator();
087            JdbcConnectionDescriptor jcd;
088            while (it.hasNext())
089            {
090                jcd = (JdbcConnectionDescriptor) it.next();
091                if (jcdAlias.equals(jcd.getJcdAlias()))
092                {
093                    return (JdbcConnectionDescriptor) SerializationUtils.clone(jcd);
094                }
095            }
096            return null;
097        }
098    
099        /**
100         * Return the matching {@link org.apache.ojb.broker.PBKey} for
101         * the given jcdAlias name, or <code>null</code> if no match
102         * was found.
103         */
104        public PBKey getStandardPBKeyForJcdAlias(String jcdAlias)
105        {
106            return (PBKey) jcdAliasToPBKeyMap.get(jcdAlias);
107        }
108    
109        /**
110         * Add a new {@link JdbcConnectionDescriptor}.
111         */
112        public void addDescriptor(JdbcConnectionDescriptor jcd)
113        {
114            synchronized (jcdMap)
115            {
116                if (jcdMap.containsKey(jcd.getPBKey()))
117                {
118                    throw new MetadataException("Found duplicate connection descriptor using PBKey " +
119                            jcd.getPBKey() + ", remove the old descriptor first, before add the new one. " + jcd);
120                }
121                jcdMap.put(jcd.getPBKey(), jcd);
122                // only if the jcdAlias was not found, put the new PBKey,
123                // because we don't want to replace the original PBKey with
124                // automatic generated descriptors PBKey's - see method getDescriptor(PBKey key)
125                if (!jcdAliasToPBKeyMap.containsKey(jcd.getJcdAlias()))
126                {
127                    jcdAliasToPBKeyMap.put(jcd.getJcdAlias(), jcd.getPBKey());
128                }
129                if (log.isDebugEnabled()) log.debug("New descriptor was added: " + jcd);
130            }
131        }
132    
133        /**
134         * Creates and adds a new connection descriptor for the given JDBC connection url.
135         * This method tries to guess the platform to be used, but it should be checked
136         * afterwards nonetheless using the {@link JdbcConnectionDescriptor#getDbms()} method.
137         * For properties that are not part of the url, the following standard values are
138         * explicitly set:
139         * <ul>
140         * <li>jdbc level = 2.0</li>
141         * </ul>
142         * 
143         * @param jcdAlias          The connection alias for the created connection; if 'default' is used,
144         *                          then the new descriptor will become the default connection descriptor
145         * @param jdbcDriver        The fully qualified jdbc driver name 
146         * @param jdbcConnectionUrl The connection url of the form '[protocol]:[sub protocol]:{database-specific path]'
147         *                          where protocol is usually 'jdbc'
148         * @param username          The user name (can be <code>null</code>) 
149         * @param password          The password (can be <code>null</code>) 
150         * @return The created connection descriptor
151         * @see JdbcConnectionDescriptor#getDbms()
152         */
153        public JdbcConnectionDescriptor addDescriptor(String jcdAlias, String jdbcDriver, String jdbcConnectionUrl, String username, String password)
154        {
155            JdbcConnectionDescriptor jcd   = new JdbcConnectionDescriptor();
156            HashMap                  props = utils.parseConnectionUrl(jdbcConnectionUrl);
157    
158            jcd.setJcdAlias(jcdAlias);
159            jcd.setProtocol((String)props.get(JdbcMetadataUtils.PROPERTY_PROTOCOL));
160            jcd.setSubProtocol((String)props.get(JdbcMetadataUtils.PROPERTY_SUBPROTOCOL));
161            jcd.setDbAlias((String)props.get(JdbcMetadataUtils.PROPERTY_DBALIAS));
162    
163            String platform = utils.findPlatformFor(jcd.getSubProtocol(), jdbcDriver);
164    
165            jcd.setDbms(platform);
166            jcd.setJdbcLevel(2.0);
167            jcd.setDriver(jdbcDriver);
168            if (username != null)
169            {
170               jcd.setUserName(username);
171               jcd.setPassWord(password);
172            }
173            if ("default".equals(jcdAlias))
174            {
175                jcd.setDefaultConnection(true);
176                // arminw: MM will search for the default key
177                // MetadataManager.getInstance().setDefaultPBKey(jcd.getPBKey());
178            }
179    
180            addDescriptor(jcd);
181            return jcd;
182        }
183        
184        /**
185         * Creates and adds a new connection descriptor for the given JDBC data source.
186         * This method tries to guess the platform to be used, but it should be checked
187         * afterwards nonetheless using the {@link JdbcConnectionDescriptor#getDbms()} method.
188         * Note that the descriptor won't have a value for the driver because it is not possible
189         * to retrieve the driver classname from the data source. 
190         * 
191         * @param jcdAlias   The connection alias for the created connection; if 'default' is used,
192         *                   then the new descriptor will become the default connection descriptor
193         * @param dataSource The data source
194         * @param username   The user name (can be <code>null</code>) 
195         * @param password   The password (can be <code>null</code>) 
196         * @return The created connection descriptor
197         * @see JdbcConnectionDescriptor#getDbms()
198         */
199        public JdbcConnectionDescriptor addDescriptor(String jcdAlias, DataSource dataSource, String username, String password)
200        {
201            JdbcConnectionDescriptor jcd = new JdbcConnectionDescriptor();
202    
203            jcd.setJcdAlias(jcdAlias);
204            jcd.setDataSource(dataSource);
205            if (username != null)
206            {
207               jcd.setUserName(username);
208               jcd.setPassWord(password);
209            }
210            utils.fillJCDFromDataSource(jcd, dataSource, username, password);
211            if ("default".equals(jcdAlias))
212            {
213                jcd.setDefaultConnection(true);
214                // arminw: MM will search for the default key
215                // MetadataManager.getInstance().setDefaultPBKey(jcd.getPBKey());
216            }
217            addDescriptor(jcd);
218            return jcd;
219        }
220    
221        /**
222         * Remove a descriptor.
223         * @param validKey  This could be the {@link JdbcConnectionDescriptor}
224         * itself, or the associated {@link JdbcConnectionDescriptor#getPBKey PBKey}.
225         */
226        public void removeDescriptor(Object validKey)
227        {
228            PBKey pbKey;
229            if (validKey instanceof PBKey)
230            {
231                pbKey = (PBKey) validKey;
232            }
233            else if (validKey instanceof JdbcConnectionDescriptor)
234            {
235                pbKey = ((JdbcConnectionDescriptor) validKey).getPBKey();
236            }
237            else
238            {
239                throw new MetadataException("Could not remove descriptor, given object was no vaild key: " +
240                        validKey);
241            }
242            Object removed = null;
243            synchronized (jcdMap)
244            {
245                removed = jcdMap.remove(pbKey);
246                jcdAliasToPBKeyMap.remove(pbKey.getAlias());
247            }
248            log.info("Remove descriptor: " + removed);
249        }
250    
251        /**
252         * Return a deep copy of all managed {@link JdbcConnectionDescriptor}.
253         */
254        public List getAllDescriptor()
255        {
256            return (List) SerializationUtils.clone(new ArrayList(jcdMap.values()));
257        }
258    
259        public String toXML()
260        {
261            String eol = SystemUtils.LINE_SEPARATOR;
262            // use copy to avoid sync problems
263            HashMap map = (HashMap) jcdMap.clone();
264            StringBuffer buf = new StringBuffer();
265            Iterator it = map.values().iterator();
266            while (it.hasNext())
267            {
268                JdbcConnectionDescriptor jcd = (JdbcConnectionDescriptor) it.next();
269                buf.append(jcd.toXML());
270                buf.append(eol);
271            }
272            return buf.toString();
273        }
274    }