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.db.DB;
26  import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
27  import com.liferay.portal.kernel.dao.db.Index;
28  import com.liferay.portal.kernel.dao.jdbc.DataAccess;
29  import com.liferay.portal.kernel.log.Log;
30  import com.liferay.portal.kernel.log.LogFactoryUtil;
31  import com.liferay.portal.kernel.search.SearchEngineUtil;
32  import com.liferay.portal.kernel.upgrade.UpgradeException;
33  import com.liferay.portal.kernel.upgrade.UpgradeProcess;
34  import com.liferay.portal.kernel.util.GetterUtil;
35  import com.liferay.portal.kernel.util.InstancePool;
36  import com.liferay.portal.kernel.util.PropertiesUtil;
37  import com.liferay.portal.kernel.util.PropsKeys;
38  import com.liferay.portal.kernel.util.StringUtil;
39  import com.liferay.portal.kernel.util.Validator;
40  import com.liferay.portal.service.persistence.BatchSessionUtil;
41  import com.liferay.portal.util.PropsUtil;
42  import com.liferay.portal.verify.VerifyException;
43  import com.liferay.portal.verify.VerifyProcess;
44  
45  import java.io.BufferedReader;
46  import java.io.StringReader;
47  
48  import java.sql.Connection;
49  import java.sql.DatabaseMetaData;
50  import java.sql.PreparedStatement;
51  import java.sql.ResultSet;
52  
53  import java.util.ArrayList;
54  import java.util.Collections;
55  import java.util.Enumeration;
56  import java.util.HashSet;
57  import java.util.List;
58  import java.util.Properties;
59  import java.util.Set;
60  
61  /**
62   * <a href="StartupHelper.java.html"><b><i>View Source</i></b></a>
63   *
64   * @author Brian Wing Shun Chan
65   * @author Alexander Chow
66   * @author Raymond Augé
67   */
68  public class StartupHelper {
69  
70      public void deleteTempImages() {
71          try {
72              DB db = DBFactoryUtil.getDB();
73  
74              db.runSQL(_DELETE_TEMP_IMAGES_1);
75              db.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         DB db = DBFactoryUtil.getDB();
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                 db.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         DB db = DBFactoryUtil.getDB();
254 
255         String type = db.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(DB.TYPE_MYSQL) || type.equals(DB.TYPE_SQLSERVER)) {
323                 sql += " on " + tableName;
324             }
325 
326             if (_log.isInfoEnabled()) {
327                 _log.info(sql);
328             }
329 
330             db.runSQL(sql);
331         }
332 
333         return validIndexNames;
334     }
335 
336     protected List<Index> getDB2Indexes() throws Exception {
337         return null;
338     }
339 
340     protected List<Index> getIndexes() throws Exception {
341         List<Index> indexes = null;
342 
343         DB db = DBFactoryUtil.getDB();
344 
345         String type = db.getType();
346 
347         if (type.equals(DB.TYPE_DB2)) {
348             indexes = getDB2Indexes();
349         }
350         else if (type.equals(DB.TYPE_MYSQL)) {
351             indexes = getMySQLIndexes();
352         }
353         else if (type.equals(DB.TYPE_ORACLE)) {
354             indexes = getOracleIndexes();
355         }
356         else if (type.equals(DB.TYPE_POSTGRESQL)) {
357             indexes = getPostgreSQLIndexes();
358         }
359         else if (type.equals(DB.TYPE_SQLSERVER)) {
360             indexes = getSQLServerIndexes();
361         }
362         else if (type.equals(DB.TYPE_SYBASE)) {
363             indexes = getSybaseIndexes();
364         }
365 
366         if (indexes == null) {
367             indexes = Collections.EMPTY_LIST;
368         }
369 
370         return indexes;
371     }
372 
373     protected List<Index> getMySQLIndexes() throws Exception {
374         List<Index> indexes = new ArrayList<Index>();
375 
376         Connection con = null;
377         PreparedStatement ps = null;
378         ResultSet rs = null;
379 
380         try {
381             con = DataAccess.getConnection();
382 
383             StringBuilder sb = new StringBuilder();
384 
385             sb.append("select distinct(index_name), table_name, non_unique ");
386             sb.append("from information_schema.statistics where ");
387             sb.append("index_schema = database() and (index_name like ");
388             sb.append("'LIFERAY_%' or index_name like 'IX_%')");
389 
390             String sql = sb.toString();
391 
392             ps = con.prepareStatement(sql);
393 
394             rs = ps.executeQuery();
395 
396             while (rs.next()) {
397                 String indexName = rs.getString("index_name");
398                 String tableName = rs.getString("table_name");
399                 boolean unique = !rs.getBoolean("non_unique");
400 
401                 indexes.add(new Index(indexName, tableName, unique));
402             }
403         }
404         finally {
405             DataAccess.cleanUp(con, ps, rs);
406         }
407 
408         return indexes;
409     }
410 
411     protected List<Index> getOracleIndexes() throws Exception {
412         List<Index> indexes = new ArrayList<Index>();
413 
414         Connection con = null;
415         PreparedStatement ps = null;
416         ResultSet rs = null;
417 
418         try {
419             con = DataAccess.getConnection();
420 
421             StringBuilder sb = new StringBuilder();
422 
423             sb.append("select index_name, table_name, uniqueness from ");
424             sb.append("user_indexes where index_name like 'LIFERAY_%' or ");
425             sb.append("index_name like 'IX_%'");
426 
427             String sql = sb.toString();
428 
429             ps = con.prepareStatement(sql);
430 
431             rs = ps.executeQuery();
432 
433             while (rs.next()) {
434                 String indexName = rs.getString("index_name");
435                 String tableName = rs.getString("table_name");
436                 String uniqueness = rs.getString("uniqueness");
437 
438                 boolean unique = true;
439 
440                 if (uniqueness.equalsIgnoreCase("NONUNIQUE")) {
441                     unique = false;
442                 }
443 
444                 indexes.add(new Index(indexName, tableName, unique));
445             }
446         }
447         finally {
448             DataAccess.cleanUp(con, ps, rs);
449         }
450 
451         return indexes;
452     }
453 
454     protected List<Index> getPostgreSQLIndexes() throws Exception {
455         List<Index> indexes = new ArrayList<Index>();
456 
457         Connection con = null;
458         PreparedStatement ps = null;
459         ResultSet rs = null;
460 
461         try {
462             con = DataAccess.getConnection();
463 
464             StringBuilder sb = new StringBuilder();
465 
466             sb.append("select indexname, tablename, indexdef from pg_indexes ");
467             sb.append("where indexname like 'liferay_%' or indexname like ");
468             sb.append("'ix_%'");
469 
470             String sql = sb.toString();
471 
472             ps = con.prepareStatement(sql);
473 
474             rs = ps.executeQuery();
475 
476             while (rs.next()) {
477                 String indexName = rs.getString("indexname");
478                 String tableName = rs.getString("tablename");
479                 String indexSQL = rs.getString("indexdef").toLowerCase().trim();
480 
481                 boolean unique = true;
482 
483                 if (indexSQL.startsWith("create index ")) {
484                     unique = false;
485                 }
486 
487                 indexes.add(new Index(indexName, tableName, unique));
488             }
489         }
490         finally {
491             DataAccess.cleanUp(con, ps, rs);
492         }
493 
494         return indexes;
495     }
496 
497     protected List<Index> getSQLServerIndexes() throws Exception {
498         List<Index> indexes = new ArrayList<Index>();
499 
500         Connection con = null;
501         PreparedStatement ps = null;
502         ResultSet rs = null;
503 
504         try {
505             con = DataAccess.getConnection();
506 
507             DatabaseMetaData metaData = con.getMetaData();
508 
509             if (metaData.getDatabaseMajorVersion() <= _SQL_SERVER_2000) {
510                 return null;
511             }
512 
513             StringBuilder sb = new StringBuilder();
514 
515             sb.append("select sys.tables.name as table_name, ");
516             sb.append("sys.indexes.name as index_name, is_unique from ");
517             sb.append("sys.indexes inner join sys.tables on ");
518             sb.append("sys.tables.object_id = sys.indexes.object_id where ");
519             sb.append("sys.indexes.name like 'LIFERAY_%' or sys.indexes.name ");
520             sb.append("like 'IX_%'");
521 
522             String sql = sb.toString();
523 
524             ps = con.prepareStatement(sql);
525 
526             rs = ps.executeQuery();
527 
528             while (rs.next()) {
529                 String indexName = rs.getString("index_name");
530                 String tableName = rs.getString("table_name");
531                 boolean unique = !rs.getBoolean("is_unique");
532 
533                 indexes.add(new Index(indexName, tableName, unique));
534             }
535         }
536         finally {
537             DataAccess.cleanUp(con, ps, rs);
538         }
539 
540         return indexes;
541     }
542 
543     protected List<Index> getSybaseIndexes() throws Exception {
544         return null;
545     }
546 
547     protected String readIndexesSQL() throws Exception {
548         Thread currentThread = Thread.currentThread();
549 
550         ClassLoader classLoader = currentThread.getContextClassLoader();
551 
552         return StringUtil.read(
553             classLoader,
554             "com/liferay/portal/tools/sql/dependencies/indexes.sql");
555     }
556 
557     protected void verifyProcess(String className) throws VerifyException {
558         if (_log.isDebugEnabled()) {
559             _log.debug("Initializing verification " + className);
560         }
561 
562         try {
563             VerifyProcess verifyProcess = (VerifyProcess)Class.forName(
564                 className).newInstance();
565 
566             if (_log.isDebugEnabled()) {
567                 _log.debug("Running verification " + className);
568             }
569 
570             verifyProcess.verify();
571 
572             if (_log.isDebugEnabled()) {
573                 _log.debug("Finished verification " + className);
574             }
575 
576             _verified = true;
577         }
578         catch (ClassNotFoundException cnfe) {
579             _log.error(className + " cannot be found");
580         }
581         catch (IllegalAccessException iae) {
582             _log.error(className + " cannot be accessed");
583         }
584         catch (InstantiationException ie) {
585             _log.error(className + " cannot be initiated");
586         }
587     }
588 
589     private static final String _DELETE_TEMP_IMAGES_1 =
590         "delete from Image where imageId IN (SELECT articleImageId FROM " +
591             "JournalArticleImage where tempImage = TRUE)";
592 
593     private static final String _DELETE_TEMP_IMAGES_2 =
594         "delete from JournalArticleImage where tempImage = TRUE";
595 
596     private static final int _SQL_SERVER_2000 = 8;
597 
598     private static Log _log = LogFactoryUtil.getLog(StartupHelper.class);
599 
600     private boolean _dropIndexes;
601     private boolean _upgraded;
602     private boolean _verified;
603 
604 }