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