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