001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
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.dao.jdbc.util.DataSourceFactoryBean;
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.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.servlet.PortletServlet;
026    import com.liferay.portal.kernel.servlet.ServletContextPool;
027    import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
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.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.ArrayList;
042    import java.util.List;
043    import java.util.Properties;
044    
045    import javax.servlet.ServletContext;
046    
047    import javax.sql.DataSource;
048    
049    import org.hibernate.dialect.Dialect;
050    
051    /**
052     * @author Alexander Chow
053     */
054    public class ConvertDatabase extends ConvertProcess {
055    
056            public String getDescription() {
057                    return "migrate-data-from-one-database-to-another";
058            }
059    
060            public String getParameterDescription() {
061                    return "please-enter-jdbc-information-for-new-database";
062            }
063    
064            public String[] getParameterNames() {
065                    return new String[] {
066                            "jdbc-driver-class-name", "jdbc-url", "jdbc-user-name",
067                            "jdbc-password"
068                    };
069            }
070    
071            public boolean isEnabled() {
072                    return true;
073            }
074    
075            protected void doConvert() throws Exception {
076                    DataSource dataSource = getDataSource();
077    
078                    Dialect dialect = DialectDetector.getDialect(dataSource);
079    
080                    DB db = DBFactoryUtil.getDB(dialect);
081    
082                    List<String> modelNames = ModelHintsUtil.getModels();
083    
084                    List<Tuple> tableDetails = new ArrayList<Tuple>();
085    
086                    Connection connection = dataSource.getConnection();
087    
088                    try {
089                            MaintenanceUtil.appendStatus(
090                                    "Collecting information for database tables to migration");
091    
092                            for (String modelName : modelNames) {
093                                    if (!modelName.contains(".model.")) {
094                                            continue;
095                                    }
096    
097                                    String implClassName = modelName.replaceFirst(
098                                            "(\\.model\\.)(\\p{Upper}.*)", "$1impl.$2Impl");
099    
100                                    if (_log.isDebugEnabled()) {
101                                            _log.debug("Loading class " + implClassName);
102                                    }
103    
104                                    Class<?> implClass = getImplClass(implClassName);
105    
106                                    if (implClass == null) {
107                                            _log.error("Unable to load class " + implClassName);
108    
109                                            continue;
110                                    }
111    
112                                    Field[] fields = implClass.getFields();
113    
114                                    for (Field field : fields) {
115                                            Tuple tuple = null;
116    
117                                            String fieldName = field.getName();
118    
119                                            if (fieldName.equals("TABLE_NAME")) {
120                                                    tuple = getTableDetails(implClass, field, fieldName);
121                                            }
122                                            else if (fieldName.startsWith("MAPPING_TABLE_") &&
123                                                             fieldName.endsWith("_NAME")) {
124    
125                                                    tuple = getTableDetails(implClass, field, fieldName);
126                                            }
127    
128                                            if (tuple != null) {
129                                                    tableDetails.add(tuple);
130                                            }
131                                    }
132                            }
133    
134                            for (Tuple tuple : _UNMAPPED_TABLES) {
135                                    tableDetails.add(tuple);
136                            }
137    
138                            if (_log.isDebugEnabled()) {
139                                    _log.debug("Migrating database tables");
140                            }
141    
142                            for (int i = 0; i < tableDetails.size(); i++) {
143                                    if ((i > 0) && (i % (tableDetails.size() / 4) == 0)) {
144                                            MaintenanceUtil.appendStatus(
145                                                     (i * 100 / tableDetails.size()) + "%");
146                                    }
147    
148                                    Tuple tuple = tableDetails.get(i);
149    
150                                    String table = (String)tuple.getObject(0);
151                                    Object[][] columns = (Object[][])tuple.getObject(1);
152                                    String sqlCreate = (String)tuple.getObject(2);
153    
154                                    migrateTable(db, connection, table, columns, sqlCreate);
155                            }
156                    }
157                    finally {
158                            DataAccess.cleanUp(connection);
159                    }
160    
161                    MaintenanceUtil.appendStatus(
162                            "Please change your JDBC settings before restarting server");
163    
164                    ShutdownUtil.shutdown(0);
165            }
166    
167            protected DataSource getDataSource() throws Exception {
168                    String[] values = getParameterValues();
169    
170                    String jdbcDriverClassName = values[0];
171                    String jdbcURL = values[1];
172                    String jdbcUserName = values[2];
173                    String jdbcPassword = values[3];
174    
175                    Properties properties = new Properties();
176    
177                    properties.setProperty(
178                            _JDBC_PREFIX + "driverClassName", jdbcDriverClassName);
179                    properties.setProperty(_JDBC_PREFIX + "url", jdbcURL);
180                    properties.setProperty(_JDBC_PREFIX + "username", jdbcUserName);
181                    properties.setProperty(_JDBC_PREFIX + "password", jdbcPassword);
182    
183                    DataSourceFactoryBean dataSourceFactory = new DataSourceFactoryBean();
184    
185                    dataSourceFactory.setProperties(properties);
186                    dataSourceFactory.setPropertyPrefix(_JDBC_PREFIX);
187    
188                    return dataSourceFactory.createInstance();
189            }
190    
191            public Class<?> getImplClass(String implClassName) throws Exception {
192                    try {
193                            ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
194    
195                            return classLoader.loadClass(implClassName);
196                    }
197                    catch (Exception e) {
198                    }
199    
200                    for (String servletContextName : ServletContextPool.keySet()) {
201                            try {
202                                    ServletContext servletContext = ServletContextPool.get(
203                                            servletContextName);
204    
205                                    ClassLoader classLoader =
206                                            (ClassLoader)servletContext.getAttribute(
207                                                    PortletServlet.PORTLET_CLASS_LOADER);
208    
209                                    return classLoader.loadClass(implClassName);
210                            }
211                            catch (Exception e) {
212                            }
213                    }
214    
215                    return null;
216            }
217    
218            protected Tuple getTableDetails(
219                    Class<?> implClass, Field tableField, String tableFieldVar) {
220    
221                    try {
222                            String columnsFieldVar = StringUtil.replace(
223                                    tableFieldVar, "_NAME", "_COLUMNS");
224                            String sqlCreateFieldVar = StringUtil.replace(
225                                    tableFieldVar, "_NAME", "_SQL_CREATE");
226    
227                            Field columnsField = implClass.getField(columnsFieldVar);
228                            Field sqlCreateField = implClass.getField(sqlCreateFieldVar);
229    
230                            String table = (String)tableField.get(StringPool.BLANK);
231                            Object[][] columns = (Object[][])columnsField.get(new Object[0][0]);
232                            String sqlCreate = (String)sqlCreateField.get(StringPool.BLANK);
233    
234                            return new Tuple(table, columns, sqlCreate);
235                    }
236                    catch (Exception e) {
237                    }
238    
239                    return null;
240            }
241    
242            protected void migrateTable(
243                            DB db, Connection connection, String tableName, Object[][] columns,
244                            String sqlCreate)
245                    throws Exception {
246    
247                    Table table = new Table(tableName, columns);
248    
249                    String tempFileName = table.generateTempFile();
250    
251                    db.runSQL(connection, sqlCreate);
252    
253                    if (tempFileName != null) {
254                            table.populateTable(tempFileName, connection);
255                    }
256            }
257    
258            private static final String _JDBC_PREFIX = "jdbc.upgrade.";
259    
260            private static final Tuple[] _UNMAPPED_TABLES = new Tuple[] {
261                    new Tuple(
262                            CyrusUser.TABLE_NAME, CyrusUser.TABLE_COLUMNS,
263                            CyrusUser.TABLE_SQL_CREATE),
264                    new Tuple(
265                            CyrusVirtual.TABLE_NAME, CyrusVirtual.TABLE_COLUMNS,
266                            CyrusVirtual.TABLE_SQL_CREATE)
267            };
268    
269            private static Log _log = LogFactoryUtil.getLog(ConvertDatabase.class);
270    
271    }