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