001
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
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 }