001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.convert;
016    
017    import com.liferay.mail.model.CyrusUser;
018    import com.liferay.mail.model.CyrusVirtual;
019    import com.liferay.portal.kernel.dao.db.DB;
020    import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
021    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
022    import com.liferay.portal.kernel.dao.jdbc.DataSourceFactoryUtil;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.servlet.PortletServlet;
026    import com.liferay.portal.kernel.servlet.ServletContextPool;
027    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.StringUtil;
030    import com.liferay.portal.kernel.util.Tuple;
031    import com.liferay.portal.model.ModelHintsUtil;
032    import com.liferay.portal.spring.hibernate.DialectDetector;
033    import com.liferay.portal.upgrade.util.Table;
034    import com.liferay.portal.util.MaintenanceUtil;
035    import com.liferay.portal.util.ShutdownUtil;
036    
037    import java.lang.reflect.Field;
038    
039    import java.sql.Connection;
040    
041    import java.util.LinkedHashMap;
042    import java.util.List;
043    import java.util.Map;
044    
045    import javax.servlet.ServletContext;
046    
047    import javax.sql.DataSource;
048    
049    import org.hibernate.dialect.Dialect;
050    
051    /**
052     * @author Alexander Chow
053     */
054    public class ConvertDatabase extends ConvertProcess {
055    
056            @Override
057            public String getDescription() {
058                    return "migrate-data-from-one-database-to-another";
059            }
060    
061            @Override
062            public String getParameterDescription() {
063                    return "please-enter-jdbc-information-for-new-database";
064            }
065    
066            @Override
067            public String[] getParameterNames() {
068                    return new String[] {
069                            "jdbc-driver-class-name", "jdbc-url", "jdbc-user-name",
070                            "jdbc-password"
071                    };
072            }
073    
074            @Override
075            public boolean isEnabled() {
076                    return true;
077            }
078    
079            @Override
080            protected void doConvert() throws Exception {
081                    DataSource dataSource = getDataSource();
082    
083                    Dialect dialect = DialectDetector.getDialect(dataSource);
084    
085                    DB db = DBFactoryUtil.getDB(dialect);
086    
087                    List<String> modelNames = ModelHintsUtil.getModels();
088    
089                    Map<String, Tuple> tableDetails = new LinkedHashMap<String, Tuple>();
090    
091                    Connection connection = dataSource.getConnection();
092    
093                    try {
094                            MaintenanceUtil.appendStatus(
095                                    "Collecting information for database tables to migration");
096    
097                            for (String modelName : modelNames) {
098                                    if (!modelName.contains(".model.")) {
099                                            continue;
100                                    }
101    
102                                    String implClassName = modelName.replaceFirst(
103                                            "(\\.model\\.)(\\p{Upper}.*)", "$1impl.$2Impl");
104    
105                                    if (_log.isDebugEnabled()) {
106                                            _log.debug("Loading class " + implClassName);
107                                    }
108    
109                                    Class<?> implClass = getImplClass(implClassName);
110    
111                                    if (implClass == null) {
112                                            _log.error("Unable to load class " + implClassName);
113    
114                                            continue;
115                                    }
116    
117                                    Field[] fields = implClass.getFields();
118    
119                                    for (Field field : fields) {
120                                            Tuple tuple = null;
121    
122                                            String fieldName = field.getName();
123    
124                                            if (fieldName.equals("TABLE_NAME") ||
125                                                    (fieldName.startsWith("MAPPING_TABLE_") &&
126                                                     fieldName.endsWith("_NAME"))) {
127    
128                                                    tuple = getTableDetails(implClass, field, fieldName);
129                                            }
130    
131                                            if (tuple != null) {
132                                                    String table = (String)tuple.getObject(0);
133    
134                                                    tableDetails.put(table, tuple);
135                                            }
136                                    }
137                            }
138    
139                            for (Tuple tuple : _UNMAPPED_TABLES) {
140                                    String table = (String)tuple.getObject(0);
141    
142                                    tableDetails.put(table, tuple);
143                            }
144    
145                            if (_log.isDebugEnabled()) {
146                                    _log.debug("Migrating database tables");
147                            }
148    
149                            int i = 0;
150    
151                            for (Tuple tuple : tableDetails.values()) {
152                                    if ((i > 0) && (i % (tableDetails.size() / 4) == 0)) {
153                                            MaintenanceUtil.appendStatus(
154                                                    (i * 100 / tableDetails.size()) + "%");
155                                    }
156    
157                                    String table = (String)tuple.getObject(0);
158                                    Object[][] columns = (Object[][])tuple.getObject(1);
159                                    String sqlCreate = (String)tuple.getObject(2);
160    
161                                    migrateTable(db, connection, table, columns, sqlCreate);
162    
163                                    i++;
164                            }
165                    }
166                    finally {
167                            DataAccess.cleanUp(connection);
168                    }
169    
170                    MaintenanceUtil.appendStatus(
171                            "Please change your JDBC settings before restarting server");
172    
173                    ShutdownUtil.shutdown(0);
174            }
175    
176            protected DataSource getDataSource() throws Exception {
177                    String[] values = getParameterValues();
178    
179                    String driverClassName = values[0];
180                    String url = values[1];
181                    String userName = values[2];
182                    String password = values[3];
183    
184                    return DataSourceFactoryUtil.initDataSource(
185                            driverClassName, url, userName, password);
186            }
187    
188            protected Class<?> getImplClass(String implClassName) throws Exception {
189                    try {
190                            ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
191    
192                            return classLoader.loadClass(implClassName);
193                    }
194                    catch (Exception e) {
195                    }
196    
197                    for (String servletContextName : ServletContextPool.keySet()) {
198                            try {
199                                    ServletContext servletContext = ServletContextPool.get(
200                                            servletContextName);
201    
202                                    ClassLoader classLoader =
203                                            (ClassLoader)servletContext.getAttribute(
204                                                    PortletServlet.PORTLET_CLASS_LOADER);
205    
206                                    return classLoader.loadClass(implClassName);
207                            }
208                            catch (Exception e) {
209                            }
210                    }
211    
212                    return null;
213            }
214    
215            protected Tuple getTableDetails(
216                    Class<?> implClass, Field tableField, String tableFieldVar) {
217    
218                    try {
219                            String columnsFieldVar = StringUtil.replace(
220                                    tableFieldVar, "_NAME", "_COLUMNS");
221                            String sqlCreateFieldVar = StringUtil.replace(
222                                    tableFieldVar, "_NAME", "_SQL_CREATE");
223    
224                            Field columnsField = implClass.getField(columnsFieldVar);
225                            Field sqlCreateField = implClass.getField(sqlCreateFieldVar);
226    
227                            String table = (String)tableField.get(StringPool.BLANK);
228                            Object[][] columns = (Object[][])columnsField.get(new Object[0][0]);
229                            String sqlCreate = (String)sqlCreateField.get(StringPool.BLANK);
230    
231                            return new Tuple(table, columns, sqlCreate);
232                    }
233                    catch (Exception e) {
234                    }
235    
236                    return null;
237            }
238    
239            protected void migrateTable(
240                            DB db, Connection connection, String tableName, Object[][] columns,
241                            String sqlCreate)
242                    throws Exception {
243    
244                    Table table = new Table(tableName, columns);
245    
246                    String tempFileName = table.generateTempFile();
247    
248                    db.runSQL(connection, sqlCreate);
249    
250                    if (tempFileName != null) {
251                            table.populateTable(tempFileName, connection);
252                    }
253            }
254    
255            private static final Tuple[] _UNMAPPED_TABLES = new Tuple[] {
256                    new Tuple(
257                            CyrusUser.TABLE_NAME, CyrusUser.TABLE_COLUMNS,
258                            CyrusUser.TABLE_SQL_CREATE),
259                    new Tuple(
260                            CyrusVirtual.TABLE_NAME, CyrusVirtual.TABLE_COLUMNS,
261                            CyrusVirtual.TABLE_SQL_CREATE)
262            };
263    
264            private static Log _log = LogFactoryUtil.getLog(ConvertDatabase.class);
265    
266    }