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.util.HttpUtil;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.xml.Document;
033    import com.liferay.portal.kernel.xml.DocumentException;
034    import com.liferay.portal.kernel.xml.Element;
035    import com.liferay.portal.kernel.xml.SAXReaderUtil;
036    import com.liferay.portal.model.ModelHintsUtil;
037    import com.liferay.portal.model.ServiceComponent;
038    import com.liferay.portal.service.base.ServiceComponentLocalServiceBaseImpl;
039    import com.liferay.portal.tools.servicebuilder.Entity;
040    
041    import java.io.IOException;
042    import java.io.InputStream;
043    
044    import java.lang.reflect.Field;
045    
046    import java.security.AccessControlContext;
047    import java.security.AccessController;
048    import java.security.PrivilegedExceptionAction;
049    import java.security.ProtectionDomain;
050    
051    import java.util.ArrayList;
052    import java.util.List;
053    
054    import javax.servlet.ServletContext;
055    
056    /**
057     * @author Brian Wing Shun Chan
058     */
059    public class ServiceComponentLocalServiceImpl
060            extends ServiceComponentLocalServiceBaseImpl {
061    
062            @Override
063            public void destroyServiceComponent(
064                            ServletContext servletContext, ClassLoader classLoader)
065                    throws SystemException {
066    
067                    try {
068                            clearCacheRegistry(servletContext);
069                    }
070                    catch (Exception e) {
071                            throw new SystemException(e);
072                    }
073            }
074    
075            @Override
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, false);
173    
174                            serviceComponentLocalService.upgradeDB(
175                                    classLoader, buildNamespace, buildNumber, buildAutoUpgrade,
176                                    previousServiceComponent, tablesSQL, sequencesSQL, indexesSQL);
177    
178                            removeOldServiceComponents(buildNamespace);
179    
180                            return serviceComponent;
181                    }
182                    catch (Exception e) {
183                            throw new SystemException(e);
184                    }
185            }
186    
187            @Override
188            public void upgradeDB(
189                            final ClassLoader classLoader, final String buildNamespace,
190                            final long buildNumber, final boolean buildAutoUpgrade,
191                            final ServiceComponent previousServiceComponent,
192                            final String tablesSQL, final String sequencesSQL,
193                            final String indexesSQL)
194                    throws Exception {
195    
196                    ProtectionDomain protectionDomain = new ProtectionDomain(
197                            null, null, classLoader, null);
198    
199                    AccessControlContext accessControlContext = new AccessControlContext(
200                            new ProtectionDomain[] {protectionDomain});
201    
202                    AccessController.doPrivileged(
203                            new PrivilegedExceptionAction<Void>() {
204    
205                                    @Override
206                                    public Void run() throws Exception {
207                                            doUpgradeDB(
208                                                    classLoader, buildNamespace, buildNumber,
209                                                    buildAutoUpgrade, previousServiceComponent, tablesSQL,
210                                                    sequencesSQL, indexesSQL);
211    
212                                            return null;
213                                    }
214    
215                            },
216                            accessControlContext
217                    );
218            }
219    
220            @Override
221            public void verifyDB() throws SystemException {
222                    List<ServiceComponent> serviceComponents =
223                            serviceComponentPersistence.findAll();
224    
225                    for (ServiceComponent serviceComponent : serviceComponents) {
226                            String buildNamespace = serviceComponent.getBuildNamespace();
227                            String tablesSQL = serviceComponent.getTablesSQL();
228                            String sequencesSQL = serviceComponent.getSequencesSQL();
229                            String indexesSQL = serviceComponent.getIndexesSQL();
230    
231                            try {
232                                    serviceComponentLocalService.upgradeDB(
233                                            null, buildNamespace, 0, false, null, tablesSQL,
234                                            sequencesSQL, indexesSQL);
235                            }
236                            catch (Exception e) {
237                                    _log.error(e, e);
238                            }
239                    }
240            }
241    
242            protected void clearCacheRegistry(ServletContext servletContext)
243                    throws DocumentException {
244    
245                    InputStream inputStream = servletContext.getResourceAsStream(
246                            "/WEB-INF/classes/META-INF/portlet-hbm.xml");
247    
248                    if (inputStream == null) {
249                            return;
250                    }
251    
252                    Document document = SAXReaderUtil.read(inputStream);
253    
254                    Element rootElement = document.getRootElement();
255    
256                    List<Element> classElements = rootElement.elements("class");
257    
258                    for (Element classElement : classElements) {
259                            String name = classElement.attributeValue("name");
260    
261                            CacheRegistryUtil.unregister(name);
262                    }
263    
264                    CacheRegistryUtil.clear();
265    
266                    EntityCacheUtil.clearCache();
267                    FinderCacheUtil.clearCache();
268            }
269    
270            protected void doUpgradeDB(
271                            ClassLoader classLoader, String buildNamespace, long buildNumber,
272                            boolean buildAutoUpgrade, ServiceComponent previousServiceComponent,
273                            String tablesSQL, String sequencesSQL, String indexesSQL)
274                    throws Exception {
275    
276                    DB db = DBFactoryUtil.getDB();
277    
278                    if (previousServiceComponent == null) {
279                            if (_log.isInfoEnabled()) {
280                                    _log.info("Running " + buildNamespace + " SQL scripts");
281                            }
282    
283                            db.runSQLTemplateString(tablesSQL, true, false);
284                            db.runSQLTemplateString(sequencesSQL, true, false);
285                            db.runSQLTemplateString(indexesSQL, true, false);
286                    }
287                    else if (buildAutoUpgrade) {
288                            if (_log.isInfoEnabled()) {
289                                    _log.info(
290                                            "Upgrading " + buildNamespace +
291                                                    " database to build number " + buildNumber);
292                            }
293    
294                            if (!tablesSQL.equals(previousServiceComponent.getTablesSQL())) {
295                                    if (_log.isInfoEnabled()) {
296                                            _log.info("Upgrading database with tables.sql");
297                                    }
298    
299                                    db.runSQLTemplateString(tablesSQL, true, false);
300    
301                                    upgradeModels(classLoader);
302                            }
303    
304                            if (!sequencesSQL.equals(
305                                            previousServiceComponent.getSequencesSQL())) {
306    
307                                    if (_log.isInfoEnabled()) {
308                                            _log.info("Upgrading database with sequences.sql");
309                                    }
310    
311                                    db.runSQLTemplateString(sequencesSQL, true, false);
312                            }
313    
314                            if (!indexesSQL.equals(previousServiceComponent.getIndexesSQL())) {
315                                    if (_log.isInfoEnabled()) {
316                                            _log.info("Upgrading database with indexes.sql");
317                                    }
318    
319                                    db.runSQLTemplateString(indexesSQL, true, false);
320                            }
321                    }
322            }
323    
324            protected List<String> getModels(ClassLoader classLoader)
325                    throws DocumentException, IOException {
326    
327                    List<String> models = new ArrayList<String>();
328    
329                    String xml = StringUtil.read(
330                            classLoader, "META-INF/portlet-model-hints.xml");
331    
332                    models.addAll(getModels(xml));
333    
334                    try {
335                            xml = StringUtil.read(
336                                    classLoader, "META-INF/portlet-model-hints-ext.xml");
337    
338                            models.addAll(getModels(xml));
339                    }
340                    catch (Exception e) {
341                            if (_log.isInfoEnabled()) {
342                                    _log.info(
343                                            "No optional file META-INF/portlet-model-hints-ext.xml " +
344                                                    "found");
345                            }
346                    }
347    
348                    return models;
349            }
350    
351            protected List<String> getModels(String xml) throws DocumentException {
352                    List<String> models = new ArrayList<String>();
353    
354                    Document document = SAXReaderUtil.read(xml);
355    
356                    Element rootElement = document.getRootElement();
357    
358                    List<Element> modelElements = rootElement.elements("model");
359    
360                    for (Element modelElement : modelElements) {
361                            String name = modelElement.attributeValue("name");
362    
363                            models.add(name);
364                    }
365    
366                    return models;
367            }
368    
369            protected void removeOldServiceComponents(String buildNamespace)
370                    throws SystemException {
371    
372                    int serviceComponentsCount =
373                            serviceComponentPersistence.countByBuildNamespace(buildNamespace);
374    
375                    if (serviceComponentsCount < _MAX_SERVICE_COMPONENTS) {
376                            return;
377                    }
378    
379                    List<ServiceComponent> serviceComponents =
380                            serviceComponentPersistence.findByBuildNamespace(
381                                    buildNamespace, _MAX_SERVICE_COMPONENTS,
382                                    serviceComponentsCount);
383    
384                    for (int i = 0; i < serviceComponents.size(); i++) {
385                            ServiceComponent serviceComponent = serviceComponents.get(i);
386    
387                            serviceComponentPersistence.remove(serviceComponent);
388                    }
389            }
390    
391            protected void upgradeModels(ClassLoader classLoader) throws Exception {
392                    List<String> models = getModels(classLoader);
393    
394                    for (String name : models) {
395                            int pos = name.lastIndexOf(".model.");
396    
397                            name =
398                                    name.substring(0, pos) + ".model.impl." +
399                                            name.substring(pos + 7) + "ModelImpl";
400    
401                            Class<?> modelClass = Class.forName(name, true, classLoader);
402    
403                            Field tableNameField = modelClass.getField("TABLE_NAME");
404                            Field tableColumnsField = modelClass.getField("TABLE_COLUMNS");
405                            Field tableSQLCreateField = modelClass.getField("TABLE_SQL_CREATE");
406                            Field dataSourceField = modelClass.getField("DATA_SOURCE");
407    
408                            String tableName = (String)tableNameField.get(null);
409                            Object[][] tableColumns = (Object[][])tableColumnsField.get(null);
410                            String tableSQLCreate = (String)tableSQLCreateField.get(null);
411                            String dataSource = (String)dataSourceField.get(null);
412    
413                            if (!dataSource.equals(Entity.DEFAULT_DATA_SOURCE)) {
414                                    continue;
415                            }
416    
417                            UpgradeTable upgradeTable = UpgradeTableFactoryUtil.getUpgradeTable(
418                                    tableName, tableColumns);
419    
420                            upgradeTable.setCreateSQL(tableSQLCreate);
421    
422                            upgradeTable.updateTable();
423                    }
424            }
425    
426            private static final int _MAX_SERVICE_COMPONENTS = 10;
427    
428            private static Log _log = LogFactoryUtil.getLog(
429                    ServiceComponentLocalServiceImpl.class);
430    
431    }