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.exception.OldServiceComponentException;
018    import com.liferay.portal.kernel.cache.CacheRegistryUtil;
019    import com.liferay.portal.kernel.dao.db.DB;
020    import com.liferay.portal.kernel.dao.db.DBManagerUtil;
021    import com.liferay.portal.kernel.exception.PortalException;
022    import com.liferay.portal.kernel.exception.SystemException;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.upgrade.util.UpgradeTable;
026    import com.liferay.portal.kernel.upgrade.util.UpgradeTableFactoryUtil;
027    import com.liferay.portal.kernel.upgrade.util.UpgradeTableListener;
028    import com.liferay.portal.kernel.util.InstanceFactory;
029    import com.liferay.portal.kernel.util.ListUtil;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.xml.Document;
033    import com.liferay.portal.kernel.xml.DocumentException;
034    import com.liferay.portal.kernel.xml.Element;
035    import com.liferay.portal.kernel.xml.SAXReaderUtil;
036    import com.liferay.portal.kernel.xml.UnsecureSAXReaderUtil;
037    import com.liferay.portal.model.ModelHintsUtil;
038    import com.liferay.portal.model.ServiceComponent;
039    import com.liferay.portal.service.base.ServiceComponentLocalServiceBaseImpl;
040    import com.liferay.portal.service.configuration.ServiceComponentConfiguration;
041    import com.liferay.portal.util.PropsValues;
042    
043    import java.io.IOException;
044    
045    import java.lang.reflect.Field;
046    
047    import java.security.PrivilegedExceptionAction;
048    
049    import java.util.ArrayList;
050    import java.util.List;
051    
052    /**
053     * @author Brian Wing Shun Chan
054     */
055    public class ServiceComponentLocalServiceImpl
056            extends ServiceComponentLocalServiceBaseImpl {
057    
058            @Override
059            public void destroyServiceComponent(
060                    ServiceComponentConfiguration serviceComponentConfiguration,
061                    ClassLoader classLoader) {
062    
063                    if (PropsValues.CACHE_CLEAR_ON_PLUGIN_UNDEPLOY) {
064                            CacheRegistryUtil.clear();
065                    }
066            }
067    
068            @Override
069            public List<ServiceComponent> getLatestServiceComponents() {
070                    return serviceComponentFinder.findByMaxBuildNumber();
071            }
072    
073            @Override
074            public ServiceComponent initServiceComponent(
075                            ServiceComponentConfiguration serviceComponentConfiguration,
076                            ClassLoader classLoader, String buildNamespace, long buildNumber,
077                            long buildDate, boolean buildAutoUpgrade)
078                    throws PortalException {
079    
080                    try {
081                            ModelHintsUtil.read(
082                                    classLoader,
083                                    serviceComponentConfiguration.getModelHintsInputStream());
084                    }
085                    catch (Exception e) {
086                            throw new SystemException(e);
087                    }
088    
089                    try {
090                            ModelHintsUtil.read(
091                                    classLoader,
092                                    serviceComponentConfiguration.getModelHintsExtInputStream());
093                    }
094                    catch (Exception e) {
095                            throw new SystemException(e);
096                    }
097    
098                    ServiceComponent serviceComponent = null;
099                    ServiceComponent previousServiceComponent = null;
100    
101                    List<ServiceComponent> serviceComponents =
102                            serviceComponentPersistence.findByBuildNamespace(
103                                    buildNamespace, 0, 1);
104    
105                    if (serviceComponents.isEmpty()) {
106                            long serviceComponentId = counterLocalService.increment();
107    
108                            serviceComponent = serviceComponentPersistence.create(
109                                    serviceComponentId);
110    
111                            serviceComponent.setBuildNamespace(buildNamespace);
112                            serviceComponent.setBuildNumber(buildNumber);
113                            serviceComponent.setBuildDate(buildDate);
114                    }
115                    else {
116                            serviceComponent = serviceComponents.get(0);
117    
118                            if (serviceComponent.getBuildNumber() < buildNumber) {
119                                    previousServiceComponent = serviceComponent;
120    
121                                    long serviceComponentId = counterLocalService.increment();
122    
123                                    serviceComponent = serviceComponentPersistence.create(
124                                            serviceComponentId);
125    
126                                    serviceComponent.setBuildNamespace(buildNamespace);
127                                    serviceComponent.setBuildNumber(buildNumber);
128                                    serviceComponent.setBuildDate(buildDate);
129                            }
130                            else if (serviceComponent.getBuildNumber() > buildNumber) {
131                                    throw new OldServiceComponentException(
132                                            "Build namespace " + buildNamespace + " has build number " +
133                                                    serviceComponent.getBuildNumber() +
134                                                            " which is newer than " + buildNumber);
135                            }
136                            else {
137                                    return serviceComponent;
138                            }
139                    }
140    
141                    try {
142                            Document document = SAXReaderUtil.createDocument(StringPool.UTF8);
143    
144                            Element dataElement = document.addElement("data");
145    
146                            Element tablesSQLElement = dataElement.addElement("tables-sql");
147    
148                            String tablesSQL = StringUtil.read(
149                                    serviceComponentConfiguration.getSQLTablesInputStream());
150    
151                            tablesSQLElement.addCDATA(tablesSQL);
152    
153                            Element sequencesSQLElement = dataElement.addElement(
154                                    "sequences-sql");
155    
156                            String sequencesSQL = StringUtil.read(
157                                    serviceComponentConfiguration.getSQLSequencesInputStream());
158    
159                            sequencesSQLElement.addCDATA(sequencesSQL);
160    
161                            Element indexesSQLElement = dataElement.addElement("indexes-sql");
162    
163                            String indexesSQL = StringUtil.read(
164                                    serviceComponentConfiguration.getSQLIndexesInputStream());
165    
166                            indexesSQLElement.addCDATA(indexesSQL);
167    
168                            String dataXML = document.formattedString();
169    
170                            serviceComponent.setData(dataXML);
171    
172                            serviceComponentPersistence.update(serviceComponent);
173    
174                            serviceComponentLocalService.upgradeDB(
175                                    classLoader, buildNamespace, buildNumber, buildAutoUpgrade,
176                                    previousServiceComponent, tablesSQL, sequencesSQL, indexesSQL);
177    
178                            removeOldServiceComponents(buildNamespace);
179    
180                            return serviceComponent;
181                    }
182                    catch (Exception e) {
183                            throw new SystemException(e);
184                    }
185            }
186    
187            @Override
188            public void upgradeDB(
189                            final ClassLoader classLoader, final String buildNamespace,
190                            final long buildNumber, final boolean buildAutoUpgrade,
191                            final ServiceComponent previousServiceComponent,
192                            final String tablesSQL, final String sequencesSQL,
193                            final String indexesSQL)
194                    throws Exception {
195    
196                    _pacl.doUpgradeDB(
197                            new DoUpgradeDBPrivilegedExceptionAction(
198                                    classLoader, buildNamespace, buildNumber, buildAutoUpgrade,
199                                    previousServiceComponent, tablesSQL, sequencesSQL, indexesSQL));
200            }
201    
202            @Override
203            public void verifyDB() {
204                    List<ServiceComponent> serviceComponents =
205                            serviceComponentPersistence.findAll();
206    
207                    for (ServiceComponent serviceComponent : serviceComponents) {
208                            String buildNamespace = serviceComponent.getBuildNamespace();
209                            String tablesSQL = serviceComponent.getTablesSQL();
210                            String sequencesSQL = serviceComponent.getSequencesSQL();
211                            String indexesSQL = serviceComponent.getIndexesSQL();
212    
213                            try {
214                                    serviceComponentLocalService.upgradeDB(
215                                            null, buildNamespace, 0, false, null, tablesSQL,
216                                            sequencesSQL, indexesSQL);
217                            }
218                            catch (Exception e) {
219                                    _log.error(e, e);
220                            }
221                    }
222            }
223    
224            public class DoUpgradeDBPrivilegedExceptionAction
225                    implements PrivilegedExceptionAction<Void> {
226    
227                    public DoUpgradeDBPrivilegedExceptionAction(
228                            ClassLoader classLoader, String buildNamespace, long buildNumber,
229                            boolean buildAutoUpgrade, ServiceComponent previousServiceComponent,
230                            String tablesSQL, String sequencesSQL, String indexesSQL) {
231    
232                            _classLoader = classLoader;
233                            _buildNamespace = buildNamespace;
234                            _buildNumber = buildNumber;
235                            _buildAutoUpgrade = buildAutoUpgrade;
236                            _previousServiceComponent = previousServiceComponent;
237                            _tablesSQL = tablesSQL;
238                            _sequencesSQL = sequencesSQL;
239                            _indexesSQL = indexesSQL;
240                    }
241    
242                    public ClassLoader getClassLoader() {
243                            return _classLoader;
244                    }
245    
246                    @Override
247                    public Void run() throws Exception {
248                            doUpgradeDB(
249                                    _classLoader, _buildNamespace, _buildNumber, _buildAutoUpgrade,
250                                    _previousServiceComponent, _tablesSQL, _sequencesSQL,
251                                    _indexesSQL);
252    
253                            return null;
254                    }
255    
256                    private final boolean _buildAutoUpgrade;
257                    private final String _buildNamespace;
258                    private final long _buildNumber;
259                    private final ClassLoader _classLoader;
260                    private final String _indexesSQL;
261                    private final ServiceComponent _previousServiceComponent;
262                    private final String _sequencesSQL;
263                    private final String _tablesSQL;
264    
265            }
266    
267            public interface PACL {
268    
269                    public void doUpgradeDB(
270                                    DoUpgradeDBPrivilegedExceptionAction
271                                            doUpgradeDBPrivilegedExceptionAction)
272                            throws Exception;
273    
274            }
275    
276            protected void doUpgradeDB(
277                            ClassLoader classLoader, String buildNamespace, long buildNumber,
278                            boolean buildAutoUpgrade, ServiceComponent previousServiceComponent,
279                            String tablesSQL, String sequencesSQL, String indexesSQL)
280                    throws Exception {
281    
282                    DB db = DBManagerUtil.getDB();
283    
284                    if (previousServiceComponent == null) {
285                            if (_log.isInfoEnabled()) {
286                                    _log.info("Running " + buildNamespace + " SQL scripts");
287                            }
288    
289                            db.runSQLTemplateString(tablesSQL, true, false);
290                            db.runSQLTemplateString(sequencesSQL, true, false);
291                            db.runSQLTemplateString(indexesSQL, true, false);
292                    }
293                    else if (buildAutoUpgrade) {
294                            if (_log.isInfoEnabled()) {
295                                    _log.info(
296                                            "Upgrading " + buildNamespace +
297                                                    " database to build number " + buildNumber);
298                            }
299    
300                            if (!tablesSQL.equals(previousServiceComponent.getTablesSQL())) {
301                                    if (_log.isInfoEnabled()) {
302                                            _log.info("Upgrading database with tables.sql");
303                                    }
304    
305                                    db.runSQLTemplateString(tablesSQL, true, false);
306    
307                                    upgradeModels(classLoader, previousServiceComponent, tablesSQL);
308                            }
309    
310                            if (!sequencesSQL.equals(
311                                            previousServiceComponent.getSequencesSQL())) {
312    
313                                    if (_log.isInfoEnabled()) {
314                                            _log.info("Upgrading database with sequences.sql");
315                                    }
316    
317                                    db.runSQLTemplateString(sequencesSQL, true, false);
318                            }
319    
320                            if (!indexesSQL.equals(previousServiceComponent.getIndexesSQL()) ||
321                                    !tablesSQL.equals(previousServiceComponent.getTablesSQL())) {
322    
323                                    if (_log.isInfoEnabled()) {
324                                            _log.info("Upgrading database with indexes.sql");
325                                    }
326    
327                                    db.runSQLTemplateString(indexesSQL, true, false);
328                            }
329                    }
330            }
331    
332            protected List<String> getModelNames(ClassLoader classLoader)
333                    throws DocumentException, IOException {
334    
335                    List<String> modelNames = new ArrayList<>();
336    
337                    String xml = StringUtil.read(
338                            classLoader, "META-INF/portlet-model-hints.xml");
339    
340                    modelNames.addAll(getModelNames(xml));
341    
342                    try {
343                            xml = StringUtil.read(
344                                    classLoader, "META-INF/portlet-model-hints-ext.xml");
345    
346                            modelNames.addAll(getModelNames(xml));
347                    }
348                    catch (Exception e) {
349                            if (_log.isInfoEnabled()) {
350                                    _log.info(
351                                            "No optional file META-INF/portlet-model-hints-ext.xml " +
352                                                    "found");
353                            }
354                    }
355    
356                    return modelNames;
357            }
358    
359            protected List<String> getModelNames(String xml) throws DocumentException {
360                    List<String> modelNames = new ArrayList<>();
361    
362                    Document document = UnsecureSAXReaderUtil.read(xml);
363    
364                    Element rootElement = document.getRootElement();
365    
366                    List<Element> modelElements = rootElement.elements("model");
367    
368                    for (Element modelElement : modelElements) {
369                            String name = modelElement.attributeValue("name");
370    
371                            modelNames.add(name);
372                    }
373    
374                    return modelNames;
375            }
376    
377            protected List<String> getModifiedTableNames(
378                    String previousTablesSQL, String tablesSQL) {
379    
380                    List<String> modifiedTableNames = new ArrayList<>();
381    
382                    List<String> previousTablesSQLParts = ListUtil.toList(
383                            StringUtil.split(previousTablesSQL, StringPool.SEMICOLON));
384                    List<String> tablesSQLParts = ListUtil.toList(
385                            StringUtil.split(tablesSQL, StringPool.SEMICOLON));
386    
387                    tablesSQLParts.removeAll(previousTablesSQLParts);
388    
389                    for (String tablesSQLPart : tablesSQLParts) {
390                            int x = tablesSQLPart.indexOf("create table ");
391                            int y = tablesSQLPart.indexOf(" (");
392    
393                            modifiedTableNames.add(tablesSQLPart.substring(x + 13, y));
394                    }
395    
396                    return modifiedTableNames;
397            }
398    
399            protected UpgradeTableListener getUpgradeTableListener(
400                    ClassLoader classLoader, Class<?> modelClass) {
401    
402                    String modelClassName = modelClass.getName();
403    
404                    String upgradeTableListenerClassName = modelClassName;
405    
406                    upgradeTableListenerClassName = StringUtil.replaceLast(
407                            upgradeTableListenerClassName, ".model.impl.", ".model.upgrade.");
408                    upgradeTableListenerClassName = StringUtil.replaceLast(
409                            upgradeTableListenerClassName, "ModelImpl", "UpgradeTableListener");
410    
411                    try {
412                            UpgradeTableListener upgradeTableListener =
413                                    (UpgradeTableListener)InstanceFactory.newInstance(
414                                            classLoader, upgradeTableListenerClassName);
415    
416                            if (_log.isInfoEnabled()) {
417                                    _log.info("Instantiated " + upgradeTableListenerClassName);
418                            }
419    
420                            return upgradeTableListener;
421                    }
422                    catch (Exception e) {
423                            if (_log.isDebugEnabled()) {
424                                    _log.debug(
425                                            "Unable to instantiate " + upgradeTableListenerClassName);
426                            }
427    
428                            return null;
429                    }
430            }
431    
432            protected void removeOldServiceComponents(String buildNamespace) {
433                    int serviceComponentsCount =
434                            serviceComponentPersistence.countByBuildNamespace(buildNamespace);
435    
436                    if (serviceComponentsCount < _SERVICE_COMPONENTS_MAX) {
437                            return;
438                    }
439    
440                    List<ServiceComponent> serviceComponents =
441                            serviceComponentPersistence.findByBuildNamespace(
442                                    buildNamespace, _SERVICE_COMPONENTS_MAX,
443                                    serviceComponentsCount);
444    
445                    for (int i = 0; i < serviceComponents.size(); i++) {
446                            ServiceComponent serviceComponent = serviceComponents.get(i);
447    
448                            serviceComponentPersistence.remove(serviceComponent);
449                    }
450            }
451    
452            protected void upgradeModels(
453                            ClassLoader classLoader, ServiceComponent previousServiceComponent,
454                            String tablesSQL)
455                    throws Exception {
456    
457                    List<String> modifiedTableNames = getModifiedTableNames(
458                            previousServiceComponent.getTablesSQL(), tablesSQL);
459    
460                    List<String> modelNames = getModelNames(classLoader);
461    
462                    for (String modelName : modelNames) {
463                            int pos = modelName.lastIndexOf(".model.");
464    
465                            Class<?> modelClass = Class.forName(
466                                    modelName.substring(0, pos) + ".model.impl." +
467                                            modelName.substring(pos + 7) + "ModelImpl",
468                                    true, classLoader);
469    
470                            Field dataSourceField = modelClass.getField("DATA_SOURCE");
471    
472                            String dataSource = (String)dataSourceField.get(null);
473    
474                            if (!dataSource.equals(_DATA_SOURCE_DEFAULT)) {
475                                    continue;
476                            }
477    
478                            Field tableNameField = modelClass.getField("TABLE_NAME");
479    
480                            String tableName = (String)tableNameField.get(null);
481    
482                            if (!modifiedTableNames.contains(tableName)) {
483                                    continue;
484                            }
485    
486                            Field tableColumnsField = modelClass.getField("TABLE_COLUMNS");
487    
488                            Object[][] tableColumns = (Object[][])tableColumnsField.get(null);
489    
490                            UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(
491                                    tableName, tableColumns);
492    
493                            UpgradeTableListener upgradeTableListener = getUpgradeTableListener(
494                                    classLoader, modelClass);
495    
496                            Field tableSQLCreateField = modelClass.getField("TABLE_SQL_CREATE");
497    
498                            String tableSQLCreate = (String)tableSQLCreateField.get(null);
499    
500                            upgradeTable.setCreateSQL(tableSQLCreate);
501    
502                            if (upgradeTableListener != null) {
503                                    upgradeTableListener.onBeforeUpdateTable(
504                                            previousServiceComponent, upgradeTable);
505                            }
506    
507                            upgradeTable.updateTable();
508    
509                            if (upgradeTableListener != null) {
510                                    upgradeTableListener.onAfterUpdateTable(
511                                            previousServiceComponent, upgradeTable);
512                            }
513                    }
514            }
515    
516            private static final String _DATA_SOURCE_DEFAULT = "liferayDataSource";
517    
518            private static final int _SERVICE_COMPONENTS_MAX = 10;
519    
520            private static final Log _log = LogFactoryUtil.getLog(
521                    ServiceComponentLocalServiceImpl.class);
522    
523            private static final PACL _pacl = new NoPACL();
524    
525            private static class NoPACL implements PACL {
526    
527                    @Override
528                    public void doUpgradeDB(
529                                    DoUpgradeDBPrivilegedExceptionAction
530                                            doUpgradeDBPrivilegedExceptionAction)
531                            throws Exception {
532    
533                            doUpgradeDBPrivilegedExceptionAction.run();
534                    }
535    
536            }
537    
538    }