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.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.service.ResourceCodeLocalServiceUtil;
040    import com.liferay.portal.spring.aop.ServiceMethodAnnotationCache;
041    import com.liferay.portal.util.InitUtil;
042    import com.liferay.portal.util.PropsUtil;
043    import com.liferay.portal.util.PropsValues;
044    import com.liferay.util.dao.orm.CustomSQLUtil;
045    
046    import java.lang.annotation.Annotation;
047    import java.lang.reflect.Field;
048    
049    import java.sql.Connection;
050    import java.sql.Date;
051    import java.sql.PreparedStatement;
052    import java.sql.ResultSet;
053    
054    import java.util.HashMap;
055    import java.util.concurrent.ConcurrentHashMap;
056    
057    import org.aopalliance.intercept.MethodInvocation;
058    
059    import org.apache.commons.lang.time.StopWatch;
060    
061    /**
062     * @author Michael C. Han
063     * @author Brian Wing Shun Chan
064     */
065    public class DBUpgrader {
066    
067            public static void main(String[] args) {
068                    try {
069                            StopWatch stopWatch = new StopWatch();
070    
071                            stopWatch.start();
072    
073                            InitUtil.initWithSpring();
074    
075                            upgrade();
076                            verify();
077    
078                            System.out.println(
079                                    "\nSuccessfully completed upgrade process in " +
080                                            (stopWatch.getTime() / Time.SECOND) + " seconds.");
081    
082                            System.exit(0);
083                    }
084                    catch (Exception e) {
085                            e.printStackTrace();
086    
087                            System.exit(1);
088                    }
089            }
090    
091            public static void upgrade() throws Exception {
092    
093                    // Disable database caching before upgrade
094    
095                    if (_log.isDebugEnabled()) {
096                            _log.debug("Disable cache registry");
097                    }
098    
099                    CacheRegistryUtil.setActive(false);
100    
101                    // Check release
102    
103                    int buildNumber = ReleaseLocalServiceUtil.getBuildNumberOrCreate();
104    
105                    if (buildNumber > ReleaseInfo.getParentBuildNumber()) {
106                            StringBundler sb = new StringBundler(6);
107    
108                            sb.append("Attempting to deploy an older Liferay Portal version. ");
109                            sb.append("Current build version is ");
110                            sb.append(buildNumber);
111                            sb.append(" and attempting to deploy version ");
112                            sb.append(ReleaseInfo.getParentBuildNumber());
113                            sb.append(".");
114    
115                            throw new IllegalStateException(sb.toString());
116                    }
117                    else if (buildNumber < ReleaseInfo.RELEASE_5_0_0_BUILD_NUMBER) {
118                            String msg = "You must first upgrade to Liferay Portal 5.0.0";
119    
120                            System.out.println(msg);
121    
122                            throw new RuntimeException(msg);
123                    }
124    
125                    // Reload SQL
126    
127                    CustomSQLUtil.reloadCustomSQL();
128                    SQLTransformer.reloadSQLTransformer();
129    
130                    // Upgrade
131    
132                    if (_log.isDebugEnabled()) {
133                            _log.debug("Update build " + buildNumber);
134                    }
135    
136                    _checkReleaseState();
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                    // Resource codes
183    
184                    if (_log.isDebugEnabled()) {
185                            _log.debug("Check resource codes");
186                    }
187    
188                    ResourceCodeLocalServiceUtil.checkResourceCodes();
189    
190                    // Delete temporary images
191    
192                    if (_log.isDebugEnabled()) {
193                            _log.debug("Delete temporary images");
194                    }
195    
196                    _deleteTempImages();
197    
198                    // Clear the caches only if the upgrade process was run
199    
200                    if (_log.isDebugEnabled()) {
201                            _log.debug("Clear cache if upgrade process was run");
202                    }
203    
204                    if (StartupHelperUtil.isUpgraded()) {
205                            MultiVMPoolUtil.clear();
206                    }
207            }
208    
209            public static void verify() throws Exception {
210    
211                    // Check release
212    
213                    Release release = null;
214    
215                    try {
216                            release = ReleaseLocalServiceUtil.getRelease(
217                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
218                                    ReleaseInfo.getParentBuildNumber());
219                    }
220                    catch (PortalException pe) {
221                            release = ReleaseLocalServiceUtil.addRelease(
222                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
223                                    ReleaseInfo.getParentBuildNumber());
224                    }
225    
226                    _checkReleaseState();
227    
228                    // Update indexes
229    
230                    if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP) {
231                            StartupHelperUtil.setDropIndexes(true);
232    
233                            StartupHelperUtil.updateIndexes();
234                    }
235                    else if (StartupHelperUtil.isUpgraded()) {
236                            StartupHelperUtil.updateIndexes();
237                    }
238    
239                    // Verify
240    
241                    if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
242                            _disableTransactions();
243                    }
244    
245                    try {
246                            StartupHelperUtil.verifyProcess(release.isVerified());
247                    }
248                    catch (Exception e) {
249                            _updateReleaseState(ReleaseConstants.STATE_VERIFY_FAILURE);
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 _checkReleaseState() throws Exception {
285                    int state = _getReleaseState();
286    
287                    if (state == ReleaseConstants.STATE_GOOD) {
288                            return;
289                    }
290    
291                    StringBundler sb = new StringBundler(6);
292    
293                    sb.append("The database contains changes from a previous ");
294                    sb.append("upgrade attempt that failed. Please restore the old ");
295                    sb.append("database and file system and retry the upgrade. A ");
296                    sb.append("patch may be required if the upgrade failed due to a");
297                    sb.append(" bug or an unforeseen data permutation that resulted ");
298                    sb.append("from a corrupt database.");
299    
300                    throw new IllegalStateException(sb.toString());
301            }
302    
303            private static void _deleteTempImages() throws Exception {
304                    DB db = DBFactoryUtil.getDB();
305    
306                    db.runSQL(_DELETE_TEMP_IMAGES_1);
307                    db.runSQL(_DELETE_TEMP_IMAGES_2);
308            }
309    
310            private static void _disableTransactions() throws Exception {
311                    if (_log.isDebugEnabled()) {
312                            _log.debug("Disable transactions");
313                    }
314    
315                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = false;
316    
317                    Field field = ReflectionUtil.getDeclaredField(
318                            ServiceMethodAnnotationCache.class, "_annotations");
319    
320                    field.set(
321                            null,
322                            new HashMap<MethodInvocation, Annotation[]>() {
323    
324                                    @Override
325                                    public Annotation[] get(Object key) {
326                                            return _annotations;
327                                    }
328    
329                                    private Annotation[] _annotations = new Annotation[] {
330                                            new Skip() {
331    
332                                                    @Override
333                                                    public Class<? extends Annotation> annotationType() {
334                                                            return Skip.class;
335                                                    }
336    
337                                            }
338                                    };
339    
340                            }
341                    );
342            }
343    
344            private static void _enableTransactions() throws Exception {
345                    if (_log.isDebugEnabled()) {
346                            _log.debug("Enable transactions");
347                    }
348    
349                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = GetterUtil.getBoolean(
350                            PropsUtil.get(PropsKeys.SPRING_HIBERNATE_SESSION_DELEGATED));
351    
352                    Field field = ReflectionUtil.getDeclaredField(
353                            ServiceMethodAnnotationCache.class, "_annotations");
354    
355                    field.set(
356                            null, new ConcurrentHashMap<MethodInvocation, Annotation[]>());
357            }
358    
359            private static int _getReleaseState() throws Exception {
360                    Connection con = null;
361                    PreparedStatement ps = null;
362                    ResultSet rs = null;
363    
364                    try {
365                            con = DataAccess.getConnection();
366    
367                            ps = con.prepareStatement(
368                                    "select state_ from Release_ where releaseId = ?");
369    
370                            ps.setLong(1, ReleaseConstants.DEFAULT_ID);
371    
372                            rs = ps.executeQuery();
373    
374                            if (rs.next()) {
375                                    return rs.getInt("state_");
376                            }
377    
378                            throw new IllegalArgumentException(
379                                    "No Release exists with the primary key " +
380                                            ReleaseConstants.DEFAULT_ID);
381                    }
382                    finally {
383                            DataAccess.cleanUp(con, ps, rs);
384                    }
385            }
386    
387            private static void _updateCompanyKey() throws Exception {
388                    DB db = DBFactoryUtil.getDB();
389    
390                    db.runSQL("update Company set key_ = null");
391            }
392    
393            private static void _updateReleaseState(int state) throws Exception {
394                    Connection con = null;
395                    PreparedStatement ps = null;
396    
397                    try {
398                            con = DataAccess.getConnection();
399    
400                            ps = con.prepareStatement(
401                                    "update Release_ set modifiedDate = ?, state_ = ? where " +
402                                            "releaseId = ?");
403    
404                            ps.setDate(1, new Date(System.currentTimeMillis()));
405                            ps.setInt(2, state);
406                            ps.setLong(3, ReleaseConstants.DEFAULT_ID);
407    
408                            ps.executeUpdate();
409                    }
410                    finally {
411                            DataAccess.cleanUp(con, ps);
412                    }
413            }
414    
415            private static final String _DELETE_TEMP_IMAGES_1 =
416                    "delete from Image where imageId IN (SELECT articleImageId FROM " +
417                            "JournalArticleImage where tempImage = TRUE)";
418    
419            private static final String _DELETE_TEMP_IMAGES_2 =
420                    "delete from JournalArticleImage where tempImage = TRUE";
421    
422            private static Log _log = LogFactoryUtil.getLog(DBUpgrader.class);
423    
424    }