1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
13   */
14  
15  package com.liferay.portal.convert;
16  
17  import com.liferay.counter.model.Counter;
18  import com.liferay.mail.model.CyrusUser;
19  import com.liferay.mail.model.CyrusVirtual;
20  import com.liferay.portal.dao.jdbc.util.DataSourceFactoryBean;
21  import com.liferay.portal.kernel.dao.db.DB;
22  import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
23  import com.liferay.portal.kernel.dao.jdbc.DataAccess;
24  import com.liferay.portal.kernel.log.Log;
25  import com.liferay.portal.kernel.log.LogFactoryUtil;
26  import com.liferay.portal.kernel.servlet.PortletServlet;
27  import com.liferay.portal.kernel.servlet.ServletContextPool;
28  import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.kernel.util.StringUtil;
31  import com.liferay.portal.kernel.util.Tuple;
32  import com.liferay.portal.model.ModelHintsUtil;
33  import com.liferay.portal.spring.hibernate.DialectDetector;
34  import com.liferay.portal.upgrade.util.Table;
35  import com.liferay.portal.util.MaintenanceUtil;
36  import com.liferay.portal.util.ShutdownUtil;
37  
38  import java.lang.reflect.Field;
39  
40  import java.sql.Connection;
41  
42  import java.util.ArrayList;
43  import java.util.List;
44  import java.util.Properties;
45  
46  import javax.servlet.ServletContext;
47  
48  import javax.sql.DataSource;
49  
50  import org.hibernate.dialect.Dialect;
51  
52  /**
53   * <a href="ConvertDatabase.java.html"><b><i>View Source</i></b></a>
54   *
55   * @author Alexander Chow
56   */
57  public class ConvertDatabase extends ConvertProcess {
58  
59      public String getDescription() {
60          return "migrate-data-from-one-database-to-another";
61      }
62  
63      public String getParameterDescription() {
64          return "please-enter-jdbc-information-for-new-database";
65      }
66  
67      public String[] getParameterNames() {
68          return new String[] {
69              "jdbc-driver-class-name", "jdbc-url", "jdbc-user-name",
70              "jdbc-password"
71          };
72      }
73  
74      public boolean isEnabled() {
75          return true;
76      }
77  
78      protected void doConvert() throws Exception {
79          DataSource dataSource = getDataSource();
80  
81          Dialect dialect = DialectDetector.getDialect(dataSource);
82  
83          DB db = DBFactoryUtil.getDB(dialect);
84  
85          List<String> modelNames = ModelHintsUtil.getModels();
86  
87          List<Tuple> tableDetails = new ArrayList<Tuple>();
88  
89          Connection connection = dataSource.getConnection();
90  
91          try {
92              MaintenanceUtil.appendStatus(
93                  "Collecting information for database tables to migration");
94  
95              for (String modelName : modelNames) {
96                  if (!modelName.contains(".model.")) {
97                      continue;
98                  }
99  
100                 String implClassName = modelName.replaceFirst(
101                     "(\\.model\\.)(\\p{Upper}.*)", "$1impl.$2Impl");
102 
103                 if (_log.isDebugEnabled()) {
104                     _log.debug("Loading class " + implClassName);
105                 }
106 
107                 Class<?> implClass = getImplClass(implClassName);
108 
109                 if (implClass == null) {
110                     _log.error("Unable to load class " + implClassName);
111 
112                     continue;
113                 }
114 
115                 Field[] fields = implClass.getFields();
116 
117                 for (Field field : fields) {
118                     Tuple tuple = null;
119 
120                     String fieldName = field.getName();
121 
122                     if (fieldName.equals("TABLE_NAME")) {
123                         tuple = getTableDetails(implClass, field, fieldName);
124                     }
125                     else if (fieldName.startsWith("MAPPING_TABLE_") &&
126                              fieldName.endsWith("_NAME")) {
127 
128                         tuple = getTableDetails(implClass, field, fieldName);
129                     }
130 
131                     if (tuple != null) {
132                         tableDetails.add(tuple);
133                     }
134                 }
135             }
136 
137             for (Tuple tuple : _UNMAPPED_TABLES) {
138                 tableDetails.add(tuple);
139             }
140 
141             if (_log.isDebugEnabled()) {
142                 _log.debug("Migrating database tables");
143             }
144 
145             for (int i = 0; i < tableDetails.size(); i++) {
146                 if ((i > 0) && (i % (tableDetails.size() / 4) == 0)) {
147                     MaintenanceUtil.appendStatus(
148                          (i * 100 / tableDetails.size()) + "%");
149                 }
150 
151                 Tuple tuple = tableDetails.get(i);
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         }
160         finally {
161             DataAccess.cleanUp(connection);
162         }
163 
164         MaintenanceUtil.appendStatus(
165             "Please change your JDBC settings before restarting server");
166 
167         ShutdownUtil.shutdown(0);
168     }
169 
170     protected DataSource getDataSource() throws Exception {
171         String[] values = getParameterValues();
172 
173         String jdbcDriverClassName = values[0];
174         String jdbcURL = values[1];
175         String jdbcUserName = values[2];
176         String jdbcPassword = values[3];
177 
178         Properties properties = new Properties();
179 
180         properties.setProperty(
181             _JDBC_PREFIX + "driverClassName", jdbcDriverClassName);
182         properties.setProperty(_JDBC_PREFIX + "url", jdbcURL);
183         properties.setProperty(_JDBC_PREFIX + "username", jdbcUserName);
184         properties.setProperty(_JDBC_PREFIX + "password", jdbcPassword);
185 
186         DataSourceFactoryBean dataSourceFactory = new DataSourceFactoryBean();
187 
188         dataSourceFactory.setProperties(properties);
189         dataSourceFactory.setPropertyPrefix(_JDBC_PREFIX);
190 
191         return (DataSource)dataSourceFactory.createInstance();
192     }
193 
194     public Class<?> getImplClass(String implClassName) throws Exception {
195         try {
196             ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
197 
198             return classLoader.loadClass(implClassName);
199         }
200         catch (Exception e) {
201         }
202 
203         for (String servletContextName : ServletContextPool.keySet()) {
204             try {
205                 ServletContext servletContext = ServletContextPool.get(
206                     servletContextName);
207 
208                 ClassLoader classLoader =
209                     (ClassLoader)servletContext.getAttribute(
210                         PortletServlet.PORTLET_CLASS_LOADER);
211 
212                 return classLoader.loadClass(implClassName);
213             }
214             catch (Exception e) {
215             }
216         }
217 
218         return null;
219     }
220 
221     protected Tuple getTableDetails(
222         Class<?> implClass, Field tableField, String tableFieldVar) {
223 
224         try {
225             String columnsFieldVar = StringUtil.replace(
226                 tableFieldVar, "_NAME", "_COLUMNS");
227             String sqlCreateFieldVar = StringUtil.replace(
228                 tableFieldVar, "_NAME", "_SQL_CREATE");
229 
230             Field columnsField = implClass.getField(columnsFieldVar);
231             Field sqlCreateField = implClass.getField(sqlCreateFieldVar);
232 
233             String table = (String)tableField.get(StringPool.BLANK);
234             Object[][] columns = (Object[][])columnsField.get(new Object[0][0]);
235             String sqlCreate = (String)sqlCreateField.get(StringPool.BLANK);
236 
237             return new Tuple(table, columns, sqlCreate);
238         }
239         catch (Exception e) {
240         }
241 
242         return null;
243     }
244 
245     protected void migrateTable(
246             DB db, Connection connection, String tableName, Object[][] columns,
247             String sqlCreate)
248         throws Exception {
249 
250         Table table = new Table(tableName, columns);
251 
252         String tempFileName = table.generateTempFile();
253 
254         db.runSQL(connection, sqlCreate);
255 
256         if (tempFileName != null) {
257             table.populateTable(tempFileName, connection);
258         }
259     }
260 
261     private static final String _JDBC_PREFIX = "jdbc.upgrade.";
262 
263     private static final Tuple[] _UNMAPPED_TABLES = new Tuple[] {
264         new Tuple(
265             Counter.TABLE_NAME, Counter.TABLE_COLUMNS,
266             Counter.TABLE_SQL_CREATE),
267         new Tuple(
268             CyrusUser.TABLE_NAME, CyrusUser.TABLE_COLUMNS,
269             CyrusUser.TABLE_SQL_CREATE),
270         new Tuple(
271             CyrusVirtual.TABLE_NAME, CyrusVirtual.TABLE_COLUMNS,
272             CyrusVirtual.TABLE_SQL_CREATE)
273     };
274 
275     private static Log _log = LogFactoryUtil.getLog(ConvertDatabase.class);
276 
277 }