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                                    if (_log.isInfoEnabled()) {
318                                            _log.info("Upgrading database with indexes.sql");
319                                    }
320    
321                                    db.runSQLTemplateString(indexesSQL, true, false);
322                            }
323                    }
324            }
325    
326            protected List<String> getModels(ClassLoader classLoader)
327                    throws DocumentException, IOException {
328    
329                    List<String> models = new ArrayList<String>();
330    
331                    String xml = StringUtil.read(
332                            classLoader, "META-INF/portlet-model-hints.xml");
333    
334                    models.addAll(getModels(xml));
335    
336                    try {
337                            xml = StringUtil.read(
338                                    classLoader, "META-INF/portlet-model-hints-ext.xml");
339    
340                            models.addAll(getModels(xml));
341                    }
342                    catch (Exception e) {
343                            if (_log.isInfoEnabled()) {
344                                    _log.info(
345                                            "No optional file META-INF/portlet-model-hints-ext.xml " +
346                                                    "found");
347                            }
348                    }
349    
350                    return models;
351            }
352    
353            protected List<String> getModels(String xml) throws DocumentException {
354                    List<String> models = new ArrayList<String>();
355    
356                    Document document = SAXReaderUtil.read(xml);
357    
358                    Element rootElement = document.getRootElement();
359    
360                    List<Element> modelElements = rootElement.elements("model");
361    
362                    for (Element modelElement : modelElements) {
363                            String name = modelElement.attributeValue("name");
364    
365                            models.add(name);
366                    }
367    
368                    return models;
369            }
370    
371            protected UpgradeTableListener getUpgradeTableListener(
372                    ClassLoader classLoader, Class<?> modelClass) {
373    
374                    String modelClassName = modelClass.getName();
375    
376                    String upgradeTableListenerClassName = modelClassName;
377    
378                    upgradeTableListenerClassName = StringUtil.replaceLast(
379                            upgradeTableListenerClassName, ".model.impl.", ".model.upgrade.");
380                    upgradeTableListenerClassName = StringUtil.replaceLast(
381                            upgradeTableListenerClassName, "ModelImpl", "UpgradeTableListener");
382    
383                    try {
384                            UpgradeTableListener upgradeTableListener =
385                                    (UpgradeTableListener)InstanceFactory.newInstance(
386                                            classLoader, upgradeTableListenerClassName);
387    
388                            if (_log.isInfoEnabled()) {
389                                    _log.info("Instantiated " + upgradeTableListenerClassName);
390                            }
391    
392                            return upgradeTableListener;
393                    }
394                    catch (Exception e) {
395                            if (_log.isDebugEnabled()) {
396                                    _log.debug(
397                                            "Unable to instantiate " + upgradeTableListenerClassName);
398                            }
399    
400                            return null;
401                    }
402            }
403    
404            protected void removeOldServiceComponents(String buildNamespace)
405                    throws SystemException {
406    
407                    int serviceComponentsCount =
408                            serviceComponentPersistence.countByBuildNamespace(buildNamespace);
409    
410                    if (serviceComponentsCount < _MAX_SERVICE_COMPONENTS) {
411                            return;
412                    }
413    
414                    List<ServiceComponent> serviceComponents =
415                            serviceComponentPersistence.findByBuildNamespace(
416                                    buildNamespace, _MAX_SERVICE_COMPONENTS,
417                                    serviceComponentsCount);
418    
419                    for (int i = 0; i < serviceComponents.size(); i++) {
420                            ServiceComponent serviceComponent = serviceComponents.get(i);
421    
422                            serviceComponentPersistence.remove(serviceComponent);
423                    }
424            }
425    
426            protected void upgradeModels(
427                            ClassLoader classLoader, ServiceComponent previousServiceComponent)
428                    throws Exception {
429    
430                    List<String> models = getModels(classLoader);
431    
432                    for (String name : models) {
433                            int pos = name.lastIndexOf(".model.");
434    
435                            name =
436                                    name.substring(0, pos) + ".model.impl." +
437                                            name.substring(pos + 7) + "ModelImpl";
438    
439                            Class<?> modelClass = Class.forName(name, true, classLoader);
440    
441                            Field tableNameField = modelClass.getField("TABLE_NAME");
442                            Field tableColumnsField = modelClass.getField("TABLE_COLUMNS");
443                            Field tableSQLCreateField = modelClass.getField("TABLE_SQL_CREATE");
444                            Field dataSourceField = modelClass.getField("DATA_SOURCE");
445    
446                            String tableName = (String)tableNameField.get(null);
447                            Object[][] tableColumns = (Object[][])tableColumnsField.get(null);
448                            String tableSQLCreate = (String)tableSQLCreateField.get(null);
449                            String dataSource = (String)dataSourceField.get(null);
450    
451                            if (!dataSource.equals(Entity.DEFAULT_DATA_SOURCE)) {
452                                    continue;
453                            }
454    
455                            UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(
456                                    tableName, tableColumns);
457    
458                            UpgradeTableListener upgradeTableListener = getUpgradeTableListener(
459                                    classLoader, modelClass);
460    
461                            upgradeTable.setCreateSQL(tableSQLCreate);
462    
463                            if (upgradeTableListener != null) {
464                                    upgradeTableListener.onBeforeUpdateTable(
465                                            previousServiceComponent, upgradeTable);
466                            }
467    
468                            upgradeTable.updateTable();
469    
470                            if (upgradeTableListener != null) {
471                                    upgradeTableListener.onAfterUpdateTable(
472                                            previousServiceComponent, upgradeTable);
473                            }
474                    }
475            }
476    
477            private static final int _MAX_SERVICE_COMPONENTS = 10;
478    
479            private static Log _log = LogFactoryUtil.getLog(
480                    ServiceComponentLocalServiceImpl.class);
481    
482    }