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            public void destroyServiceComponent(
065                            ServletContext servletContext, ClassLoader classLoader)
066                    throws SystemException {
067    
068                    try {
069                            clearCacheRegistry(servletContext);
070                    }
071                    catch (Exception e) {
072                            throw new SystemException(e);
073                    }
074            }
075    
076            public ServiceComponent initServiceComponent(
077                            ServletContext servletContext, ClassLoader classLoader,
078                            String buildNamespace, long buildNumber, long buildDate,
079                            boolean buildAutoUpgrade)
080                    throws PortalException, SystemException {
081    
082                    try {
083                            ModelHintsUtil.read(
084                                    classLoader, "META-INF/portlet-model-hints.xml");
085                    }
086                    catch (Exception e) {
087                            throw new SystemException(e);
088                    }
089    
090                    try {
091                            ModelHintsUtil.read(
092                                    classLoader, "META-INF/portlet-model-hints-ext.xml");
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 = HttpUtil.URLtoString(
149                                    servletContext.getResource("/WEB-INF/sql/tables.sql"));
150    
151                            tablesSQLElement.addCDATA(tablesSQL);
152    
153                            Element sequencesSQLElement = dataElement.addElement(
154                                    "sequences-sql");
155    
156                            String sequencesSQL = HttpUtil.URLtoString(
157                                    servletContext.getResource("/WEB-INF/sql/sequences.sql"));
158    
159                            sequencesSQLElement.addCDATA(sequencesSQL);
160    
161                            Element indexesSQLElement = dataElement.addElement("indexes-sql");
162    
163                            String indexesSQL = HttpUtil.URLtoString(
164                                    servletContext.getResource("/WEB-INF/sql/indexes.sql"));
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            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                    ProtectionDomain protectionDomain = new ProtectionDomain(
196                            null, null, classLoader, null);
197    
198                    AccessControlContext accessControlContext = new AccessControlContext(
199                            new ProtectionDomain[] {protectionDomain});
200    
201                    AccessController.doPrivileged(
202                            new PrivilegedExceptionAction<Void>() {
203    
204                                    public Void run() throws Exception {
205                                            doUpgradeDB(
206                                                    classLoader, buildNamespace, buildNumber,
207                                                    buildAutoUpgrade, previousServiceComponent, tablesSQL,
208                                                    sequencesSQL, indexesSQL);
209    
210                                            return null;
211                                    }
212    
213                            },
214                            accessControlContext
215                    );
216            }
217    
218            public void verifyDB() throws SystemException {
219                    List<ServiceComponent> serviceComponents =
220                            serviceComponentPersistence.findAll();
221    
222                    for (ServiceComponent serviceComponent : serviceComponents) {
223                            String buildNamespace = serviceComponent.getBuildNamespace();
224                            String tablesSQL = serviceComponent.getTablesSQL();
225                            String sequencesSQL = serviceComponent.getSequencesSQL();
226                            String indexesSQL = serviceComponent.getIndexesSQL();
227    
228                            try {
229                                    serviceComponentLocalService.upgradeDB(
230                                            null, buildNamespace, 0, false, null, tablesSQL,
231                                            sequencesSQL, indexesSQL);
232                            }
233                            catch (Exception e) {
234                                    _log.error(e, e);
235                            }
236                    }
237            }
238    
239            protected void clearCacheRegistry(ServletContext servletContext)
240                    throws DocumentException {
241    
242                    InputStream inputStream = servletContext.getResourceAsStream(
243                            "/WEB-INF/classes/META-INF/portlet-hbm.xml");
244    
245                    if (inputStream == null) {
246                            return;
247                    }
248    
249                    Document document = SAXReaderUtil.read(inputStream);
250    
251                    Element rootElement = document.getRootElement();
252    
253                    List<Element> classElements = rootElement.elements("class");
254    
255                    for (Element classElement : classElements) {
256                            String name = classElement.attributeValue("name");
257    
258                            CacheRegistryUtil.unregister(name);
259                    }
260    
261                    CacheRegistryUtil.clear();
262    
263                    EntityCacheUtil.clearCache();
264                    FinderCacheUtil.clearCache();
265            }
266    
267            protected void doUpgradeDB(
268                            ClassLoader classLoader, String buildNamespace, long buildNumber,
269                            boolean buildAutoUpgrade, ServiceComponent previousServiceComponent,
270                            String tablesSQL, String sequencesSQL, String indexesSQL)
271                    throws Exception {
272    
273                    DB db = DBFactoryUtil.getDB();
274    
275                    if (previousServiceComponent == null) {
276                            if (_log.isInfoEnabled()) {
277                                    _log.info("Running " + buildNamespace + " SQL scripts");
278                            }
279    
280                            db.runSQLTemplateString(tablesSQL, true, false);
281                            db.runSQLTemplateString(sequencesSQL, true, false);
282                            db.runSQLTemplateString(indexesSQL, true, false);
283                    }
284                    else if (buildAutoUpgrade) {
285                            if (_log.isInfoEnabled()) {
286                                    _log.info(
287                                            "Upgrading " + buildNamespace +
288                                                    " database to build number " + buildNumber);
289                            }
290    
291                            if (!tablesSQL.equals(previousServiceComponent.getTablesSQL())) {
292                                    if (_log.isInfoEnabled()) {
293                                            _log.info("Upgrading database with tables.sql");
294                                    }
295    
296                                    db.runSQLTemplateString(tablesSQL, true, false);
297    
298                                    upgradeModels(classLoader, previousServiceComponent);
299                            }
300    
301                            if (!sequencesSQL.equals(
302                                            previousServiceComponent.getSequencesSQL())) {
303    
304                                    if (_log.isInfoEnabled()) {
305                                            _log.info("Upgrading database with sequences.sql");
306                                    }
307    
308                                    db.runSQLTemplateString(sequencesSQL, true, false);
309                            }
310    
311                            if (!indexesSQL.equals(previousServiceComponent.getIndexesSQL())) {
312                                    if (_log.isInfoEnabled()) {
313                                            _log.info("Upgrading database with indexes.sql");
314                                    }
315    
316                                    db.runSQLTemplateString(indexesSQL, true, false);
317                            }
318                    }
319            }
320    
321            protected List<String> getModels(ClassLoader classLoader)
322                    throws DocumentException, IOException {
323    
324                    List<String> models = new ArrayList<String>();
325    
326                    String xml = StringUtil.read(
327                            classLoader, "META-INF/portlet-model-hints.xml");
328    
329                    models.addAll(getModels(xml));
330    
331                    try {
332                            xml = StringUtil.read(
333                                    classLoader, "META-INF/portlet-model-hints-ext.xml");
334    
335                            models.addAll(getModels(xml));
336                    }
337                    catch (Exception e) {
338                            if (_log.isInfoEnabled()) {
339                                    _log.info(
340                                            "No optional file META-INF/portlet-model-hints-ext.xml " +
341                                                    "found");
342                            }
343                    }
344    
345                    return models;
346            }
347    
348            protected List<String> getModels(String xml) throws DocumentException {
349                    List<String> models = new ArrayList<String>();
350    
351                    Document document = SAXReaderUtil.read(xml);
352    
353                    Element rootElement = document.getRootElement();
354    
355                    List<Element> modelElements = rootElement.elements("model");
356    
357                    for (Element modelElement : modelElements) {
358                            String name = modelElement.attributeValue("name");
359    
360                            models.add(name);
361                    }
362    
363                    return models;
364            }
365    
366            protected UpgradeTableListener getUpgradeTableListener(
367                    ClassLoader classLoader, Class<?> modelClass) {
368    
369                    String modelClassName = modelClass.getName();
370    
371                    String upgradeTableListenerClassName = modelClassName;
372    
373                    upgradeTableListenerClassName = StringUtil.replaceLast(
374                            upgradeTableListenerClassName, ".model.impl.", ".model.upgrade.");
375                    upgradeTableListenerClassName = StringUtil.replaceLast(
376                            upgradeTableListenerClassName, "ModelImpl", "UpgradeTableListener");
377    
378                    try {
379                            UpgradeTableListener upgradeTableListener =
380                                    (UpgradeTableListener)InstanceFactory.newInstance(
381                                            classLoader, upgradeTableListenerClassName);
382    
383                            if (_log.isInfoEnabled()) {
384                                    _log.info("Instantiated " + upgradeTableListenerClassName);
385                            }
386    
387                            return upgradeTableListener;
388                    }
389                    catch (Exception e) {
390                            if (_log.isDebugEnabled()) {
391                                    _log.debug(
392                                            "Unable to instantiate " + upgradeTableListenerClassName);
393                            }
394    
395                            return null;
396                    }
397            }
398    
399            protected void removeOldServiceComponents(String buildNamespace)
400                    throws SystemException {
401    
402                    int serviceComponentsCount =
403                            serviceComponentPersistence.countByBuildNamespace(buildNamespace);
404    
405                    if (serviceComponentsCount < _MAX_SERVICE_COMPONENTS) {
406                            return;
407                    }
408    
409                    List<ServiceComponent> serviceComponents =
410                            serviceComponentPersistence.findByBuildNamespace(
411                                    buildNamespace, _MAX_SERVICE_COMPONENTS,
412                                    serviceComponentsCount);
413    
414                    for (int i = 0; i < serviceComponents.size(); i++) {
415                            ServiceComponent serviceComponent = serviceComponents.get(i);
416    
417                            serviceComponentPersistence.remove(serviceComponent);
418                    }
419            }
420    
421            protected void upgradeModels(
422                            ClassLoader classLoader, ServiceComponent previousServiceComponent)
423                    throws Exception {
424    
425                    List<String> models = getModels(classLoader);
426    
427                    for (String name : models) {
428                            int pos = name.lastIndexOf(".model.");
429    
430                            name =
431                                    name.substring(0, pos) + ".model.impl." +
432                                            name.substring(pos + 7) + "ModelImpl";
433    
434                            Class<?> modelClass = Class.forName(name, true, classLoader);
435    
436                            Field tableNameField = modelClass.getField("TABLE_NAME");
437                            Field tableColumnsField = modelClass.getField("TABLE_COLUMNS");
438                            Field tableSQLCreateField = modelClass.getField("TABLE_SQL_CREATE");
439                            Field dataSourceField = modelClass.getField("DATA_SOURCE");
440    
441                            String tableName = (String)tableNameField.get(null);
442                            Object[][] tableColumns = (Object[][])tableColumnsField.get(null);
443                            String tableSQLCreate = (String)tableSQLCreateField.get(null);
444                            String dataSource = (String)dataSourceField.get(null);
445    
446                            if (!dataSource.equals(Entity.DEFAULT_DATA_SOURCE)) {
447                                    continue;
448                            }
449    
450                            UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(
451                                    tableName, tableColumns);
452    
453                            UpgradeTableListener upgradeTableListener = getUpgradeTableListener(
454                                    classLoader, modelClass);
455    
456                            upgradeTable.setCreateSQL(tableSQLCreate);
457    
458                            if (upgradeTableListener != null) {
459                                    upgradeTableListener.onBeforeUpdateTable(
460                                            previousServiceComponent, upgradeTable);
461                            }
462    
463                            upgradeTable.updateTable();
464    
465                            if (upgradeTableListener != null) {
466                                    upgradeTableListener.onAfterUpdateTable(
467                                            previousServiceComponent, upgradeTable);
468                            }
469                    }
470            }
471    
472            private static final int _MAX_SERVICE_COMPONENTS = 10;
473    
474            private static Log _log = LogFactoryUtil.getLog(
475                    ServiceComponentLocalServiceImpl.class);
476    
477    }