001    /**
002     * Copyright (c) 2000-2013 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.events.StartupHelperUtil;
020    import com.liferay.portal.kernel.dao.db.DB;
021    import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
022    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
023    import com.liferay.portal.kernel.dao.jdbc.DataSourceFactoryUtil;
024    import com.liferay.portal.kernel.dao.orm.QueryUtil;
025    import com.liferay.portal.kernel.log.Log;
026    import com.liferay.portal.kernel.log.LogFactoryUtil;
027    import com.liferay.portal.kernel.servlet.PluginContextListener;
028    import com.liferay.portal.kernel.servlet.ServletContextPool;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.StringUtil;
031    import com.liferay.portal.kernel.util.Tuple;
032    import com.liferay.portal.model.ModelHintsUtil;
033    import com.liferay.portal.model.ServiceComponent;
034    import com.liferay.portal.service.ServiceComponentLocalServiceUtil;
035    import com.liferay.portal.spring.hibernate.DialectDetector;
036    import com.liferay.portal.upgrade.util.Table;
037    import com.liferay.portal.util.ClassLoaderUtil;
038    import com.liferay.portal.util.MaintenanceUtil;
039    import com.liferay.portal.util.ShutdownUtil;
040    
041    import java.lang.reflect.Field;
042    
043    import java.sql.Connection;
044    
045    import java.util.HashSet;
046    import java.util.LinkedHashMap;
047    import java.util.List;
048    import java.util.Map;
049    import java.util.Set;
050    
051    import javax.servlet.ServletContext;
052    
053    import javax.sql.DataSource;
054    
055    import org.hibernate.dialect.Dialect;
056    
057    /**
058     * @author Alexander Chow
059     */
060    public class ConvertDatabase extends ConvertProcess {
061    
062            @Override
063            public String getDescription() {
064                    return "migrate-data-from-one-database-to-another";
065            }
066    
067            @Override
068            public String getParameterDescription() {
069                    return "please-enter-jdbc-information-for-new-database";
070            }
071    
072            @Override
073            public String[] getParameterNames() {
074                    return new String[] {
075                            "jdbc-driver-class-name", "jdbc-url", "jdbc-user-name",
076                            "jdbc-password"
077                    };
078            }
079    
080            @Override
081            public boolean isEnabled() {
082                    return true;
083            }
084    
085            @Override
086            protected void doConvert() throws Exception {
087                    DataSource dataSource = getDataSource();
088    
089                    Dialect dialect = DialectDetector.getDialect(dataSource);
090    
091                    DB db = DBFactoryUtil.getDB(dialect);
092    
093                    List<String> modelNames = ModelHintsUtil.getModels();
094    
095                    Map<String, Tuple> tableDetails = new LinkedHashMap<String, Tuple>();
096    
097                    Connection connection = dataSource.getConnection();
098    
099                    try {
100                            MaintenanceUtil.appendStatus(
101                                    "Collecting information for database tables to migration");
102    
103                            for (String modelName : modelNames) {
104                                    if (!modelName.contains(".model.")) {
105                                            continue;
106                                    }
107    
108                                    String implClassName = modelName.replaceFirst(
109                                            "(\\.model\\.)(\\p{Upper}.*)", "$1impl.$2Impl");
110    
111                                    if (_log.isDebugEnabled()) {
112                                            _log.debug("Loading class " + implClassName);
113                                    }
114    
115                                    Class<?> implClass = getImplClass(implClassName);
116    
117                                    if (implClass == null) {
118                                            _log.error("Unable to load class " + implClassName);
119    
120                                            continue;
121                                    }
122    
123                                    Field[] fields = implClass.getFields();
124    
125                                    for (Field field : fields) {
126                                            Tuple tuple = null;
127    
128                                            String fieldName = field.getName();
129    
130                                            if (fieldName.equals("TABLE_NAME") ||
131                                                    (fieldName.startsWith("MAPPING_TABLE_") &&
132                                                     fieldName.endsWith("_NAME"))) {
133    
134                                                    tuple = getTableDetails(implClass, field, fieldName);
135                                            }
136    
137                                            if (tuple != null) {
138                                                    String table = (String)tuple.getObject(0);
139    
140                                                    tableDetails.put(table, tuple);
141                                            }
142                                    }
143                            }
144    
145                            for (Tuple tuple : _UNMAPPED_TABLES) {
146                                    String table = (String)tuple.getObject(0);
147    
148                                    tableDetails.put(table, tuple);
149                            }
150    
151                            if (_log.isDebugEnabled()) {
152                                    _log.debug("Migrating database tables");
153                            }
154    
155                            int i = 0;
156    
157                            for (Tuple tuple : tableDetails.values()) {
158                                    if ((i > 0) && (i % (tableDetails.size() / 4) == 0)) {
159                                            MaintenanceUtil.appendStatus(
160                                                    (i * 100 / tableDetails.size()) + "%");
161                                    }
162    
163                                    String table = (String)tuple.getObject(0);
164                                    Object[][] columns = (Object[][])tuple.getObject(1);
165                                    String sqlCreate = (String)tuple.getObject(2);
166    
167                                    migrateTable(db, connection, table, columns, sqlCreate);
168    
169                                    i++;
170                            }
171    
172                            if (_log.isDebugEnabled()) {
173                                    _log.debug("Migrating database indexes");
174                            }
175    
176                            StartupHelperUtil.updateIndexes(db, connection, false);
177    
178                            List<ServiceComponent> serviceComponents =
179                                    ServiceComponentLocalServiceUtil.getServiceComponents(
180                                            QueryUtil.ALL_POS, QueryUtil.ALL_POS);
181    
182                            Set<String> validIndexNames = new HashSet<String>();
183    
184                            for (ServiceComponent serviceComponent : serviceComponents) {
185                                    String indexesSQL = serviceComponent.getIndexesSQL();
186    
187                                    db.addIndexes(connection, indexesSQL, validIndexNames);
188                            }
189                    }
190                    finally {
191                            DataAccess.cleanUp(connection);
192                    }
193    
194                    MaintenanceUtil.appendStatus(
195                            "Please change your JDBC settings before restarting server");
196    
197                    ShutdownUtil.shutdown(0);
198            }
199    
200            protected DataSource getDataSource() throws Exception {
201                    String[] values = getParameterValues();
202    
203                    String driverClassName = values[0];
204                    String url = values[1];
205                    String userName = values[2];
206                    String password = values[3];
207                    String jndiName = StringPool.BLANK;
208    
209                    return DataSourceFactoryUtil.initDataSource(
210                            driverClassName, url, userName, password, jndiName);
211            }
212    
213            protected Class<?> getImplClass(String implClassName) throws Exception {
214                    try {
215                            ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
216    
217                            return classLoader.loadClass(implClassName);
218                    }
219                    catch (Exception e) {
220                    }
221    
222                    for (String servletContextName : ServletContextPool.keySet()) {
223                            try {
224                                    ServletContext servletContext = ServletContextPool.get(
225                                            servletContextName);
226    
227                                    ClassLoader classLoader =
228                                            (ClassLoader)servletContext.getAttribute(
229                                                    PluginContextListener.PLUGIN_CLASS_LOADER);
230    
231                                    return classLoader.loadClass(implClassName);
232                            }
233                            catch (Exception e) {
234                            }
235                    }
236    
237                    return null;
238            }
239    
240            protected Tuple getTableDetails(
241                    Class<?> implClass, Field tableField, String tableFieldVar) {
242    
243                    try {
244                            String columnsFieldVar = StringUtil.replace(
245                                    tableFieldVar, "_NAME", "_COLUMNS");
246                            String sqlCreateFieldVar = StringUtil.replace(
247                                    tableFieldVar, "_NAME", "_SQL_CREATE");
248    
249                            Field columnsField = implClass.getField(columnsFieldVar);
250                            Field sqlCreateField = implClass.getField(sqlCreateFieldVar);
251    
252                            String table = (String)tableField.get(StringPool.BLANK);
253                            Object[][] columns = (Object[][])columnsField.get(new Object[0][0]);
254                            String sqlCreate = (String)sqlCreateField.get(StringPool.BLANK);
255    
256                            return new Tuple(table, columns, sqlCreate);
257                    }
258                    catch (Exception e) {
259                    }
260    
261                    return null;
262            }
263    
264            protected void migrateTable(
265                            DB db, Connection connection, String tableName, Object[][] columns,
266                            String sqlCreate)
267                    throws Exception {
268    
269                    Table table = new Table(tableName, columns);
270    
271                    String tempFileName = table.generateTempFile();
272    
273                    db.runSQL(connection, sqlCreate);
274    
275                    if (tempFileName != null) {
276                            table.populateTable(tempFileName, connection);
277                    }
278            }
279    
280            private static final Tuple[] _UNMAPPED_TABLES = new Tuple[] {
281                    new Tuple(
282                            CyrusUser.TABLE_NAME, CyrusUser.TABLE_COLUMNS,
283                            CyrusUser.TABLE_SQL_CREATE),
284                    new Tuple(
285                            CyrusVirtual.TABLE_NAME, CyrusVirtual.TABLE_COLUMNS,
286                            CyrusVirtual.TABLE_SQL_CREATE)
287            };
288    
289            private static Log _log = LogFactoryUtil.getLog(ConvertDatabase.class);
290    
291    }