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.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.DBFactoryUtil;
021    import com.liferay.portal.kernel.dao.orm.EntityCacheUtil;
022    import com.liferay.portal.kernel.dao.orm.FinderCacheUtil;
023    import com.liferay.portal.kernel.exception.PortalException;
024    import com.liferay.portal.kernel.exception.SystemException;
025    import com.liferay.portal.kernel.log.Log;
026    import com.liferay.portal.kernel.log.LogFactoryUtil;
027    import com.liferay.portal.kernel.upgrade.util.UpgradeTable;
028    import com.liferay.portal.kernel.upgrade.util.UpgradeTableFactoryUtil;
029    import com.liferay.portal.kernel.upgrade.util.UpgradeTableListener;
030    import com.liferay.portal.kernel.util.InstanceFactory;
031    import com.liferay.portal.kernel.util.StringPool;
032    import com.liferay.portal.kernel.util.StringUtil;
033    import com.liferay.portal.kernel.xml.Document;
034    import com.liferay.portal.kernel.xml.DocumentException;
035    import com.liferay.portal.kernel.xml.Element;
036    import com.liferay.portal.kernel.xml.SAXReaderUtil;
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.tools.servicebuilder.Entity;
042    import com.liferay.portal.util.PropsValues;
043    
044    import java.io.IOException;
045    import java.io.InputStream;
046    
047    import java.lang.reflect.Field;
048    
049    import java.security.PrivilegedExceptionAction;
050    
051    import java.util.ArrayList;
052    import java.util.List;
053    
054    /**
055     * @author Brian Wing Shun Chan
056     */
057    public class ServiceComponentLocalServiceImpl
058            extends ServiceComponentLocalServiceBaseImpl {
059    
060            @Override
061            public void destroyServiceComponent(
062                    ServiceComponentConfiguration serviceComponentConfiguration,
063                    ClassLoader classLoader) {
064    
065                    try {
066                            clearCacheRegistry(serviceComponentConfiguration);
067                    }
068                    catch (Exception e) {
069                            throw new SystemException(e);
070                    }
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 boolean _buildAutoUpgrade;
257                    private String _buildNamespace;
258                    private long _buildNumber;
259                    private ClassLoader _classLoader;
260                    private String _indexesSQL;
261                    private ServiceComponent _previousServiceComponent;
262                    private String _sequencesSQL;
263                    private 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 clearCacheRegistry(
277                            ServiceComponentConfiguration serviceComponentConfiguration)
278                    throws DocumentException {
279    
280                    InputStream inputStream =
281                            serviceComponentConfiguration.getHibernateInputStream();
282    
283                    if (inputStream == null) {
284                            return;
285                    }
286    
287                    Document document = SAXReaderUtil.read(inputStream);
288    
289                    Element rootElement = document.getRootElement();
290    
291                    List<Element> classElements = rootElement.elements("class");
292    
293                    for (Element classElement : classElements) {
294                            String name = classElement.attributeValue("name");
295    
296                            CacheRegistryUtil.unregister(name);
297                    }
298    
299                    CacheRegistryUtil.clear();
300    
301                    if (PropsValues.CACHE_CLEAR_ON_PLUGIN_UNDEPLOY) {
302                            EntityCacheUtil.clearCache();
303                            FinderCacheUtil.clearCache();
304                    }
305            }
306    
307            protected void doUpgradeDB(
308                            ClassLoader classLoader, String buildNamespace, long buildNumber,
309                            boolean buildAutoUpgrade, ServiceComponent previousServiceComponent,
310                            String tablesSQL, String sequencesSQL, String indexesSQL)
311                    throws Exception {
312    
313                    DB db = DBFactoryUtil.getDB();
314    
315                    if (previousServiceComponent == null) {
316                            if (_log.isInfoEnabled()) {
317                                    _log.info("Running " + buildNamespace + " SQL scripts");
318                            }
319    
320                            db.runSQLTemplateString(tablesSQL, true, false);
321                            db.runSQLTemplateString(sequencesSQL, true, false);
322                            db.runSQLTemplateString(indexesSQL, true, false);
323                    }
324                    else if (buildAutoUpgrade) {
325                            if (_log.isInfoEnabled()) {
326                                    _log.info(
327                                            "Upgrading " + buildNamespace +
328                                                    " database to build number " + buildNumber);
329                            }
330    
331                            if (!tablesSQL.equals(previousServiceComponent.getTablesSQL())) {
332                                    if (_log.isInfoEnabled()) {
333                                            _log.info("Upgrading database with tables.sql");
334                                    }
335    
336                                    db.runSQLTemplateString(tablesSQL, true, false);
337    
338                                    upgradeModels(classLoader, previousServiceComponent);
339                            }
340    
341                            if (!sequencesSQL.equals(
342                                            previousServiceComponent.getSequencesSQL())) {
343    
344                                    if (_log.isInfoEnabled()) {
345                                            _log.info("Upgrading database with sequences.sql");
346                                    }
347    
348                                    db.runSQLTemplateString(sequencesSQL, true, false);
349                            }
350    
351                            if (!indexesSQL.equals(previousServiceComponent.getIndexesSQL()) ||
352                                    !tablesSQL.equals(previousServiceComponent.getTablesSQL())) {
353    
354                                    if (_log.isInfoEnabled()) {
355                                            _log.info("Upgrading database with indexes.sql");
356                                    }
357    
358                                    db.runSQLTemplateString(indexesSQL, true, false);
359                            }
360                    }
361            }
362    
363            protected List<String> getModels(ClassLoader classLoader)
364                    throws DocumentException, IOException {
365    
366                    List<String> models = new ArrayList<String>();
367    
368                    String xml = StringUtil.read(
369                            classLoader, "META-INF/portlet-model-hints.xml");
370    
371                    models.addAll(getModels(xml));
372    
373                    try {
374                            xml = StringUtil.read(
375                                    classLoader, "META-INF/portlet-model-hints-ext.xml");
376    
377                            models.addAll(getModels(xml));
378                    }
379                    catch (Exception e) {
380                            if (_log.isInfoEnabled()) {
381                                    _log.info(
382                                            "No optional file META-INF/portlet-model-hints-ext.xml " +
383                                                    "found");
384                            }
385                    }
386    
387                    return models;
388            }
389    
390            protected List<String> getModels(String xml) throws DocumentException {
391                    List<String> models = new ArrayList<String>();
392    
393                    Document document = SAXReaderUtil.read(xml);
394    
395                    Element rootElement = document.getRootElement();
396    
397                    List<Element> modelElements = rootElement.elements("model");
398    
399                    for (Element modelElement : modelElements) {
400                            String name = modelElement.attributeValue("name");
401    
402                            models.add(name);
403                    }
404    
405                    return models;
406            }
407    
408            protected UpgradeTableListener getUpgradeTableListener(
409                    ClassLoader classLoader, Class<?> modelClass) {
410    
411                    String modelClassName = modelClass.getName();
412    
413                    String upgradeTableListenerClassName = modelClassName;
414    
415                    upgradeTableListenerClassName = StringUtil.replaceLast(
416                            upgradeTableListenerClassName, ".model.impl.", ".model.upgrade.");
417                    upgradeTableListenerClassName = StringUtil.replaceLast(
418                            upgradeTableListenerClassName, "ModelImpl", "UpgradeTableListener");
419    
420                    try {
421                            UpgradeTableListener upgradeTableListener =
422                                    (UpgradeTableListener)InstanceFactory.newInstance(
423                                            classLoader, upgradeTableListenerClassName);
424    
425                            if (_log.isInfoEnabled()) {
426                                    _log.info("Instantiated " + upgradeTableListenerClassName);
427                            }
428    
429                            return upgradeTableListener;
430                    }
431                    catch (Exception e) {
432                            if (_log.isDebugEnabled()) {
433                                    _log.debug(
434                                            "Unable to instantiate " + upgradeTableListenerClassName);
435                            }
436    
437                            return null;
438                    }
439            }
440    
441            protected void removeOldServiceComponents(String buildNamespace) {
442                    int serviceComponentsCount =
443                            serviceComponentPersistence.countByBuildNamespace(buildNamespace);
444    
445                    if (serviceComponentsCount < _MAX_SERVICE_COMPONENTS) {
446                            return;
447                    }
448    
449                    List<ServiceComponent> serviceComponents =
450                            serviceComponentPersistence.findByBuildNamespace(
451                                    buildNamespace, _MAX_SERVICE_COMPONENTS,
452                                    serviceComponentsCount);
453    
454                    for (int i = 0; i < serviceComponents.size(); i++) {
455                            ServiceComponent serviceComponent = serviceComponents.get(i);
456    
457                            serviceComponentPersistence.remove(serviceComponent);
458                    }
459            }
460    
461            protected void upgradeModels(
462                            ClassLoader classLoader, ServiceComponent previousServiceComponent)
463                    throws Exception {
464    
465                    List<String> models = getModels(classLoader);
466    
467                    for (String name : models) {
468                            int pos = name.lastIndexOf(".model.");
469    
470                            name =
471                                    name.substring(0, pos) + ".model.impl." +
472                                            name.substring(pos + 7) + "ModelImpl";
473    
474                            Class<?> modelClass = Class.forName(name, true, classLoader);
475    
476                            Field tableNameField = modelClass.getField("TABLE_NAME");
477                            Field tableColumnsField = modelClass.getField("TABLE_COLUMNS");
478                            Field tableSQLCreateField = modelClass.getField("TABLE_SQL_CREATE");
479                            Field dataSourceField = modelClass.getField("DATA_SOURCE");
480    
481                            String tableName = (String)tableNameField.get(null);
482                            Object[][] tableColumns = (Object[][])tableColumnsField.get(null);
483                            String tableSQLCreate = (String)tableSQLCreateField.get(null);
484                            String dataSource = (String)dataSourceField.get(null);
485    
486                            if (!dataSource.equals(Entity.DEFAULT_DATA_SOURCE)) {
487                                    continue;
488                            }
489    
490                            UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(
491                                    tableName, tableColumns);
492    
493                            UpgradeTableListener upgradeTableListener = getUpgradeTableListener(
494                                    classLoader, modelClass);
495    
496                            upgradeTable.setCreateSQL(tableSQLCreate);
497    
498                            if (upgradeTableListener != null) {
499                                    upgradeTableListener.onBeforeUpdateTable(
500                                            previousServiceComponent, upgradeTable);
501                            }
502    
503                            upgradeTable.updateTable();
504    
505                            if (upgradeTableListener != null) {
506                                    upgradeTableListener.onAfterUpdateTable(
507                                            previousServiceComponent, upgradeTable);
508                            }
509                    }
510            }
511    
512            private static final int _MAX_SERVICE_COMPONENTS = 10;
513    
514            private static Log _log = LogFactoryUtil.getLog(
515                    ServiceComponentLocalServiceImpl.class);
516    
517            private static PACL _pacl = new NoPACL();
518    
519            private static class NoPACL implements PACL {
520    
521                    @Override
522                    public void doUpgradeDB(
523                                    DoUpgradeDBPrivilegedExceptionAction
524                                            doUpgradeDBPrivilegedExceptionAction)
525                            throws Exception {
526    
527                            doUpgradeDBPrivilegedExceptionAction.run();
528                    }
529    
530            }
531    
532    }