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                    // 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                    // Check release
208    
209                    Release release = ReleaseLocalServiceUtil.fetchRelease(
210                            ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME);
211    
212                    if (release == null) {
213                            release = ReleaseLocalServiceUtil.addRelease(
214                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME,
215                                    ReleaseInfo.getParentBuildNumber());
216                    }
217    
218                    _checkReleaseState(release.getState());
219    
220                    // Update indexes
221    
222                    if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP) {
223                            StartupHelperUtil.setDropIndexes(true);
224    
225                            StartupHelperUtil.updateIndexes();
226                    }
227                    else if (StartupHelperUtil.isUpgraded()) {
228                            StartupHelperUtil.updateIndexes();
229                    }
230    
231                    // Verify
232    
233                    if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
234                            _disableTransactions();
235                    }
236    
237                    boolean newBuildNumber = false;
238    
239                    if (ReleaseInfo.getBuildNumber() > release.getBuildNumber()) {
240                            newBuildNumber = true;
241                    }
242    
243                    try {
244                            StartupHelperUtil.verifyProcess(
245                                    newBuildNumber, release.isVerified());
246                    }
247                    catch (Exception e) {
248                            _updateReleaseState(ReleaseConstants.STATE_VERIFY_FAILURE);
249    
250                            _log.error(
251                                    "Unable to execute verify process: " + e.getMessage(), e);
252    
253                            throw e;
254                    }
255                    finally {
256                            if (PropsValues.VERIFY_DATABASE_TRANSACTIONS_DISABLED) {
257                                    _enableTransactions();
258                            }
259                    }
260    
261                    // Update indexes
262    
263                    if (PropsValues.DATABASE_INDEXES_UPDATE_ON_STARTUP ||
264                            StartupHelperUtil.isUpgraded()) {
265    
266                            StartupHelperUtil.updateIndexes(false);
267                    }
268    
269                    // Update release
270    
271                    boolean verified = StartupHelperUtil.isVerified();
272    
273                    if (release.isVerified()) {
274                            verified = true;
275                    }
276    
277                    release = ReleaseLocalServiceUtil.updateRelease(
278                            release.getReleaseId(), ReleaseInfo.getParentBuildNumber(),
279                            ReleaseInfo.getBuildDate(), verified);
280    
281                    // Enable database caching after verify
282    
283                    CacheRegistryUtil.setActive(true);
284    
285                    // Register release service
286    
287                    Registry registry = RegistryUtil.getRegistry();
288    
289                    ServiceRegistrar<Release> serviceRegistrar =
290                            registry.getServiceRegistrar(Release.class);
291    
292                    Map<String, Object> properties = new HashMap<>();
293    
294                    properties.put("build.date", release.getBuildDate());
295                    properties.put("build.number", release.getBuildNumber());
296                    properties.put("servlet.context.name", release.getServletContextName());
297    
298                    serviceRegistrar.registerService(Release.class, release, properties);
299            }
300    
301            private static void _checkPermissionAlgorithm() throws Exception {
302                    long count = _getResourceCodesCount();
303    
304                    if (count == 0) {
305                            return;
306                    }
307    
308                    StringBundler sb = new StringBundler(8);
309    
310                    sb.append("Permission conversion to algorithm 6 has not been ");
311                    sb.append("completed. Please complete the conversion prior to ");
312                    sb.append("starting the portal. The conversion process is ");
313                    sb.append("available in portal versions starting with ");
314                    sb.append(ReleaseInfo.RELEASE_5_2_3_BUILD_NUMBER);
315                    sb.append(" and prior to ");
316                    sb.append(ReleaseInfo.RELEASE_6_2_0_BUILD_NUMBER);
317                    sb.append(".");
318    
319                    throw new IllegalStateException(sb.toString());
320            }
321    
322            private static void _checkReleaseState(int state) throws Exception {
323                    if (state == ReleaseConstants.STATE_GOOD) {
324                            return;
325                    }
326    
327                    StringBundler sb = new StringBundler(6);
328    
329                    sb.append("The database contains changes from a previous ");
330                    sb.append("upgrade attempt that failed. Please restore the old ");
331                    sb.append("database and file system and retry the upgrade. A ");
332                    sb.append("patch may be required if the upgrade failed due to a");
333                    sb.append(" bug or an unforeseen data permutation that resulted ");
334                    sb.append("from a corrupt database.");
335    
336                    throw new IllegalStateException(sb.toString());
337            }
338    
339            private static void _deleteTempImages() throws Exception {
340                    DB db = DBFactoryUtil.getDB();
341    
342                    db.runSQL(_DELETE_TEMP_IMAGES_1);
343                    db.runSQL(_DELETE_TEMP_IMAGES_2);
344            }
345    
346            private static void _disableTransactions() throws Exception {
347                    if (_log.isDebugEnabled()) {
348                            _log.debug("Disable transactions");
349                    }
350    
351                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = false;
352    
353                    Field field = ReflectionUtil.getDeclaredField(
354                            ServiceBeanAopCacheManager.class, "_annotations");
355    
356                    field.set(
357                            null,
358                            new HashMap<MethodInvocation, Annotation[]>() {
359    
360                                    @Override
361                                    public Annotation[] get(Object key) {
362                                            return _annotations;
363                                    }
364    
365                                    private Annotation[] _annotations = new Annotation[] {
366                                            new Skip() {
367    
368                                                    @Override
369                                                    public Class<? extends Annotation> annotationType() {
370                                                            return Skip.class;
371                                                    }
372    
373                                            }
374                                    };
375    
376                            }
377                    );
378            }
379    
380            private static void _enableTransactions() throws Exception {
381                    if (_log.isDebugEnabled()) {
382                            _log.debug("Enable transactions");
383                    }
384    
385                    PropsValues.SPRING_HIBERNATE_SESSION_DELEGATED = GetterUtil.getBoolean(
386                            PropsUtil.get(PropsKeys.SPRING_HIBERNATE_SESSION_DELEGATED));
387    
388                    Field field = ReflectionUtil.getDeclaredField(
389                            ServiceBeanAopCacheManager.class, "_annotations");
390    
391                    field.set(
392                            null, new ConcurrentHashMap<MethodInvocation, Annotation[]>());
393    
394                    ServiceBeanAopCacheManagerUtil.reset();
395            }
396    
397            private static int _getReleaseState() throws Exception {
398                    Connection con = null;
399                    PreparedStatement ps = null;
400                    ResultSet rs = null;
401    
402                    try {
403                            con = DataAccess.getConnection();
404    
405                            ps = con.prepareStatement(
406                                    "select state_ from Release_ where releaseId = ?");
407    
408                            ps.setLong(1, ReleaseConstants.DEFAULT_ID);
409    
410                            rs = ps.executeQuery();
411    
412                            if (rs.next()) {
413                                    return rs.getInt("state_");
414                            }
415    
416                            throw new IllegalArgumentException(
417                                    "No Release exists with the primary key " +
418                                            ReleaseConstants.DEFAULT_ID);
419                    }
420                    finally {
421                            DataAccess.cleanUp(con, ps, rs);
422                    }
423            }
424    
425            private static long _getResourceCodesCount() throws Exception {
426                    Connection con = null;
427                    PreparedStatement ps = null;
428                    ResultSet rs = null;
429    
430                    try {
431                            con = DataAccess.getConnection();
432    
433                            ps = con.prepareStatement("select count(*) from ResourceCode");
434    
435                            rs = ps.executeQuery();
436    
437                            if (rs.next()) {
438                                    int count = rs.getInt(1);
439    
440                                    return count;
441                            }
442    
443                            return 0;
444                    }
445                    catch (Exception e) {
446                            return 0;
447                    }
448                    finally {
449                            DataAccess.cleanUp(con, ps, rs);
450                    }
451            }
452    
453            private static void _updateCompanyKey() throws Exception {
454                    DB db = DBFactoryUtil.getDB();
455    
456                    db.runSQL("update Company set key_ = null");
457            }
458    
459            private static void _updateReleaseState(int state) throws Exception {
460                    Connection con = null;
461                    PreparedStatement ps = null;
462    
463                    try {
464                            con = DataAccess.getConnection();
465    
466                            ps = con.prepareStatement(
467                                    "update Release_ set modifiedDate = ?, state_ = ? where " +
468                                            "releaseId = ?");
469    
470                            ps.setDate(1, new Date(System.currentTimeMillis()));
471                            ps.setInt(2, state);
472                            ps.setLong(3, ReleaseConstants.DEFAULT_ID);
473    
474                            ps.executeUpdate();
475                    }
476                    finally {
477                            DataAccess.cleanUp(con, ps);
478                    }
479            }
480    
481            private static final String _DELETE_TEMP_IMAGES_1 =
482                    "delete from Image where imageId IN (SELECT articleImageId FROM " +
483                            "JournalArticleImage where tempImage = TRUE)";
484    
485            private static final String _DELETE_TEMP_IMAGES_2 =
486                    "delete from JournalArticleImage where tempImage = TRUE";
487    
488            private static final Log _log = LogFactoryUtil.getLog(DBUpgrader.class);
489    
490    }