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> buildNamespaces = new HashSet<String>();
183                            Set<String> validIndexNames = new HashSet<String>();
184    
185                            for (ServiceComponent serviceComponent : serviceComponents) {
186                                    if (!buildNamespaces.contains(
187                                                    serviceComponent.getBuildNamespace())) {
188    
189                                            String indexesSQL = serviceComponent.getIndexesSQL();
190    
191                                            db.addIndexes(connection, indexesSQL, validIndexNames);
192    
193                                            buildNamespaces.add(serviceComponent.getBuildNamespace());
194                                    }
195                            }
196                    }
197                    finally {
198                            DataAccess.cleanUp(connection);
199                    }
200    
201                    MaintenanceUtil.appendStatus(
202                            "Please change your JDBC settings before restarting server");
203    
204                    ShutdownUtil.shutdown(0);
205            }
206    
207            protected DataSource getDataSource() throws Exception {
208                    String[] values = getParameterValues();
209    
210                    String driverClassName = values[0];
211                    String url = values[1];
212                    String userName = values[2];
213                    String password = values[3];
214                    String jndiName = StringPool.BLANK;
215    
216                    return DataSourceFactoryUtil.initDataSource(
217                            driverClassName, url, userName, password, jndiName);
218            }
219    
220            protected Class<?> getImplClass(String implClassName) throws Exception {
221                    try {
222                            ClassLoader classLoader = ClassLoaderUtil.getPortalClassLoader();
223    
224                            return classLoader.loadClass(implClassName);
225                    }
226                    catch (Exception e) {
227                    }
228    
229                    for (String servletContextName : ServletContextPool.keySet()) {
230                            try {
231                                    ServletContext servletContext = ServletContextPool.get(
232                                            servletContextName);
233    
234                                    ClassLoader classLoader =
235                                            (ClassLoader)servletContext.getAttribute(
236                                                    PluginContextListener.PLUGIN_CLASS_LOADER);
237    
238                                    return classLoader.loadClass(implClassName);
239                            }
240                            catch (Exception e) {
241                            }
242                    }
243    
244                    return null;
245            }
246    
247            protected Tuple getTableDetails(
248                    Class<?> implClass, Field tableField, String tableFieldVar) {
249    
250                    try {
251                            String columnsFieldVar = StringUtil.replace(
252                                    tableFieldVar, "_NAME", "_COLUMNS");
253                            String sqlCreateFieldVar = StringUtil.replace(
254                                    tableFieldVar, "_NAME", "_SQL_CREATE");
255    
256                            Field columnsField = implClass.getField(columnsFieldVar);
257                            Field sqlCreateField = implClass.getField(sqlCreateFieldVar);
258    
259                            String table = (String)tableField.get(StringPool.BLANK);
260                            Object[][] columns = (Object[][])columnsField.get(new Object[0][0]);
261                            String sqlCreate = (String)sqlCreateField.get(StringPool.BLANK);
262    
263                            return new Tuple(table, columns, sqlCreate);
264                    }
265                    catch (Exception e) {
266                    }
267    
268                    return null;
269            }
270    
271            protected void migrateTable(
272                            DB db, Connection connection, String tableName, Object[][] columns,
273                            String sqlCreate)
274                    throws Exception {
275    
276                    Table table = new Table(tableName, columns);
277    
278                    try {
279                            String tempFileName = table.generateTempFile();
280    
281                            db.runSQL(connection, sqlCreate);
282    
283                            if (tempFileName != null) {
284                                    table.populateTable(tempFileName, connection);
285                            }
286                    }
287                    catch (Exception e) {
288                            _log.error(e, e);
289    
290                            MaintenanceUtil.appendStatus(e.getMessage());
291                    }
292            }
293    
294            private static final Tuple[] _UNMAPPED_TABLES = new Tuple[] {
295                    new Tuple(
296                            CyrusUser.TABLE_NAME, CyrusUser.TABLE_COLUMNS,
297                            CyrusUser.TABLE_SQL_CREATE),
298                    new Tuple(
299                            CyrusVirtual.TABLE_NAME, CyrusVirtual.TABLE_COLUMNS,
300                            CyrusVirtual.TABLE_SQL_CREATE)
301            };
302    
303            private static Log _log = LogFactoryUtil.getLog(ConvertDatabase.class);
304    
305    }