001    /**
002     * Copyright (c) 2000-2012 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.exception.PortalException;
025    import com.liferay.portal.kernel.log.Log;
026    import com.liferay.portal.kernel.log.LogFactoryUtil;
027    import com.liferay.portal.kernel.spring.aop.Skip;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.ReflectionUtil;
031    import com.liferay.portal.kernel.util.ReleaseInfo;
032    import com.liferay.portal.kernel.util.StringBundler;
033    import com.liferay.portal.kernel.util.Time;
034    import com.liferay.portal.model.Release;
035    import com.liferay.portal.model.ReleaseConstants;
036    import com.liferay.portal.service.ClassNameLocalServiceUtil;
037    import com.liferay.portal.service.ReleaseLocalServiceUtil;
038    import com.liferay.portal.service.ResourceActionLocalServiceUtil;
039    import com.liferay.portal.spring.aop.ServiceBeanAopCacheManager;
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                    // Upgrade
101    
102                    if (_log.isDebugEnabled()) {
103                            _log.debug("Run upgrade process");
104                    }
105    
106                    int buildNumber = ReleaseLocalServiceUtil.getBuildNumberOrCreate();
107    
108                    if (buildNumber > ReleaseInfo.getParentBuildNumber()) {
109                            StringBundler sb = new StringBundler(6);
110    
111                            sb.append("Attempting to deploy an older Liferay Portal version. ");
112                            sb.append("Current build version is ");
113                            sb.append(buildNumber);
114                            sb.append(" and attempting to deploy version ");
115                            sb.append(ReleaseInfo.getParentBuildNumber());
116                            sb.append(".");
117    
118                            throw new IllegalStateException(sb.toString());
119                    }
120                    else if (buildNumber < ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER) {
121                            String msg = "You must first upgrade to Liferay Portal 5.2.3";
122    
123                            System.out.println(msg);
124    
125                            throw new RuntimeException(msg);
126                    }
127    
128                    // Reload SQL
129    
130                    CustomSQLUtil.reloadCustomSQL();
131                    SQLTransformer.reloadSQLTransformer();
132    
133                    // Upgrade build
134    
135                    if (_log.isDebugEnabled()) {
136                            _log.debug("Update build " + buildNumber);
137                    }
138    
139                    _checkPermissionAlgorithm();
140                    _checkReleaseState();
141    
142                    if (PropsValues.UPGRADE_DATABASE_TRANSACTIONS_DISABLED) {
143                            _disableTransactions();
144                    }
145    
146                    try {
147                            StartupHelperUtil.upgradeProcess(buildNumber);
148                    }
149                    catch (Exception e) {
150                            _updateReleaseState(ReleaseConstants.STATE_UPGRADE_FAILURE);
151    
152                            throw e;
153                    }
154                    finally {
155                            if (PropsValues.UPGRADE_DATABASE_TRANSACTIONS_DISABLED) {
156                                    _enableTransactions();
157                            }
158                    }
159    
160                    // Update company key
161    
162                    if (StartupHelperUtil.isUpgraded()) {
163                            if (_log.isDebugEnabled()) {
164                                    _log.debug("Update company key");
165                            }
166    
167                            _updateCompanyKey();
168                    }
169    
170                    // Class names
171    
172                    if (_log.isDebugEnabled()) {
173                            _log.debug("Check class names");
174                    }
175    
176                    ClassNameLocalServiceUtil.checkClassNames();
177    
178                    // Resource actions
179    
180                    if (_log.isDebugEnabled()) {
181                            _log.debug("Check resource actions");
182                    }
183    
184                    ResourceActionLocalServiceUtil.checkResourceActions();
185    
186                    // Delete temporary images
187    
188                    if (_log.isDebugEnabled()) {
189                            _log.debug("Delete temporary images");
190                    }
191    
192                    _deleteTempImages();
193    
194                    // Clear the caches only if the upgrade process was run
195    
196                    if (_log.isDebugEnabled()) {
197                            _log.debug("Clear cache if upgrade process was run");
198                    }
199    
200                    if (StartupHelperUtil.isUpgraded()) {
201                            MultiVMPoolUtil.clear();
202                    }
203            }
204    
205            public static void verify() throws Exception {
206    
207                    // Verify
208    
209                    Release release = null;
210    
211                    try {
212                            release = ReleaseLocalServiceUtil.getRelease(
213                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
214                                    ReleaseInfo.getParentBuildNumber());
215                    }
216                    catch (PortalException pe) {
217                            release = ReleaseLocalServiceUtil.addRelease(
218                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
219                                    ReleaseInfo.getParentBuildNumber());
220                    }
221    
222                    _checkReleaseState();
223    
224                    if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
225                            _disableTransactions();
226                    }
227    
228                    try {
229                            StartupHelperUtil.verifyProcess(release.isVerified());
230                    }
231                    catch (Exception e) {
232                            _updateReleaseState(ReleaseConstants.STATE_VERIFY_FAILURE);
233    
234                            throw e;
235                    }
236                    finally {
237                            if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
238                                    _enableTransactions();
239                            }
240                    }
241    
242                    // Update indexes
243    
244                    if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP) {
245                            StartupHelperUtil.setDropIndexes(true);
246    
247                            StartupHelperUtil.updateIndexes();
248                    }
249                    else if (StartupHelperUtil.isUpgraded()) {
250                            StartupHelperUtil.updateIndexes();
251                    }
252    
253                    // Update release
254    
255                    boolean verified = StartupHelperUtil.isVerified();
256    
257                    if (release.isVerified()) {
258                            verified = true;
259                    }
260    
261                    ReleaseLocalServiceUtil.updateRelease(
262                            release.getReleaseId(), ReleaseInfo.getParentBuildNumber(),
263                            ReleaseInfo.getBuildDate(), verified);
264    
265                    // Enable database caching after verify
266    
267                    CacheRegistryUtil.setActive(true);
268            }
269    
270            private static void _checkPermissionAlgorithm() throws Exception {
271                    long count = _getResourceCodesCount();
272    
273                    if (count == 0) {
274                            return;
275                    }
276    
277                    StringBundler sb = new StringBundler(8);
278    
279                    sb.append("Permission conversion to algorithm 6 has not been ");
280                    sb.append("completed. Please complete the conversion prior to ");
281                    sb.append("starting the portal. The conversion process is ");
282                    sb.append("available in portal versions starting with ");
283                    sb.append(ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER);
284                    sb.append(" and prior to ");
285                    sb.append(ReleaseInfo.RELEASE_6_2_0_BUILD_NUMBER);
286                    sb.append(".");
287    
288                    throw new IllegalStateException(sb.toString());
289            }
290    
291            private static void _checkReleaseState() throws Exception {
292                    int state = _getReleaseState();
293    
294                    if (state == ReleaseConstants.STATE_GOOD) {
295                            return;
296                    }
297    
298                    StringBundler sb = new StringBundler(6);
299    
300                    sb.append("The database contains changes from a previous ");
301                    sb.append("upgrade attempt that failed. Please restore the old ");
302                    sb.append("database and file system and retry the upgrade. A ");
303                    sb.append("patch may be required if the upgrade failed due to a");
304                    sb.append(" bug or an unforeseen data permutation that resulted ");
305                    sb.append("from a corrupt database.");
306    
307                    throw new IllegalStateException(sb.toString());
308            }
309    
310            private static void _deleteTempImages() throws Exception {
311                    DB db = DBFactoryUtil.getDB();
312    
313                    db.runSQL(_DELETE_TEMP_IMAGES_1);
314                    db.runSQL(_DELETE_TEMP_IMAGES_2);
315            }
316    
317            private static void _disableTransactions() throws Exception {
318                    if (_log.isDebugEnabled()) {
319                            _log.debug("Disable transactions");
320                    }
321    
322                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = false;
323    
324                    Field field = ReflectionUtil.getDeclaredField(
325                            ServiceBeanAopCacheManager.class, "_annotations");
326    
327                    field.set(
328                            null,
329                            new HashMap<MethodInvocation, Annotation[]>() {
330    
331                                    @Override
332                                    public Annotation[] get(Object key) {
333                                            return _annotations;
334                                    }
335    
336                                    private Annotation[] _annotations = new Annotation[] {
337                                            new Skip() {
338    
339                                                    public Class<? extends Annotation> annotationType() {
340                                                            return Skip.class;
341                                                    }
342    
343                                            }
344                                    };
345    
346                            }
347                    );
348            }
349    
350            private static void _enableTransactions() throws Exception {
351                    if (_log.isDebugEnabled()) {
352                            _log.debug("Enable transactions");
353                    }
354    
355                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = GetterUtil.getBoolean(
356                            PropsUtil.get(PropsKeys.SPRING_HIBERNATE_SESSION_DELEGATED));
357    
358                    Field field = ReflectionUtil.getDeclaredField(
359                            ServiceBeanAopCacheManager.class, "_annotations");
360    
361                    field.set(
362                            null, new ConcurrentHashMap<MethodInvocation, Annotation[]>());
363            }
364    
365            private static int _getReleaseState() throws Exception {
366                    Connection con = null;
367                    PreparedStatement ps = null;
368                    ResultSet rs = null;
369    
370                    try {
371                            con = DataAccess.getConnection();
372    
373                            ps = con.prepareStatement(
374                                    "select state_ from Release_ where releaseId = ?");
375    
376                            ps.setLong(1, ReleaseConstants.DEFAULT_ID);
377    
378                            rs = ps.executeQuery();
379    
380                            if (rs.next()) {
381                                    return rs.getInt("state_");
382                            }
383    
384                            throw new IllegalArgumentException(
385                                    "No Release exists with the primary key " +
386                                            ReleaseConstants.DEFAULT_ID);
387                    }
388                    finally {
389                            DataAccess.cleanUp(con, ps, rs);
390                    }
391            }
392    
393            private static long _getResourceCodesCount() throws Exception {
394                    Connection con = null;
395                    PreparedStatement ps = null;
396                    ResultSet rs = null;
397    
398                    try {
399                            con = DataAccess.getConnection();
400    
401                            ps = con.prepareStatement("select count(*) from ResourceCode");
402    
403                            rs = ps.executeQuery();
404    
405                            if (rs.next()) {
406                                    int count = rs.getInt(1);
407    
408                                    return count;
409                            }
410    
411                            return 0;
412                    }
413                    catch (Exception e) {
414                            return 0;
415                    }
416                    finally {
417                            DataAccess.cleanUp(con, ps, rs);
418                    }
419            }
420    
421            private static void _updateCompanyKey() throws Exception {
422                    DB db = DBFactoryUtil.getDB();
423    
424                    db.runSQL("update Company set key_ = null");
425            }
426    
427            private static void _updateReleaseState(int state) throws Exception {
428                    Connection con = null;
429                    PreparedStatement ps = null;
430    
431                    try {
432                            con = DataAccess.getConnection();
433    
434                            ps = con.prepareStatement(
435                                    "update Release_ set modifiedDate = ?, state_ = ? where " +
436                                            "releaseId = ?");
437    
438                            ps.setDate(1, new Date(System.currentTimeMillis()));
439                            ps.setInt(2, state);
440                            ps.setLong(3, ReleaseConstants.DEFAULT_ID);
441    
442                            ps.executeUpdate();
443                    }
444                    finally {
445                            DataAccess.cleanUp(con, ps);
446                    }
447            }
448    
449            private static final String _DELETE_TEMP_IMAGES_1 =
450                    "delete from Image where imageId IN (SELECT articleImageId FROM " +
451                            "JournalArticleImage where tempImage = TRUE)";
452    
453            private static final String _DELETE_TEMP_IMAGES_2 =
454                    "delete from JournalArticleImage where tempImage = TRUE";
455    
456            private static Log _log = LogFactoryUtil.getLog(DBUpgrader.class);
457    
458    }