1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.events;
24  
25  import com.liferay.portal.kernel.dao.jdbc.DataAccess;
26  import com.liferay.portal.kernel.log.Log;
27  import com.liferay.portal.kernel.log.LogFactoryUtil;
28  import com.liferay.portal.kernel.search.SearchEngineUtil;
29  import com.liferay.portal.kernel.util.GetterUtil;
30  import com.liferay.portal.kernel.util.InstancePool;
31  import com.liferay.portal.kernel.util.PropertiesUtil;
32  import com.liferay.portal.kernel.util.StringUtil;
33  import com.liferay.portal.kernel.util.Validator;
34  import com.liferay.portal.service.persistence.BatchSessionUtil;
35  import com.liferay.portal.tools.sql.DBUtil;
36  import com.liferay.portal.tools.sql.Index;
37  import com.liferay.portal.upgrade.UpgradeException;
38  import com.liferay.portal.upgrade.UpgradeProcess;
39  import com.liferay.portal.util.PropsKeys;
40  import com.liferay.portal.util.PropsUtil;
41  import com.liferay.portal.verify.VerifyException;
42  import com.liferay.portal.verify.VerifyProcess;
43  
44  import java.io.BufferedReader;
45  import java.io.StringReader;
46  
47  import java.sql.Connection;
48  import java.sql.PreparedStatement;
49  import java.sql.ResultSet;
50  
51  import java.util.ArrayList;
52  import java.util.Enumeration;
53  import java.util.HashSet;
54  import java.util.List;
55  import java.util.Properties;
56  import java.util.Set;
57  
58  /**
59   * <a href="StartupHelper.java.html"><b><i>View Source</i></b></a>
60   *
61   * @author Brian Wing Shun Chan
62   * @author Alexander Chow
63   * @author Raymond Augé
64   *
65   */
66  public class StartupHelper {
67  
68      public void deleteTempImages() {
69          try {
70              DBUtil dbUtil = DBUtil.getInstance();
71  
72              dbUtil.runSQL(_DELETE_TEMP_IMAGES_1);
73              dbUtil.runSQL(_DELETE_TEMP_IMAGES_2);
74          }
75          catch (Exception e) {
76              _log.error(e, e);
77          }
78      }
79  
80      public void setDropIndexes(boolean dropIndexes) {
81          _dropIndexes = dropIndexes;
82      }
83  
84      public void updateIndexes() {
85          Set<String> existingIndexNames = new HashSet<String>();
86  
87          try {
88              dropIndexes(existingIndexNames);
89          }
90          catch (Exception e) {
91              _log.error(e, e);
92          }
93  
94          try {
95              addIndexes(existingIndexNames);
96          }
97          catch (Exception e) {
98              _log.error(e, e);
99          }
100     }
101 
102     public void upgradeProcess(int buildNumber) throws UpgradeException {
103         String[] upgradeProcesses = PropsUtil.getArray(
104             PropsKeys.UPGRADE_PROCESSES);
105 
106         for (int i = 0; i < upgradeProcesses.length; i++) {
107             if (_log.isDebugEnabled()) {
108                 _log.debug("Initializing upgrade " + upgradeProcesses[i]);
109             }
110 
111             UpgradeProcess upgradeProcess = (UpgradeProcess)InstancePool.get(
112                 upgradeProcesses[i]);
113 
114             if (upgradeProcess == null) {
115                 _log.error(upgradeProcesses[i] + " cannot be found");
116 
117                 continue;
118             }
119 
120             if ((upgradeProcess.getThreshold() == 0) ||
121                 (upgradeProcess.getThreshold() > buildNumber)) {
122 
123                 if (_log.isDebugEnabled()) {
124                     _log.debug("Running upgrade " + upgradeProcesses[i]);
125                 }
126 
127                 upgradeProcess.upgrade();
128 
129                 if (_log.isDebugEnabled()) {
130                     _log.debug("Finished upgrade " + upgradeProcesses[i]);
131                 }
132 
133                 _upgraded = true;
134             }
135             else {
136                 if (_log.isDebugEnabled()) {
137                     _log.debug(
138                         "Upgrade threshold " + upgradeProcess.getThreshold() +
139                             " will not trigger upgrade");
140 
141                     _log.debug("Skipping upgrade " + upgradeProcesses[i]);
142                 }
143             }
144         }
145     }
146 
147     public void verifyProcess(boolean verified) throws VerifyException {
148 
149         // LPS-1880
150 
151         int verifyFrequency = GetterUtil.getInteger(
152             PropsUtil.get(PropsKeys.VERIFY_FREQUENCY));
153 
154         if ((verifyFrequency == VerifyProcess.ALWAYS) ||
155             ((verifyFrequency == VerifyProcess.ONCE) && !verified) ||
156             (_upgraded)) {
157 
158             if (!_upgraded) {
159                 PropsUtil.set(PropsKeys.INDEX_ON_STARTUP, "true");
160             }
161 
162             String[] verifyProcesses = PropsUtil.getArray(
163                 PropsKeys.VERIFY_PROCESSES);
164 
165             BatchSessionUtil.setEnabled(true);
166 
167             boolean tempIndexReadOnly = SearchEngineUtil.isIndexReadOnly();
168 
169             SearchEngineUtil.setIndexReadOnly(true);
170 
171             try {
172                 for (String className : verifyProcesses) {
173                     verifyProcess(className);
174                 }
175             }
176             finally {
177                 BatchSessionUtil.setEnabled(false);
178 
179                 SearchEngineUtil.setIndexReadOnly(tempIndexReadOnly);
180             }
181         }
182     }
183 
184     public boolean isUpgraded() {
185         return _upgraded;
186     }
187 
188     public boolean isVerified() {
189         return _verified;
190     }
191 
192     protected void addIndexes(Set<String> existingIndexNames) throws Exception {
193         if (_log.isInfoEnabled()) {
194             _log.info("Adding indexes");
195         }
196 
197         DBUtil dbUtil = DBUtil.getInstance();
198 
199         BufferedReader bufferedReader = new BufferedReader(new StringReader(
200             readIndexesSQL()));
201 
202         String sql = null;
203 
204         while ((sql = bufferedReader.readLine()) != null) {
205             if (Validator.isNull(sql)) {
206                 continue;
207             }
208 
209             int y = sql.indexOf(" on ");
210             int x = sql.lastIndexOf(" ", y - 1);
211 
212             String indexName = sql.substring(x + 1, y);
213 
214             if (existingIndexNames.contains(indexName)) {
215                 continue;
216             }
217 
218             if (_dropIndexes) {
219                 if (_log.isInfoEnabled()) {
220                     _log.info(sql);
221                 }
222             }
223 
224             try {
225                 dbUtil.runSQL(sql);
226             }
227             catch (Exception e) {
228                 if (_log.isWarnEnabled()) {
229                     _log.warn(e.getMessage());
230                 }
231             }
232         }
233     }
234 
235     protected void dropIndexes(Set<String> existingIndexNames)
236         throws Exception {
237 
238         if (!_dropIndexes) {
239             return;
240         }
241 
242         List<Index> indexes = null;
243 
244         DBUtil dbUtil = DBUtil.getInstance();
245 
246         String type = dbUtil.getType();
247 
248         if (type.equals(DBUtil.TYPE_DB2)) {
249             indexes = getDB2Indexes();
250         }
251         else if (type.equals(DBUtil.TYPE_MYSQL)) {
252             indexes = getMySQLIndexes();
253         }
254         else if (type.equals(DBUtil.TYPE_ORACLE)) {
255             indexes = getOracleIndexes();
256         }
257         else if (type.equals(DBUtil.TYPE_POSTGRESQL)) {
258             indexes = getPostgreSQLIndexes();
259         }
260         else if (type.equals(DBUtil.TYPE_SQLSERVER)) {
261             indexes = getSQLServerIndexes();
262         }
263         else if (type.equals(DBUtil.TYPE_SYBASE)) {
264             indexes = getSybaseIndexes();
265         }
266 
267         if ((indexes == null) || indexes.isEmpty()) {
268             return;
269         }
270 
271         if (_log.isInfoEnabled()) {
272             _log.info("Dropping stale indexes");
273         }
274 
275         Thread currentThread = Thread.currentThread();
276 
277         ClassLoader classLoader = currentThread.getContextClassLoader();
278 
279         String indexPropertiesString = StringUtil.read(
280             classLoader,
281             "com/liferay/portal/tools/sql/dependencies/indexes.properties");
282 
283         Properties indexProperties = PropertiesUtil.load(indexPropertiesString);
284 
285         Enumeration<String> indexPropertiesEnu =
286             (Enumeration<String>)indexProperties.propertyNames();
287 
288         while (indexPropertiesEnu.hasMoreElements()) {
289             String key = indexPropertiesEnu.nextElement();
290 
291             String value = indexProperties.getProperty(key);
292 
293             indexProperties.setProperty(key.toLowerCase(), value);
294         }
295 
296         String indexesSQLString = readIndexesSQL().toLowerCase();
297 
298         String portalTablesSQLString = StringUtil.read(
299             classLoader,
300             "com/liferay/portal/tools/sql/dependencies/portal-tables.sql");
301 
302         portalTablesSQLString = portalTablesSQLString.toLowerCase();
303 
304         for (Index index : indexes) {
305             String indexName = index.getIndexName().toUpperCase();
306             String indexNameLowerCase = indexName.toLowerCase();
307             String tableName = index.getTableName();
308             String tableNameLowerCase = tableName.toLowerCase();
309             boolean unique = index.isUnique();
310 
311             existingIndexNames.add(indexName);
312 
313             if (indexProperties.containsKey(indexNameLowerCase)) {
314                 if (unique &&
315                     indexesSQLString.contains(
316                         "create unique index " + indexNameLowerCase + " ")) {
317 
318                     continue;
319                 }
320 
321                 if (!unique &&
322                     indexesSQLString.contains(
323                         "create index " + indexNameLowerCase + " ")) {
324 
325                     continue;
326                 }
327             }
328             else {
329                 if (!portalTablesSQLString.contains(
330                         "create table " + tableNameLowerCase + " (")) {
331 
332                     continue;
333                 }
334             }
335 
336             existingIndexNames.remove(indexName);
337 
338             String sql = "drop index " + indexName;
339 
340             if (type.equals(DBUtil.TYPE_MYSQL) ||
341                 type.equals(DBUtil.TYPE_SQLSERVER)) {
342 
343                 sql += " on " + tableName;
344             }
345 
346             if (_log.isInfoEnabled()) {
347                 _log.info(sql);
348             }
349 
350             dbUtil.runSQL(sql);
351         }
352     }
353 
354     protected List<Index> getDB2Indexes() throws Exception {
355         return null;
356     }
357 
358     protected List<Index> getMySQLIndexes() throws Exception {
359         List<Index> indexes = new ArrayList<Index>();
360 
361         Connection con = null;
362         PreparedStatement ps = null;
363         ResultSet rs = null;
364 
365         try {
366             con = DataAccess.getConnection();
367 
368             StringBuilder sb = new StringBuilder();
369 
370             sb.append("select distinct(index_name), table_name, non_unique ");
371             sb.append("from information_schema.statistics where ");
372             sb.append("index_schema = database() and (index_name like ");
373             sb.append("'LIFERAY_%' or index_name like 'IX_%')");
374 
375             String sql = sb.toString();
376 
377             ps = con.prepareStatement(sql);
378 
379             rs = ps.executeQuery();
380 
381             while (rs.next()) {
382                 String indexName = rs.getString("index_name");
383                 String tableName = rs.getString("table_name");
384                 boolean unique = !rs.getBoolean("non_unique");
385 
386                 indexes.add(new Index(indexName, tableName, unique));
387             }
388         }
389         finally {
390             DataAccess.cleanUp(con, ps, rs);
391         }
392 
393         return indexes;
394     }
395 
396     protected List<Index> getOracleIndexes() throws Exception {
397         List<Index> indexes = new ArrayList<Index>();
398 
399         Connection con = null;
400         PreparedStatement ps = null;
401         ResultSet rs = null;
402 
403         try {
404             con = DataAccess.getConnection();
405 
406             StringBuilder sb = new StringBuilder();
407 
408             sb.append("select index_name, table_name, uniqueness from ");
409             sb.append("user_indexes where index_name like 'LIFERAY_%' or ");
410             sb.append("index_name like 'IX_%'");
411 
412             String sql = sb.toString();
413 
414             ps = con.prepareStatement(sql);
415 
416             rs = ps.executeQuery();
417 
418             while (rs.next()) {
419                 String indexName = rs.getString("index_name");
420                 String tableName = rs.getString("table_name");
421                 String uniqueness = rs.getString("uniqueness");
422 
423                 boolean unique = true;
424 
425                 if (uniqueness.equalsIgnoreCase("NONUNIQUE")) {
426                     unique = false;
427                 }
428 
429                 indexes.add(new Index(indexName, tableName, unique));
430             }
431         }
432         finally {
433             DataAccess.cleanUp(con, ps, rs);
434         }
435 
436         return indexes;
437     }
438 
439     protected List<Index> getPostgreSQLIndexes() throws Exception {
440         List<Index> indexes = new ArrayList<Index>();
441 
442         Connection con = null;
443         PreparedStatement ps = null;
444         ResultSet rs = null;
445 
446         try {
447             con = DataAccess.getConnection();
448 
449             StringBuilder sb = new StringBuilder();
450 
451             sb.append("select indexname, tablename, indexdef from pg_indexes ");
452             sb.append("where indexname like 'liferay_%' or indexname like ");
453             sb.append("'ix_%'");
454 
455             String sql = sb.toString();
456 
457             ps = con.prepareStatement(sql);
458 
459             rs = ps.executeQuery();
460 
461             while (rs.next()) {
462                 String indexName = rs.getString("indexname");
463                 String tableName = rs.getString("tablename");
464                 String indexSQL = rs.getString("indexdef").toLowerCase().trim();
465 
466                 boolean unique = true;
467 
468                 if (indexSQL.startsWith("create index ")) {
469                     unique = false;
470                 }
471 
472                 indexes.add(new Index(indexName, tableName, unique));
473             }
474         }
475         finally {
476             DataAccess.cleanUp(con, ps, rs);
477         }
478 
479         return indexes;
480     }
481 
482     protected List<Index> getSQLServerIndexes() throws Exception {
483         List<Index> indexes = new ArrayList<Index>();
484 
485         Connection con = null;
486         PreparedStatement ps = null;
487         ResultSet rs = null;
488 
489         try {
490             con = DataAccess.getConnection();
491 
492             StringBuilder sb = new StringBuilder();
493 
494             sb.append("select sys.tables.name as table_name, ");
495             sb.append("sys.indexes.name as index_name, is_unique from ");
496             sb.append("sys.indexes inner join sys.tables on ");
497             sb.append("sys.tables.object_id = sys.indexes.object_id where ");
498             sb.append("sys.indexes.name like 'LIFERAY_%' or sys.indexes.name ");
499             sb.append("like 'IX_%'");
500 
501             String sql = sb.toString();
502 
503             ps = con.prepareStatement(sql);
504 
505             rs = ps.executeQuery();
506 
507             while (rs.next()) {
508                 String indexName = rs.getString("index_name");
509                 String tableName = rs.getString("table_name");
510                 boolean unique = !rs.getBoolean("is_unique");
511 
512                 indexes.add(new Index(indexName, tableName, unique));
513             }
514         }
515         finally {
516             DataAccess.cleanUp(con, ps, rs);
517         }
518 
519         return indexes;
520     }
521 
522     protected List<Index> getSybaseIndexes() throws Exception {
523         return null;
524     }
525 
526     protected String readIndexesSQL() throws Exception {
527         Thread currentThread = Thread.currentThread();
528 
529         ClassLoader classLoader = currentThread.getContextClassLoader();
530 
531         return StringUtil.read(
532             classLoader,
533             "com/liferay/portal/tools/sql/dependencies/indexes.sql");
534     }
535 
536     protected void verifyProcess(String className) throws VerifyException {
537         if (_log.isDebugEnabled()) {
538             _log.debug("Initializing verification " + className);
539         }
540 
541         try {
542             VerifyProcess verifyProcess = (VerifyProcess)Class.forName(
543                 className).newInstance();
544 
545             if (_log.isDebugEnabled()) {
546                 _log.debug("Running verification " + className);
547             }
548 
549             verifyProcess.verify();
550 
551             if (_log.isDebugEnabled()) {
552                 _log.debug("Finished verification " + className);
553             }
554 
555             _verified = true;
556         }
557         catch (ClassNotFoundException cnfe) {
558             _log.error(className + " cannot be found");
559         }
560         catch (IllegalAccessException iae) {
561             _log.error(className + " cannot be accessed");
562         }
563         catch (InstantiationException ie) {
564             _log.error(className + " cannot be initiated");
565         }
566     }
567 
568     private static final String _DELETE_TEMP_IMAGES_1 =
569         "delete from Image where imageId IN (SELECT articleImageId FROM " +
570             "JournalArticleImage where tempImage = TRUE)";
571 
572     private static final String _DELETE_TEMP_IMAGES_2 =
573         "delete from JournalArticleImage where tempImage = TRUE";
574 
575     private static Log _log = LogFactoryUtil.getLog(StartupHelper.class);
576 
577     private boolean _dropIndexes;
578     private boolean _upgraded;
579     private boolean _verified;
580 
581 }