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