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