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