001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.tools;
016    
017    import com.liferay.portal.dao.orm.common.SQLTransformer;
018    import com.liferay.portal.events.StartupHelperUtil;
019    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
020    import com.liferay.portal.kernel.cache.MultiVMPoolUtil;
021    import com.liferay.portal.kernel.dao.db.DB;
022    import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
023    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.spring.aop.Skip;
027    import com.liferay.portal.kernel.util.GetterUtil;
028    import com.liferay.portal.kernel.util.PropsKeys;
029    import com.liferay.portal.kernel.util.ReflectionUtil;
030    import com.liferay.portal.kernel.util.ReleaseInfo;
031    import com.liferay.portal.kernel.util.StringBundler;
032    import com.liferay.portal.kernel.util.Time;
033    import com.liferay.portal.model.Release;
034    import com.liferay.portal.model.ReleaseConstants;
035    import com.liferay.portal.service.ClassNameLocalServiceUtil;
036    import com.liferay.portal.service.ReleaseLocalServiceUtil;
037    import com.liferay.portal.service.ResourceActionLocalServiceUtil;
038    import com.liferay.portal.spring.aop.ServiceBeanAopCacheManager;
039    import com.liferay.portal.spring.aop.ServiceBeanAopCacheManagerUtil;
040    import com.liferay.portal.util.InitUtil;
041    import com.liferay.portal.util.PropsUtil;
042    import com.liferay.portal.util.PropsValues;
043    import com.liferay.util.dao.orm.CustomSQLUtil;
044    
045    import java.lang.annotation.Annotation;
046    import java.lang.reflect.Field;
047    
048    import java.sql.Connection;
049    import java.sql.Date;
050    import java.sql.PreparedStatement;
051    import java.sql.ResultSet;
052    
053    import java.util.HashMap;
054    import java.util.concurrent.ConcurrentHashMap;
055    
056    import org.aopalliance.intercept.MethodInvocation;
057    
058    import org.apache.commons.lang.time.StopWatch;
059    
060    /**
061     * @author Michael C. Han
062     * @author Brian Wing Shun Chan
063     */
064    public class DBUpgrader {
065    
066            public static void main(String[] args) {
067                    try {
068                            StopWatch stopWatch = new StopWatch();
069    
070                            stopWatch.start();
071    
072                            InitUtil.initWithSpring();
073    
074                            upgrade();
075                            verify();
076    
077                            System.out.println(
078                                    "\nSuccessfully completed upgrade process in " +
079                                            (stopWatch.getTime() / Time.SECOND) + " seconds.");
080    
081                            System.exit(0);
082                    }
083                    catch (Exception e) {
084                            e.printStackTrace();
085    
086                            System.exit(1);
087                    }
088            }
089    
090            public static void upgrade() throws Exception {
091    
092                    // Disable database caching before upgrade
093    
094                    if (_log.isDebugEnabled()) {
095                            _log.debug("Disable cache registry");
096                    }
097    
098                    CacheRegistryUtil.setActive(false);
099    
100                    // Check release
101    
102                    int buildNumber = ReleaseLocalServiceUtil.getBuildNumberOrCreate();
103    
104                    if (buildNumber > ReleaseInfo.getParentBuildNumber()) {
105                            StringBundler sb = new StringBundler(6);
106    
107                            sb.append("Attempting to deploy an older Liferay Portal version. ");
108                            sb.append("Current build version is ");
109                            sb.append(buildNumber);
110                            sb.append(" and attempting to deploy version ");
111                            sb.append(ReleaseInfo.getParentBuildNumber());
112                            sb.append(".");
113    
114                            throw new IllegalStateException(sb.toString());
115                    }
116                    else if (buildNumber < ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER) {
117                            String msg = "You must first upgrade to Liferay Portal 5.2.3";
118    
119                            System.out.println(msg);
120    
121                            throw new RuntimeException(msg);
122                    }
123    
124                    // Reload SQL
125    
126                    CustomSQLUtil.reloadCustomSQL();
127                    SQLTransformer.reloadSQLTransformer();
128    
129                    // Upgrade
130    
131                    if (_log.isDebugEnabled()) {
132                            _log.debug("Update build " + buildNumber);
133                    }
134    
135                    _checkPermissionAlgorithm();
136                    _checkReleaseState(_getReleaseState());
137    
138                    if (PropsValues.UPGRADE_DATABASE_TRANSACTIONS_DISABLED) {
139                            _disableTransactions();
140                    }
141    
142                    try {
143                            StartupHelperUtil.upgradeProcess(buildNumber);
144                    }
145                    catch (Exception e) {
146                            _updateReleaseState(ReleaseConstants.STATE_UPGRADE_FAILURE);
147    
148                            throw e;
149                    }
150                    finally {
151                            if (PropsValues.UPGRADE_DATABASE_TRANSACTIONS_DISABLED) {
152                                    _enableTransactions();
153                            }
154                    }
155    
156                    // Update company key
157    
158                    if (StartupHelperUtil.isUpgraded()) {
159                            if (_log.isDebugEnabled()) {
160                                    _log.debug("Update company key");
161                            }
162    
163                            _updateCompanyKey();
164                    }
165    
166                    // Check class names
167    
168                    if (_log.isDebugEnabled()) {
169                            _log.debug("Check class names");
170                    }
171    
172                    ClassNameLocalServiceUtil.checkClassNames();
173    
174                    // Check resource actions
175    
176                    if (_log.isDebugEnabled()) {
177                            _log.debug("Check resource actions");
178                    }
179    
180                    ResourceActionLocalServiceUtil.checkResourceActions();
181    
182                    // Delete temporary images
183    
184                    if (_log.isDebugEnabled()) {
185                            _log.debug("Delete temporary images");
186                    }
187    
188                    _deleteTempImages();
189    
190                    // Clear the caches only if the upgrade process was run
191    
192                    if (_log.isDebugEnabled()) {
193                            _log.debug("Clear cache if upgrade process was run");
194                    }
195    
196                    if (StartupHelperUtil.isUpgraded()) {
197                            MultiVMPoolUtil.clear();
198                    }
199            }
200    
201            public static void verify() throws Exception {
202    
203                    // Check release
204    
205                    Release release = ReleaseLocalServiceUtil.fetchRelease(
206                            ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME);
207    
208                    if (release == null) {
209                            release = ReleaseLocalServiceUtil.addRelease(
210                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
211                                    ReleaseInfo.getParentBuildNumber());
212                    }
213    
214                    _checkReleaseState(release.getState());
215    
216                    // Update indexes
217    
218                    if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP) {
219                            StartupHelperUtil.setDropIndexes(true);
220    
221                            StartupHelperUtil.updateIndexes();
222                    }
223                    else if (StartupHelperUtil.isUpgraded()) {
224                            StartupHelperUtil.updateIndexes();
225                    }
226    
227                    // Verify
228    
229                    if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
230                            _disableTransactions();
231                    }
232    
233                    boolean newBuildNumber = false;
234    
235                    if (ReleaseInfo.getBuildNumber() > release.getBuildNumber()) {
236                            newBuildNumber = true;
237                    }
238    
239                    try {
240                            StartupHelperUtil.verifyProcess(
241                                    newBuildNumber, release.isVerified());
242                    }
243                    catch (Exception e) {
244                            _updateReleaseState(ReleaseConstants.STATE_VERIFY_FAILURE);
245    
246                            if (_log.isErrorEnabled()) {
247                                    _log.error(
248                                            "Unable to execute verify process: " + e.getMessage(), e);
249                            }
250    
251                            throw e;
252                    }
253                    finally {
254                            if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
255                                    _enableTransactions();
256                            }
257                    }
258    
259                    // Update indexes
260    
261                    if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP ||
262                            StartupHelperUtil.isUpgraded()) {
263    
264                            StartupHelperUtil.updateIndexes(false);
265                    }
266    
267                    // Update release
268    
269                    boolean verified = StartupHelperUtil.isVerified();
270    
271                    if (release.isVerified()) {
272                            verified = true;
273                    }
274    
275                    ReleaseLocalServiceUtil.updateRelease(
276                            release.getReleaseId(), ReleaseInfo.getParentBuildNumber(),
277                            ReleaseInfo.getBuildDate(), verified);
278    
279                    // Enable database caching after verify
280    
281                    CacheRegistryUtil.setActive(true);
282            }
283    
284            private static void _checkPermissionAlgorithm() throws Exception {
285                    long count = _getResourceCodesCount();
286    
287                    if (count == 0) {
288                            return;
289                    }
290    
291                    StringBundler sb = new StringBundler(8);
292    
293                    sb.append("Permission conversion to algorithm 6 has not been ");
294                    sb.append("completed. Please complete the conversion prior to ");
295                    sb.append("starting the portal. The conversion process is ");
296                    sb.append("available in portal versions starting with ");
297                    sb.append(ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER);
298                    sb.append(" and prior to ");
299                    sb.append(ReleaseInfo.RELEASE_6_2_0_BUILD_NUMBER);
300                    sb.append(".");
301    
302                    throw new IllegalStateException(sb.toString());
303            }
304    
305            private static void _checkReleaseState(int state) throws Exception {
306                    if (state == ReleaseConstants.STATE_GOOD) {
307                            return;
308                    }
309    
310                    StringBundler sb = new StringBundler(6);
311    
312                    sb.append("The database contains changes from a previous ");
313                    sb.append("upgrade attempt that failed. Please restore the old ");
314                    sb.append("database and file system and retry the upgrade. A ");
315                    sb.append("patch may be required if the upgrade failed due to a");
316                    sb.append(" bug or an unforeseen data permutation that resulted ");
317                    sb.append("from a corrupt database.");
318    
319                    throw new IllegalStateException(sb.toString());
320            }
321    
322            private static void _deleteTempImages() throws Exception {
323                    DB db = DBFactoryUtil.getDB();
324    
325                    db.runSQL(_DELETE_TEMP_IMAGES_1);
326                    db.runSQL(_DELETE_TEMP_IMAGES_2);
327            }
328    
329            private static void _disableTransactions() throws Exception {
330                    if (_log.isDebugEnabled()) {
331                            _log.debug("Disable transactions");
332                    }
333    
334                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = false;
335    
336                    Field field = ReflectionUtil.getDeclaredField(
337                            ServiceBeanAopCacheManager.class, "_annotations");
338    
339                    field.set(
340                            null,
341                            new HashMap<MethodInvocation, Annotation[]>() {
342    
343                                    @Override
344                                    public Annotation[] get(Object key) {
345                                            return _annotations;
346                                    }
347    
348                                    private Annotation[] _annotations = new Annotation[] {
349                                            new Skip() {
350    
351                                                    @Override
352                                                    public Class<? extends Annotation> annotationType() {
353                                                            return Skip.class;
354                                                    }
355    
356                                            }
357                                    };
358    
359                            }
360                    );
361            }
362    
363            private static void _enableTransactions() throws Exception {
364                    if (_log.isDebugEnabled()) {
365                            _log.debug("Enable transactions");
366                    }
367    
368                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = GetterUtil.getBoolean(
369                            PropsUtil.get(PropsKeys.SPRING_HIBERNATE_SESSION_DELEGATED));
370    
371                    Field field = ReflectionUtil.getDeclaredField(
372                            ServiceBeanAopCacheManager.class, "_annotations");
373    
374                    field.set(
375                            null, new ConcurrentHashMap<MethodInvocation, Annotation[]>());
376    
377                    ServiceBeanAopCacheManagerUtil.reset();
378            }
379    
380            private static int _getReleaseState() throws Exception {
381                    Connection con = null;
382                    PreparedStatement ps = null;
383                    ResultSet rs = null;
384    
385                    try {
386                            con = DataAccess.getConnection();
387    
388                            ps = con.prepareStatement(
389                                    "select state_ from Release_ where releaseId = ?");
390    
391                            ps.setLong(1, ReleaseConstants.DEFAULT_ID);
392    
393                            rs = ps.executeQuery();
394    
395                            if (rs.next()) {
396                                    return rs.getInt("state_");
397                            }
398    
399                            throw new IllegalArgumentException(
400                                    "No Release exists with the primary key " +
401                                            ReleaseConstants.DEFAULT_ID);
402                    }
403                    finally {
404                            DataAccess.cleanUp(con, ps, rs);
405                    }
406            }
407    
408            private static long _getResourceCodesCount() throws Exception {
409                    Connection con = null;
410                    PreparedStatement ps = null;
411                    ResultSet rs = null;
412    
413                    try {
414                            con = DataAccess.getConnection();
415    
416                            ps = con.prepareStatement("select count(*) from ResourceCode");
417    
418                            rs = ps.executeQuery();
419    
420                            if (rs.next()) {
421                                    int count = rs.getInt(1);
422    
423                                    return count;
424                            }
425    
426                            return 0;
427                    }
428                    catch (Exception e) {
429                            return 0;
430                    }
431                    finally {
432                            DataAccess.cleanUp(con, ps, rs);
433                    }
434            }
435    
436            private static void _updateCompanyKey() throws Exception {
437                    DB db = DBFactoryUtil.getDB();
438    
439                    db.runSQL("update Company set key_ = null");
440            }
441    
442            private static void _updateReleaseState(int state) throws Exception {
443                    Connection con = null;
444                    PreparedStatement ps = null;
445    
446                    try {
447                            con = DataAccess.getConnection();
448    
449                            ps = con.prepareStatement(
450                                    "update Release_ set modifiedDate = ?, state_ = ? where " +
451                                            "releaseId = ?");
452    
453                            ps.setDate(1, new Date(System.currentTimeMillis()));
454                            ps.setInt(2, state);
455                            ps.setLong(3, ReleaseConstants.DEFAULT_ID);
456    
457                            ps.executeUpdate();
458                    }
459                    finally {
460                            DataAccess.cleanUp(con, ps);
461                    }
462            }
463    
464            private static final String _DELETE_TEMP_IMAGES_1 =
465                    "delete from Image where imageId IN (SELECT articleImageId FROM " +
466                            "JournalArticleImage where tempImage = TRUE)";
467    
468            private static final String _DELETE_TEMP_IMAGES_2 =
469                    "delete from JournalArticleImage where tempImage = TRUE";
470    
471            private static Log _log = LogFactoryUtil.getLog(DBUpgrader.class);
472    
473    }