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