001    /**
002     * Copyright (c) 2000-present 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.registry.Registry;
044    import com.liferay.registry.RegistryUtil;
045    import com.liferay.registry.ServiceRegistrar;
046    import com.liferay.util.dao.orm.CustomSQLUtil;
047    
048    import java.lang.annotation.Annotation;
049    import java.lang.reflect.Field;
050    
051    import java.sql.Connection;
052    import java.sql.Date;
053    import java.sql.PreparedStatement;
054    import java.sql.ResultSet;
055    
056    import java.util.HashMap;
057    import java.util.Map;
058    import java.util.concurrent.ConcurrentHashMap;
059    
060    import org.aopalliance.intercept.MethodInvocation;
061    
062    import org.apache.commons.lang.time.StopWatch;
063    
064    /**
065     * @author Michael C. Han
066     * @author Brian Wing Shun Chan
067     */
068    public class DBUpgrader {
069    
070            public static void main(String[] args) {
071                    try {
072                            StopWatch stopWatch = new StopWatch();
073    
074                            stopWatch.start();
075    
076                            InitUtil.initWithSpring(true);
077    
078                            upgrade();
079                            verify();
080    
081                            System.out.println(
082                                    "\nCompleted upgrade and verify processes in " +
083                                            (stopWatch.getTime() / Time.SECOND) + " seconds");
084    
085                            System.exit(0);
086                    }
087                    catch (Exception e) {
088                            e.printStackTrace();
089    
090                            System.exit(1);
091                    }
092            }
093    
094            public static void upgrade() throws Exception {
095    
096                    // Disable database caching before upgrade
097    
098                    if (_log.isDebugEnabled()) {
099                            _log.debug("Disable cache registry");
100                    }
101    
102                    CacheRegistryUtil.setActive(false);
103    
104                    // Check release
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                    // 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                    // Reload SQL
156    
157                    CustomSQLUtil.reloadCustomSQL();
158                    SQLTransformer.reloadSQLTransformer();
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                    // Check class names
171    
172                    if (_log.isDebugEnabled()) {
173                            _log.debug("Check class names");
174                    }
175    
176                    ClassNameLocalServiceUtil.checkClassNames();
177    
178                    // Check resource actions
179    
180                    if (_log.isDebugEnabled()) {
181                            _log.debug("Check resource actions");
182                    }
183    
184                    ResourceActionLocalServiceUtil.checkResourceActions();
185    
186                    // Clear the caches only if the upgrade process was run
187    
188                    if (_log.isDebugEnabled()) {
189                            _log.debug("Clear cache if upgrade process was run");
190                    }
191    
192                    if (StartupHelperUtil.isUpgraded()) {
193                            MultiVMPoolUtil.clear();
194                    }
195            }
196    
197            public static void verify() throws Exception {
198    
199                    // Check release
200    
201                    Release release = ReleaseLocalServiceUtil.fetchRelease(
202                            ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME);
203    
204                    if (release == null) {
205                            release = ReleaseLocalServiceUtil.addRelease(
206                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
207                                    ReleaseInfo.getParentBuildNumber());
208                    }
209    
210                    _checkReleaseState(release.getState());
211    
212                    // Update indexes
213    
214                    if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP) {
215                            StartupHelperUtil.setDropIndexes(true);
216    
217                            StartupHelperUtil.updateIndexes();
218                    }
219                    else if (StartupHelperUtil.isUpgraded()) {
220                            StartupHelperUtil.updateIndexes();
221                    }
222    
223                    // Verify
224    
225                    if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
226                            _disableTransactions();
227                    }
228    
229                    boolean newBuildNumber = false;
230    
231                    if (ReleaseInfo.getBuildNumber() > release.getBuildNumber()) {
232                            newBuildNumber = true;
233                    }
234    
235                    try {
236                            StartupHelperUtil.verifyProcess(
237                                    newBuildNumber, release.isVerified());
238                    }
239                    catch (Exception e) {
240                            _updateReleaseState(ReleaseConstants.STATE_VERIFY_FAILURE);
241    
242                            _log.error(
243                                    "Unable to execute verify process: " + e.getMessage(), e);
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                    release = ReleaseLocalServiceUtil.updateRelease(
270                            release.getReleaseId(), ReleaseInfo.getParentBuildNumber(),
271                            ReleaseInfo.getBuildDate(), verified);
272    
273                    // Enable database caching after verify
274    
275                    CacheRegistryUtil.setActive(true);
276    
277                    // Register release service
278    
279                    Registry registry = RegistryUtil.getRegistry();
280    
281                    ServiceRegistrar<Release> serviceRegistrar =
282                            registry.getServiceRegistrar(Release.class);
283    
284                    Map<String, Object> properties = new HashMap<>();
285    
286                    properties.put("build.date", release.getBuildDate());
287                    properties.put("build.number", release.getBuildNumber());
288                    properties.put("servlet.context.name", release.getServletContextName());
289    
290                    serviceRegistrar.registerService(Release.class, release, properties);
291            }
292    
293            private static void _checkPermissionAlgorithm() throws Exception {
294                    long count = _getResourceCodesCount();
295    
296                    if (count == 0) {
297                            return;
298                    }
299    
300                    StringBundler sb = new StringBundler(8);
301    
302                    sb.append("Permission conversion to algorithm 6 has not been ");
303                    sb.append("completed. Please complete the conversion prior to ");
304                    sb.append("starting the portal. The conversion process is ");
305                    sb.append("available in portal versions starting with ");
306                    sb.append(ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER);
307                    sb.append(" and prior to ");
308                    sb.append(ReleaseInfo.RELEASE_6_2_0_BUILD_NUMBER);
309                    sb.append(".");
310    
311                    throw new IllegalStateException(sb.toString());
312            }
313    
314            private static void _checkReleaseState(int state) throws Exception {
315                    if (state == ReleaseConstants.STATE_GOOD) {
316                            return;
317                    }
318    
319                    StringBundler sb = new StringBundler(6);
320    
321                    sb.append("The database contains changes from a previous ");
322                    sb.append("upgrade attempt that failed. Please restore the old ");
323                    sb.append("database and file system and retry the upgrade. A ");
324                    sb.append("patch may be required if the upgrade failed due to a");
325                    sb.append(" bug or an unforeseen data permutation that resulted ");
326                    sb.append("from a corrupt database.");
327    
328                    throw new IllegalStateException(sb.toString());
329            }
330    
331            private static void _disableTransactions() throws Exception {
332                    if (_log.isDebugEnabled()) {
333                            _log.debug("Disable transactions");
334                    }
335    
336                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = false;
337    
338                    Field field = ReflectionUtil.getDeclaredField(
339                            ServiceBeanAopCacheManager.class, "_annotations");
340    
341                    field.set(
342                            null,
343                            new HashMap<MethodInvocation, Annotation[]>() {
344    
345                                    @Override
346                                    public Annotation[] get(Object key) {
347                                            return _annotations;
348                                    }
349    
350                                    private Annotation[] _annotations = new Annotation[] {
351                                            new Skip() {
352    
353                                                    @Override
354                                                    public Class<? extends Annotation> annotationType() {
355                                                            return Skip.class;
356                                                    }
357    
358                                            }
359                                    };
360    
361                            }
362                    );
363            }
364    
365            private static void _enableTransactions() throws Exception {
366                    if (_log.isDebugEnabled()) {
367                            _log.debug("Enable transactions");
368                    }
369    
370                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = GetterUtil.getBoolean(
371                            PropsUtil.get(PropsKeys.SPRING_HIBERNATE_SESSION_DELEGATED));
372    
373                    Field field = ReflectionUtil.getDeclaredField(
374                            ServiceBeanAopCacheManager.class, "_annotations");
375    
376                    field.set(
377                            null, new ConcurrentHashMap<MethodInvocation, Annotation[]>());
378    
379                    ServiceBeanAopCacheManagerUtil.reset();
380            }
381    
382            private static int _getReleaseState() throws Exception {
383                    Connection con = null;
384                    PreparedStatement ps = null;
385                    ResultSet rs = null;
386    
387                    try {
388                            con = DataAccess.getConnection();
389    
390                            ps = con.prepareStatement(
391                                    "select state_ from Release_ where releaseId = ?");
392    
393                            ps.setLong(1, ReleaseConstants.DEFAULT_ID);
394    
395                            rs = ps.executeQuery();
396    
397                            if (rs.next()) {
398                                    return rs.getInt("state_");
399                            }
400    
401                            throw new IllegalArgumentException(
402                                    "No Release exists with the primary key " +
403                                            ReleaseConstants.DEFAULT_ID);
404                    }
405                    finally {
406                            DataAccess.cleanUp(con, ps, rs);
407                    }
408            }
409    
410            private static long _getResourceCodesCount() throws Exception {
411                    Connection con = null;
412                    PreparedStatement ps = null;
413                    ResultSet rs = null;
414    
415                    try {
416                            con = DataAccess.getConnection();
417    
418                            ps = con.prepareStatement("select count(*) from ResourceCode");
419    
420                            rs = ps.executeQuery();
421    
422                            if (rs.next()) {
423                                    int count = rs.getInt(1);
424    
425                                    return count;
426                            }
427    
428                            return 0;
429                    }
430                    catch (Exception e) {
431                            return 0;
432                    }
433                    finally {
434                            DataAccess.cleanUp(con, ps, rs);
435                    }
436            }
437    
438            private static void _updateCompanyKey() throws Exception {
439                    DB db = DBFactoryUtil.getDB();
440    
441                    db.runSQL("update Company set key_ = null");
442            }
443    
444            private static void _updateReleaseState(int state) throws Exception {
445                    Connection con = null;
446                    PreparedStatement ps = null;
447    
448                    try {
449                            con = DataAccess.getConnection();
450    
451                            ps = con.prepareStatement(
452                                    "update Release_ set modifiedDate = ?, state_ = ? where " +
453                                            "releaseId = ?");
454    
455                            ps.setDate(1, new Date(System.currentTimeMillis()));
456                            ps.setInt(2, state);
457                            ps.setLong(3, ReleaseConstants.DEFAULT_ID);
458    
459                            ps.executeUpdate();
460                    }
461                    finally {
462                            DataAccess.cleanUp(con, ps);
463                    }
464            }
465    
466            private static final Log _log = LogFactoryUtil.getLog(DBUpgrader.class);
467    
468    }