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.service.impl;
016    
017    import com.liferay.portal.events.StartupHelperUtil;
018    import com.liferay.portal.kernel.dao.db.DB;
019    import com.liferay.portal.kernel.dao.db.DBManagerUtil;
020    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
021    import com.liferay.portal.kernel.exception.NoSuchReleaseException;
022    import com.liferay.portal.kernel.exception.PortalException;
023    import com.liferay.portal.kernel.exception.SystemException;
024    import com.liferay.portal.kernel.log.Log;
025    import com.liferay.portal.kernel.log.LogFactoryUtil;
026    import com.liferay.portal.kernel.model.Release;
027    import com.liferay.portal.kernel.model.ReleaseConstants;
028    import com.liferay.portal.kernel.upgrade.OlderVersionException;
029    import com.liferay.portal.kernel.upgrade.UpgradeProcess;
030    import com.liferay.portal.kernel.upgrade.util.UpgradeProcessUtil;
031    import com.liferay.portal.kernel.util.GetterUtil;
032    import com.liferay.portal.kernel.util.PropsKeys;
033    import com.liferay.portal.kernel.util.StringBundler;
034    import com.liferay.portal.kernel.util.StringUtil;
035    import com.liferay.portal.kernel.util.Validator;
036    import com.liferay.portal.service.base.ReleaseLocalServiceBaseImpl;
037    import com.liferay.portal.util.PropsUtil;
038    import com.liferay.portal.util.PropsValues;
039    
040    import java.sql.Connection;
041    import java.sql.PreparedStatement;
042    import java.sql.ResultSet;
043    
044    import java.util.Date;
045    import java.util.List;
046    import java.util.Properties;
047    
048    /**
049     * @author Brian Wing Shun Chan
050     */
051    public class ReleaseLocalServiceImpl extends ReleaseLocalServiceBaseImpl {
052    
053            @Override
054            public Release addRelease(String servletContextName, int buildNumber) {
055                    Release release = null;
056    
057                    if (servletContextName.equals(
058                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME)) {
059    
060                            release = releasePersistence.create(ReleaseConstants.DEFAULT_ID);
061                    }
062                    else {
063                            long releaseId = counterLocalService.increment();
064    
065                            release = releasePersistence.create(releaseId);
066                    }
067    
068                    Date now = new Date();
069    
070                    release.setCreateDate(now);
071                    release.setModifiedDate(now);
072                    release.setServletContextName(servletContextName);
073                    release.setBuildNumber(buildNumber);
074    
075                    if (servletContextName.equals(
076                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME)) {
077    
078                            release.setTestString(ReleaseConstants.TEST_STRING);
079                    }
080    
081                    releasePersistence.update(release);
082    
083                    return release;
084            }
085    
086            @Override
087            public Release addRelease(String servletContextName, String schemaVersion) {
088                    Release release = null;
089    
090                    if (servletContextName.equals(
091                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME)) {
092    
093                            release = releasePersistence.create(ReleaseConstants.DEFAULT_ID);
094                    }
095    
096                    else {
097                            long releaseId = counterLocalService.increment();
098    
099                            release = releasePersistence.create(releaseId);
100                    }
101    
102                    Date now = new Date();
103    
104                    release.setCreateDate(now);
105                    release.setModifiedDate(now);
106                    release.setServletContextName(servletContextName);
107                    release.setSchemaVersion(schemaVersion);
108    
109                    if (servletContextName.equals(
110                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME)) {
111    
112                            release.setTestString(ReleaseConstants.TEST_STRING);
113                    }
114    
115                    releasePersistence.update(release);
116    
117                    return release;
118            }
119    
120            @Override
121            public void createTablesAndPopulate() {
122                    try {
123                            if (_log.isInfoEnabled()) {
124                                    _log.info("Create tables and populate with default data");
125                            }
126    
127                            DB db = DBManagerUtil.getDB();
128    
129                            db.runSQLTemplate("portal-tables.sql", false);
130                            db.runSQLTemplate("portal-data-common.sql", false);
131                            db.runSQLTemplate("portal-data-counter.sql", false);
132                            db.runSQLTemplate("portal-data-release.sql", false);
133                            db.runSQLTemplate("indexes.sql", false);
134                            db.runSQLTemplate("sequences.sql", false);
135    
136                            StartupHelperUtil.setDbNew(true);
137                    }
138                    catch (Exception e) {
139                            _log.error(e, e);
140    
141                            throw new SystemException(e);
142                    }
143            }
144    
145            @Override
146            public Release fetchRelease(String servletContextName) {
147                    if (Validator.isNull(servletContextName)) {
148                            throw new IllegalArgumentException("Servlet context name is null");
149                    }
150    
151                    Release release = null;
152    
153                    if (servletContextName.equals(
154                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME)) {
155    
156                            release = releasePersistence.fetchByPrimaryKey(
157                                    ReleaseConstants.DEFAULT_ID);
158                    }
159                    else {
160                            release = releasePersistence.fetchByServletContextName(
161                                    servletContextName);
162                    }
163    
164                    return release;
165            }
166    
167            @Override
168            public int getBuildNumberOrCreate() throws PortalException {
169    
170                    // Gracefully add version column
171    
172                    DB db = DBManagerUtil.getDB();
173    
174                    try {
175                            db.runSQL(
176                                    "alter table Release_ add schemaVersion VARCHAR(75) null");
177    
178                            populateVersion();
179                    }
180                    catch (Exception e) {
181                            if (_log.isDebugEnabled()) {
182                                    _log.debug(e.getMessage());
183                            }
184                    }
185    
186                    // Get release build number
187    
188                    Connection con = null;
189                    PreparedStatement ps = null;
190                    ResultSet rs = null;
191    
192                    try {
193                            con = DataAccess.getConnection();
194    
195                            ps = con.prepareStatement(_GET_BUILD_NUMBER);
196    
197                            ps.setLong(1, ReleaseConstants.DEFAULT_ID);
198    
199                            rs = ps.executeQuery();
200    
201                            if (rs.next()) {
202                                    int buildNumber = rs.getInt("buildNumber");
203    
204                                    if (_log.isDebugEnabled()) {
205                                            _log.debug("Build number " + buildNumber);
206                                    }
207    
208                                    // Gracefully add state_ column
209    
210                                    try {
211                                            db.runSQL("alter table Release_ add state_ INTEGER");
212                                    }
213                                    catch (Exception e) {
214                                            if (_log.isDebugEnabled()) {
215                                                    _log.debug(e.getMessage());
216                                            }
217                                    }
218    
219                                    testSupportsStringCaseSensitiveQuery();
220    
221                                    return buildNumber;
222                            }
223                    }
224                    catch (Exception e) {
225                            if (_log.isWarnEnabled()) {
226                                    _log.warn(e.getMessage());
227                            }
228                    }
229                    finally {
230                            DataAccess.cleanUp(con, ps, rs);
231                    }
232    
233                    // Create tables and populate with default data
234    
235                    if (GetterUtil.getBoolean(
236                                    PropsUtil.get(PropsKeys.SCHEMA_RUN_ENABLED))) {
237    
238                            releaseLocalService.createTablesAndPopulate();
239    
240                            testSupportsStringCaseSensitiveQuery();
241    
242                            Release release = fetchRelease(
243                                    ReleaseConstants.DEFAULT_SERVLET_CONTEXT_NAME);
244    
245                            return release.getBuildNumber();
246                    }
247                    else {
248                            throw new NoSuchReleaseException(
249                                    "The database needs to be populated");
250                    }
251            }
252    
253            @Override
254            public Release updateRelease(
255                            long releaseId, String schemaVersion, int buildNumber,
256                            Date buildDate, boolean verified)
257                    throws PortalException {
258    
259                    Release release = releasePersistence.findByPrimaryKey(releaseId);
260    
261                    release.setModifiedDate(new Date());
262                    release.setSchemaVersion(schemaVersion);
263                    release.setBuildNumber(buildNumber);
264                    release.setBuildDate(buildDate);
265                    release.setVerified(verified);
266    
267                    releasePersistence.update(release);
268    
269                    return release;
270            }
271    
272            @Override
273            public void updateRelease(
274                            String servletContextName, List<UpgradeProcess> upgradeProcesses,
275                            int buildNumber, int previousBuildNumber, boolean indexOnUpgrade)
276                    throws PortalException {
277    
278                    if (buildNumber <= 0) {
279                            _log.error(
280                                    "Skipping upgrade processes for " + servletContextName +
281                                            " because \"release.info.build.number\" is not specified");
282    
283                            return;
284                    }
285    
286                    Release release = releaseLocalService.fetchRelease(servletContextName);
287    
288                    if (release == null) {
289                            release = releaseLocalService.addRelease(
290                                    servletContextName, previousBuildNumber);
291                    }
292    
293                    if (buildNumber == release.getBuildNumber()) {
294                            if (_log.isDebugEnabled()) {
295                                    _log.debug(
296                                            "Skipping upgrade processes for " + servletContextName +
297                                                    " because it is already up to date");
298                            }
299                    }
300                    else if (buildNumber < release.getBuildNumber()) {
301                            throw new OlderVersionException(
302                                    "Skipping upgrade processes for " + servletContextName +
303                                            " because you are trying to upgrade with an older version");
304                    }
305                    else {
306                            UpgradeProcessUtil.upgradeProcess(
307                                    release.getBuildNumber(), upgradeProcesses, indexOnUpgrade);
308                    }
309    
310                    releaseLocalService.updateRelease(
311                            release.getReleaseId(), release.getSchemaVersion(), buildNumber,
312                            null, true);
313            }
314    
315            @Override
316            public void updateRelease(
317                            String servletContextName, List<UpgradeProcess> upgradeProcesses,
318                            Properties unfilteredPortalProperties)
319                    throws Exception {
320    
321                    int buildNumber = GetterUtil.getInteger(
322                            unfilteredPortalProperties.getProperty(
323                                    PropsKeys.RELEASE_INFO_BUILD_NUMBER));
324                    int previousBuildNumber = GetterUtil.getInteger(
325                            unfilteredPortalProperties.getProperty(
326                                    PropsKeys.RELEASE_INFO_PREVIOUS_BUILD_NUMBER),
327                            buildNumber);
328                    boolean indexOnUpgrade = GetterUtil.getBoolean(
329                            unfilteredPortalProperties.getProperty(PropsKeys.INDEX_ON_UPGRADE),
330                            PropsValues.INDEX_ON_UPGRADE);
331    
332                    updateRelease(
333                            servletContextName, upgradeProcesses, buildNumber,
334                            previousBuildNumber, indexOnUpgrade);
335            }
336    
337            @Override
338            public void updateRelease(
339                    String servletContextName, String schemaVersion,
340                    String previousSchemaVersion) {
341    
342                    Release release = releaseLocalService.fetchRelease(servletContextName);
343    
344                    if (release == null) {
345                            if (previousSchemaVersion.equals("0.0.0")) {
346                                    release = releaseLocalService.addRelease(
347                                            servletContextName, previousSchemaVersion);
348                            }
349                            else {
350                                    throw new IllegalStateException(
351                                            "Unable to update release because it does not exist");
352                            }
353                    }
354    
355                    String currentSchemaVersion = release.getSchemaVersion();
356    
357                    if (Validator.isNull(currentSchemaVersion)) {
358                            currentSchemaVersion = "0.0.0";
359                    }
360    
361                    if (!previousSchemaVersion.equals(currentSchemaVersion)) {
362                            StringBundler sb = new StringBundler(5);
363    
364                            sb.append("Unable to update release because the previous schema ");
365                            sb.append("version ");
366                            sb.append(previousSchemaVersion);
367                            sb.append(" does not match the expected schema version ");
368                            sb.append(currentSchemaVersion);
369    
370                            throw new IllegalStateException(sb.toString());
371                    }
372    
373                    release.setSchemaVersion(schemaVersion);
374    
375                    releasePersistence.update(release);
376            }
377    
378            protected void populateVersion() {
379    
380                    // This method is called if and only if the version column did not
381                    // previously exist and was safely added to the database
382    
383            }
384    
385            protected void testSupportsStringCaseSensitiveQuery() {
386                    DB db = DBManagerUtil.getDB();
387    
388                    int count = testSupportsStringCaseSensitiveQuery(
389                            ReleaseConstants.TEST_STRING);
390    
391                    if (count == 0) {
392                            try {
393                                    db.runSQL(
394                                            "alter table Release_ add testString VARCHAR(1024) null");
395                            }
396                            catch (Exception e) {
397                                    if (_log.isDebugEnabled()) {
398                                            _log.debug(e.getMessage());
399                                    }
400                            }
401    
402                            try {
403                                    db.runSQL(
404                                            "update Release_ set testString = '" +
405                                                    ReleaseConstants.TEST_STRING + "'");
406                            }
407                            catch (Exception e) {
408                                    if (_log.isDebugEnabled()) {
409                                            _log.debug(e.getMessage());
410                                    }
411                            }
412    
413                            count = testSupportsStringCaseSensitiveQuery(
414                                    ReleaseConstants.TEST_STRING);
415                    }
416    
417                    if (count == 0) {
418                            throw new SystemException(
419                                    "Release_ table was not initialized properly");
420                    }
421    
422                    count = testSupportsStringCaseSensitiveQuery(
423                            StringUtil.toUpperCase(ReleaseConstants.TEST_STRING));
424    
425                    if (count == 0) {
426                            db.setSupportsStringCaseSensitiveQuery(true);
427                    }
428                    else {
429                            db.setSupportsStringCaseSensitiveQuery(false);
430                    }
431            }
432    
433            protected int testSupportsStringCaseSensitiveQuery(String testString) {
434                    int count = 0;
435    
436                    Connection con = null;
437                    PreparedStatement ps = null;
438                    ResultSet rs = null;
439    
440                    try {
441                            con = DataAccess.getConnection();
442    
443                            ps = con.prepareStatement(_TEST_DATABASE_STRING_CASE_SENSITIVITY);
444    
445                            ps.setLong(1, ReleaseConstants.DEFAULT_ID);
446                            ps.setString(2, testString);
447    
448                            rs = ps.executeQuery();
449    
450                            if (rs.next()) {
451                                    count = rs.getInt(1);
452                            }
453                    }
454                    catch (Exception e) {
455                            if (_log.isWarnEnabled()) {
456                                    _log.warn(e.getMessage());
457                            }
458                    }
459                    finally {
460                            DataAccess.cleanUp(con, ps, rs);
461                    }
462    
463                    return count;
464            }
465    
466            private static final String _GET_BUILD_NUMBER =
467                    "select buildNumber from Release_ where releaseId = ?";
468    
469            private static final String _TEST_DATABASE_STRING_CASE_SENSITIVITY =
470                    "select count(*) from Release_ where releaseId = ? and testString = ?";
471    
472            private static final Log _log = LogFactoryUtil.getLog(
473                    ReleaseLocalServiceImpl.class);
474    
475    }