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