001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  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    package org.apache.geronimo.converter.bea;
018    
019    import java.io.Reader;
020    import java.io.IOException;
021    import java.io.StringReader;
022    import java.util.List;
023    import java.util.ArrayList;
024    import java.util.Map;
025    import java.util.HashMap;
026    import java.util.Properties;
027    import java.util.Iterator;
028    import javax.xml.parsers.DocumentBuilderFactory;
029    import javax.xml.parsers.DocumentBuilder;
030    import javax.xml.parsers.ParserConfigurationException;
031    import org.apache.geronimo.converter.DOMUtils;
032    import org.apache.geronimo.converter.DatabaseConversionStatus;
033    import org.apache.geronimo.converter.JDBCPool;
034    import org.apache.geronimo.converter.XADatabasePool;
035    import org.apache.geronimo.converter.AbstractDatabasePool;
036    import org.apache.geronimo.kernel.util.XmlUtil;
037    import org.w3c.dom.Document;
038    import org.w3c.dom.Element;
039    import org.w3c.dom.NodeList;
040    import org.w3c.dom.Node;
041    import org.xml.sax.InputSource;
042    import org.xml.sax.SAXException;
043    
044    /**
045     * Converts database pools from WebLogic 8.1 to Geronimo
046     *
047     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
048     */
049    public class WebLogic81DatabaseConverter extends DOMUtils {
050        public static DatabaseConversionStatus convert(String libDir, String domainDir) throws IOException {
051            Weblogic81Utils utils = new Weblogic81Utils(libDir, domainDir);
052            String config = utils.getConfigXML();
053            return convert(new StringReader(config));
054        }
055    
056        public static DatabaseConversionStatus convert(Reader configXml) throws IOException {
057            List status = new ArrayList();
058            List noTx = new ArrayList();
059            List local = new ArrayList();
060            List xa = new ArrayList();
061    
062            DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
063            factory.setValidating(false);
064            try {
065                DocumentBuilder builder = factory.newDocumentBuilder();
066                Document doc = builder.parse(new InputSource(configXml));
067                configXml.close();
068                parseDocument(doc, status, local, xa);
069            } catch (ParserConfigurationException e) {
070                throw (IOException)new IOException().initCause(e);
071            } catch (SAXException e) {
072                throw (IOException)new IOException().initCause(e);
073            }
074    
075            DatabaseConversionStatus result = new DatabaseConversionStatus();
076            result.setMessages((String[]) status.toArray(new String[status.size()]));
077            result.setNoTXPools((JDBCPool[]) noTx.toArray(new JDBCPool[noTx.size()]));
078            result.setJdbcPools((JDBCPool[]) local.toArray(new JDBCPool[noTx.size()]));
079            result.setXaPools((XADatabasePool[]) xa.toArray(new XADatabasePool[xa.size()]));
080            return result;
081        }
082    
083        private static void parseDocument(Document doc, List status, List local, List xa) {
084            Element domain = doc.getDocumentElement();
085            if(!domain.getNodeName().equalsIgnoreCase("Domain")) {
086                status.add("ERROR: Unrecognized file beginning with "+domain.getNodeName()+" element.  Expected a WebLogic config.xml file.");
087                return;
088            }
089            NodeList list = domain.getChildNodes();
090            Map pools = new HashMap();
091            for(int i=0; i<list.getLength(); i++) {
092                Node node = list.item(i);
093                if(node.getNodeType() == Node.ELEMENT_NODE) {
094                    String name = node.getNodeName();
095                    if(name.equalsIgnoreCase("JDBCConnectionPool")) {
096                        ConnectionPool pool = getConnectionPool((Element)node, status);
097                        pools.put(pool.getName(), pool);
098                    } else if(name.equalsIgnoreCase("JDBCDataSource")) {
099                        DataSource ds = getDataSource((Element)node, false);
100                        ConnectionPool pool = (ConnectionPool) pools.get(ds.getPoolName());
101                        if(pool != null) {
102                            pool.getDataSources().add(ds);
103                        } else {
104                            status.add("ERROR: Can't find pool for data source '"+ds.getName()+"' ("+ds.getPoolName()+")");
105                        }
106                    } else if(name.equalsIgnoreCase("JDBCTxDataSource")) {
107                        DataSource ds = getDataSource((Element)node, true);
108                        ConnectionPool pool = (ConnectionPool) pools.get(ds.getPoolName());
109                        if(pool != null) {
110                            pool.getDataSources().add(ds);
111                        } else {
112                            status.add("ERROR: Can't find pool for data source '"+ds.getName()+"' ("+ds.getPoolName()+")");
113                        }
114                    } else {
115                        status.add("Skipping element '"+name+"'");
116                    }
117                }
118            }
119            if(pools.size() > 0) {
120                for (Iterator it = pools.values().iterator(); it.hasNext();) {
121                    ConnectionPool pool = (ConnectionPool) it.next();
122                    if(pool.getPassword() != null && pool.getPassword().startsWith("{")) {
123                        status.add("NOTE: When importing from WebLogic, typically database passwords cannot be recovered, and will need to be re-entered.");
124                        break;
125                    }
126                }
127            }
128            processPools((ConnectionPool[]) pools.values().toArray(new ConnectionPool[0]),
129                    status, local, xa);
130        }
131    
132        private static void processPools(ConnectionPool[] pools, List status, List local, List xa) {
133            for (int i = 0; i < pools.length; i++) {
134                ConnectionPool pool = pools[i];
135                boolean isXA;
136                if(pool.hasEmulate()) {
137                    isXA = false;
138                } else if(pool.hasNonTX()) {
139                    isXA = false;
140                } else if(pool.hasXADriverName()) {
141                    isXA = true;
142                } else {
143                    isXA = false;
144                    status.add("Can't tell whether pool '"+pool.getName()+"' is an XA driver or not; will create local transaction pools in Geronimo.");
145                }
146                if(pool.getDataSources().size() == 0) {
147                    status.add("Pool '"+pool.getName()+"' has no associated data sources.  Creating a default pool for it.");
148                    if(isXA) {
149                        xa.add(createXAPool(pool, pool.getName(), null));
150                    } else {
151                        local.add(createJDBCPool(pool, pool.getName(), null));
152                    }
153                } else {
154                    for (int j = 0; j < pool.getDataSources().size(); j++) {
155                        DataSource ds = (DataSource) pool.getDataSources().get(j);
156                        if(isXA) {
157                            xa.add(createXAPool(pool, ds.getName(), ds.getJndiName()));
158                        } else {
159                            local.add(createJDBCPool(pool, ds.getName(), ds.getJndiName()));
160                        }
161                    }
162                }
163            }
164        }
165    
166        private static void populatePool(ConnectionPool pool, AbstractDatabasePool target) {
167            if(pool.getReserveTimeoutSecs() != null) {
168                target.setBlockingTimeoutMillis(new Integer(pool.getReserveTimeoutSecs().intValue()*1000));
169            }
170            if(pool.getIdleTimeoutSecs() != null) {
171                target.setIdleTimeoutMillis(new Integer(pool.getIdleTimeoutSecs().intValue()*1000));
172            }
173            target.setMaxSize(pool.getMax());
174            target.setMinSize(pool.getMin());
175            target.setNewConnectionSQL(pool.getInitSQL());
176            target.setStatementCacheSize(pool.getCacheSize());
177            target.setTestConnectionSQL(pool.getTestTable() == null ? null : "SELECT * FROM "+pool.getTestTable()+" WHERE 0=1");
178            if(pool.getDriverName().toLowerCase().indexOf("oracle") > -1) target.setVendor(JDBCPool.VENDOR_ORACLE);
179            if(pool.getDriverName().toLowerCase().indexOf("mysql") > -1) target.setVendor(JDBCPool.VENDOR_MYSQL);
180            if(pool.getDriverName().toLowerCase().indexOf("sybase") > -1) target.setVendor(JDBCPool.VENDOR_SYBASE);
181            if(pool.getDriverName().toLowerCase().indexOf("informix") > -1) target.setVendor(JDBCPool.VENDOR_INFORMIX);
182        }
183    
184        private static JDBCPool createJDBCPool(ConnectionPool pool, String name, String jndiName) {
185            JDBCPool result = new JDBCPool();
186            result.setName(name);
187            result.setJndiName(jndiName);
188            populatePool(pool, result);
189            result.setConnectionProperties(pool.getProperties());
190            result.setDriverClass(pool.getDriverName());
191            result.setJdbcURL(pool.getUrl());
192            // Don't bother putting encrypted passwords into the pool
193            if(pool.getPassword() != null && !pool.getPassword().startsWith("{")) {
194                result.setPassword(pool.getPassword());
195            }
196            result.setUsername(pool.getUsername());
197            return result;
198        }
199    
200        private static XADatabasePool createXAPool(ConnectionPool pool, String name, String jndiName) {
201            XADatabasePool result = new XADatabasePool();
202            result.setName(name);
203            result.setJndiName(jndiName);
204            populatePool(pool, result);
205            result.setXaDataSourceClass(pool.getDriverName());
206            result.setProperties(pool.getProperties());
207            return result;
208        }
209    
210        private static DataSource getDataSource(Element root, boolean tx) {
211            DataSource ds = new DataSource();
212            ds.setDeclaredAsTX(tx);
213            ds.setEmulate(getBoolean(root.getAttribute("EnableTwoPhaseCommit"), false));
214            ds.setName(root.getAttribute("Name"));
215            ds.setJndiName(root.getAttribute("JNDIName"));
216            ds.setPoolName(root.getAttribute("PoolName"));
217            return ds;
218        }
219    
220        private static boolean getBoolean(String value, boolean defaultResult) {
221            if(value == null) {
222                return defaultResult;
223            }
224            return new Boolean(value).booleanValue();
225        }
226    
227        private static ConnectionPool getConnectionPool(Element root, List status) {
228            ConnectionPool pool = new ConnectionPool();
229            pool.setName(root.getAttribute("Name"));
230            pool.setDriverName(root.getAttribute("DriverName"));
231            pool.setUrl(root.getAttribute("URL"));
232            pool.setMin(getInteger(root.getAttribute("InitialCapacity")));
233            pool.setMax(getInteger(root.getAttribute("MaxCapacity")));
234            readProperties(pool.getProperties(), root.getAttribute("Properties"), status);
235            pool.setUsername(pool.getProperties().getProperty("user"));
236            pool.getProperties().remove("user");
237            if(root.hasAttribute("Password")) {
238                pool.setPassword(root.getAttribute("Password"));
239            } else if(root.hasAttribute("PasswordEncrypted")) {
240                pool.setPassword(root.getAttribute("PasswordEncrypted"));
241            }
242            pool.setReserveTimeoutSecs(getInteger(root.getAttribute("ConnectionReserveTimeoutSeconds")));
243            pool.setIdleTimeoutSecs(getInteger(root.getAttribute("InactiveConnectionTimeoutSeconds")));
244            pool.setCacheSize(getInteger(root.getAttribute("StatementCacheSize")));
245            pool.setInitSQL(root.getAttribute("InitSQL"));
246            pool.setTestTable(root.getAttribute("TestTableName"));
247            return pool;
248        }
249    
250        private static void readProperties(Properties props, String value, List status) {
251            if(value == null) {
252                return;
253            }
254            value = value.trim();
255            if(value.equals("")) {
256                return;
257            }
258            int last = -1;
259            int pos = value.indexOf(';');
260            while(pos > -1) {
261                String s = value.substring(last+1, pos);
262                int eq = s.indexOf('=');
263                if(eq > -1) {
264                    props.setProperty(s.substring(0, eq), s.substring(eq+1));
265                } else {
266                    status.add("WARN: Unable to read property '"+s+"'");
267                }
268                last = pos;
269                pos = value.indexOf(';', pos+1);
270            }
271            String s = value.substring(last+1);
272            int eq = s.indexOf('=');
273            if(eq > -1) {
274                props.setProperty(s.substring(0, eq), s.substring(eq+1));
275            } else {
276                status.add("WARN: Unable to read property '"+s+"'");
277            }
278        }
279    
280        private static Integer getInteger(String value) {
281            if(value == null) {
282                return null;
283            }
284            value = value.trim();
285            if(value.equals("")) {
286                return null;
287            }
288            return new Integer(value);
289        }
290    
291        public static class DataSource {
292            private String name;
293            private String poolName;
294            private String jndiName;
295            private boolean emulate;
296            private boolean declaredAsTX;
297    
298            public String getName() {
299                return name;
300            }
301    
302            public void setName(String name) {
303                this.name = name;
304            }
305    
306            public String getPoolName() {
307                return poolName;
308            }
309    
310            public void setPoolName(String poolName) {
311                this.poolName = poolName;
312            }
313    
314            public String getJndiName() {
315                return jndiName;
316            }
317    
318            public void setJndiName(String jndiName) {
319                this.jndiName = jndiName;
320            }
321    
322            public boolean isEmulate() {
323                return emulate;
324            }
325    
326            public void setEmulate(boolean emulate) {
327                this.emulate = emulate;
328            }
329    
330            public boolean isDeclaredAsTX() {
331                return declaredAsTX;
332            }
333    
334            public void setDeclaredAsTX(boolean declaredAsTX) {
335                this.declaredAsTX = declaredAsTX;
336            }
337        }
338    
339        public static class ConnectionPool {
340            private String name;
341            private String driverName;
342            private Integer min, max;
343            private String url;
344            private String username;
345            private String password;
346            private Integer reserveTimeoutSecs;
347            private Integer idleTimeoutSecs;
348            private Integer cacheSize;
349            private String initSQL;
350            private String testTable;
351            private Properties properties = new Properties();
352            private List dataSources = new ArrayList();
353    
354            public boolean hasEmulate() {
355                for (int i = 0; i < dataSources.size(); i++) {
356                    DataSource ds = (DataSource) dataSources.get(i);
357                    if(ds.isEmulate()) {
358                        return true;
359                    }
360                }
361                return false;
362            }
363    
364            public boolean hasNonTX() {
365                for (int i = 0; i < dataSources.size(); i++) {
366                    DataSource ds = (DataSource) dataSources.get(i);
367                    if(!ds.isDeclaredAsTX()) {
368                        return true;
369                    }
370                }
371                return false;
372            }
373    
374            public boolean hasXADriverName() {
375                return driverName.toUpperCase().indexOf("XA") > -1;
376            }
377    
378            public String getName() {
379                return name;
380            }
381    
382            public void setName(String name) {
383                this.name = name;
384            }
385    
386            public String getDriverName() {
387                return driverName;
388            }
389    
390            public void setDriverName(String driverName) {
391                this.driverName = driverName;
392            }
393    
394            public String getUsername() {
395                return username;
396            }
397    
398            public void setUsername(String username) {
399                this.username = username;
400            }
401    
402            public String getPassword() {
403                return password;
404            }
405    
406            public void setPassword(String password) {
407                this.password = password;
408            }
409    
410            public String getInitSQL() {
411                return initSQL;
412            }
413    
414            public void setInitSQL(String initSQL) {
415                this.initSQL = initSQL;
416            }
417    
418            public String getTestTable() {
419                return testTable;
420            }
421    
422            public void setTestTable(String testTable) {
423                this.testTable = testTable;
424            }
425    
426            public Properties getProperties() {
427                return properties;
428            }
429    
430            public void setProperties(Properties properties) {
431                this.properties = properties;
432            }
433    
434            public List getDataSources() {
435                return dataSources;
436            }
437    
438            public void setDataSources(List dataSources) {
439                this.dataSources = dataSources;
440            }
441    
442            public Integer getMin() {
443                return min;
444            }
445    
446            public void setMin(Integer min) {
447                this.min = min;
448            }
449    
450            public Integer getMax() {
451                return max;
452            }
453    
454            public void setMax(Integer max) {
455                this.max = max;
456            }
457    
458            public Integer getReserveTimeoutSecs() {
459                return reserveTimeoutSecs;
460            }
461    
462            public void setReserveTimeoutSecs(Integer reserveTimeoutSecs) {
463                this.reserveTimeoutSecs = reserveTimeoutSecs;
464            }
465    
466            public Integer getIdleTimeoutSecs() {
467                return idleTimeoutSecs;
468            }
469    
470            public void setIdleTimeoutSecs(Integer idleTimeoutSecs) {
471                this.idleTimeoutSecs = idleTimeoutSecs;
472            }
473    
474            public Integer getCacheSize() {
475                return cacheSize;
476            }
477    
478            public void setCacheSize(Integer cacheSize) {
479                this.cacheSize = cacheSize;
480            }
481    
482            public String getUrl() {
483                return url;
484            }
485    
486            public void setUrl(String url) {
487                this.url = url;
488            }
489        }
490    }