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