1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.service.impl;
24  
25  import com.liferay.portal.OldServiceComponentException;
26  import com.liferay.portal.PortalException;
27  import com.liferay.portal.SystemException;
28  import com.liferay.portal.kernel.cache.CacheRegistry;
29  import com.liferay.portal.kernel.log.Log;
30  import com.liferay.portal.kernel.log.LogFactoryUtil;
31  import com.liferay.portal.kernel.util.HttpUtil;
32  import com.liferay.portal.kernel.util.StringPool;
33  import com.liferay.portal.kernel.util.StringUtil;
34  import com.liferay.portal.kernel.xml.Document;
35  import com.liferay.portal.kernel.xml.DocumentException;
36  import com.liferay.portal.kernel.xml.Element;
37  import com.liferay.portal.kernel.xml.SAXReaderUtil;
38  import com.liferay.portal.model.ModelHintsUtil;
39  import com.liferay.portal.model.ServiceComponent;
40  import com.liferay.portal.service.base.ServiceComponentLocalServiceBaseImpl;
41  import com.liferay.portal.tools.servicebuilder.Entity;
42  import com.liferay.portal.tools.sql.DBUtil;
43  import com.liferay.portal.upgrade.util.DefaultUpgradeTableImpl;
44  import com.liferay.portal.upgrade.util.UpgradeTable;
45  
46  import java.io.IOException;
47  
48  import java.lang.reflect.Field;
49  
50  import java.util.ArrayList;
51  import java.util.Iterator;
52  import java.util.List;
53  
54  import javax.servlet.ServletContext;
55  
56  /**
57   * <a href="ServiceComponentLocalServiceImpl.java.html"><b><i>View Source</i>
58   * </b></a>
59   *
60   * @author Brian Wing Shun Chan
61   *
62   */
63  public class ServiceComponentLocalServiceImpl
64      extends ServiceComponentLocalServiceBaseImpl {
65  
66      public void destroyServiceComponent(
67              ServletContext servletContext, ClassLoader classLoader)
68          throws SystemException {
69  
70          try {
71              clearCacheRegistry(servletContext);
72          }
73          catch (Exception e) {
74              throw new SystemException(e);
75          }
76      }
77  
78      public ServiceComponent initServiceComponent(
79              ServletContext servletContext, ClassLoader classLoader,
80              String buildNamespace, long buildNumber, long buildDate,
81              boolean buildAutoUpgrade)
82          throws PortalException, SystemException {
83  
84          try {
85              ModelHintsUtil.read(
86                  classLoader, "META-INF/portlet-model-hints.xml");
87          }
88          catch (Exception e) {
89              throw new SystemException(e);
90          }
91  
92          try {
93              ModelHintsUtil.read(
94                  classLoader, "META-INF/portlet-model-hints-ext.xml");
95          }
96          catch (Exception e) {
97              throw new SystemException(e);
98          }
99  
100         ServiceComponent serviceComponent = null;
101         ServiceComponent previousServiceComponent = null;
102 
103         List<ServiceComponent> serviceComponents =
104             serviceComponentPersistence.findByBuildNamespace(
105                 buildNamespace, 0, 1);
106 
107         if (serviceComponents.size() == 0) {
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 doc = SAXReaderUtil.createDocument(StringPool.UTF8);
145 
146             Element data = doc.addElement("data");
147 
148             String tablesSQL = HttpUtil.URLtoString(servletContext.getResource(
149                 "/WEB-INF/sql/tables.sql"));
150 
151             data.addElement("tables-sql").addCDATA(tablesSQL);
152 
153             String sequencesSQL = HttpUtil.URLtoString(
154                 servletContext.getResource("/WEB-INF/sql/sequences.sql"));
155 
156             data.addElement("sequences-sql").addCDATA(sequencesSQL);
157 
158             String indexesSQL = HttpUtil.URLtoString(servletContext.getResource(
159                 "/WEB-INF/sql/indexes.sql"));
160 
161             data.addElement("indexes-sql").addCDATA(indexesSQL);
162 
163             String dataXML = doc.formattedString();
164 
165             serviceComponent.setData(dataXML);
166 
167             serviceComponentPersistence.update(serviceComponent, false);
168 
169             upgradeDB(
170                 classLoader, buildNamespace, buildNumber, buildAutoUpgrade,
171                 previousServiceComponent, tablesSQL, sequencesSQL, indexesSQL);
172 
173             removeOldServiceComponents(buildNamespace);
174 
175             return serviceComponent;
176         }
177         catch (Exception e) {
178             throw new SystemException(e);
179         }
180     }
181 
182     protected void clearCacheRegistry(ServletContext servletContext)
183         throws DocumentException, IOException {
184 
185         String xml = HttpUtil.URLtoString(
186             servletContext.getResource(
187                 "/WEB-INF/classes/META-INF/portlet-hbm.xml"));
188 
189         if (xml == null) {
190             return;
191         }
192 
193         Document doc = SAXReaderUtil.read(xml);
194 
195         Element root = doc.getRootElement();
196 
197         List<Element> classEls = root.elements("class");
198 
199         for (Element classEl : classEls) {
200             String name = classEl.attributeValue("name");
201 
202             CacheRegistry.unregister(name);
203         }
204 
205         CacheRegistry.clear();
206     }
207 
208     protected List<String> getModels(ClassLoader classLoader)
209         throws DocumentException, IOException {
210 
211         List<String> models = new ArrayList<String>();
212 
213         String xml = StringUtil.read(
214             classLoader, "META-INF/portlet-model-hints.xml");
215 
216         models.addAll(getModels(xml));
217 
218         try {
219             xml = StringUtil.read(
220                 classLoader, "META-INF/portlet-model-hints-ext.xml");
221 
222             models.addAll(getModels(xml));
223         }
224         catch (Exception e) {
225             if (_log.isInfoEnabled()) {
226                 _log.info(
227                     "No optional file META-INF/portlet-model-hints-ext.xml " +
228                         "found");
229             }
230         }
231 
232         return models;
233     }
234 
235     protected List<String> getModels(String xml) throws DocumentException {
236         List<String> models = new ArrayList<String>();
237 
238         Document doc = SAXReaderUtil.read(xml);
239 
240         Element root = doc.getRootElement();
241 
242         Iterator<Element> itr = root.elements("model").iterator();
243 
244         while (itr.hasNext()) {
245             Element modelEl = itr.next();
246 
247             String name = modelEl.attributeValue("name");
248 
249             models.add(name);
250         }
251 
252         return models;
253     }
254 
255     protected void upgradeDB(
256             ClassLoader classLoader, String buildNamespace, long buildNumber,
257             boolean buildAutoUpgrade, ServiceComponent previousServiceComponent,
258             String tablesSQL, String sequencesSQL, String indexesSQL)
259         throws Exception {
260 
261         DBUtil dbUtil = DBUtil.getInstance();
262 
263         if (previousServiceComponent == null) {
264             if (_log.isInfoEnabled()) {
265                 _log.info(
266                     "Running " + buildNamespace +
267                         " SQL scripts for the first time");
268             }
269 
270             dbUtil.runSQLTemplateString(tablesSQL, true, false);
271             dbUtil.runSQLTemplateString(sequencesSQL, true, false);
272             dbUtil.runSQLTemplateString(indexesSQL, true, false);
273         }
274         else if (buildAutoUpgrade) {
275             if (_log.isInfoEnabled()) {
276                 _log.info(
277                     "Upgrading " + buildNamespace +
278                         " database to build number " + buildNumber);
279             }
280 
281             if (!tablesSQL.equals(
282                     previousServiceComponent.getTablesSQL())) {
283 
284                 if (_log.isInfoEnabled()) {
285                     _log.info("Upgrading database with tables.sql");
286                 }
287 
288                 dbUtil.runSQLTemplateString(tablesSQL, true, false);
289 
290                 upgradeModels(classLoader);
291             }
292 
293             if (!sequencesSQL.equals(
294                     previousServiceComponent.getSequencesSQL())) {
295 
296                 if (_log.isInfoEnabled()) {
297                     _log.info("Upgrading database with sequences.sql");
298                 }
299 
300                 dbUtil.runSQLTemplateString(sequencesSQL, true, false);
301             }
302 
303             if (!indexesSQL.equals(
304                     previousServiceComponent.getIndexesSQL())) {
305 
306                 if (_log.isInfoEnabled()) {
307                     _log.info("Upgrading database with indexes.sql");
308                 }
309 
310                 dbUtil.runSQLTemplateString(indexesSQL, true, false);
311             }
312         }
313     }
314 
315     protected void upgradeModels(ClassLoader classLoader) throws Exception {
316         List<String> models = getModels(classLoader);
317 
318         for (String name : models) {
319             int pos = name.lastIndexOf(".model.");
320 
321             name =
322                 name.substring(0, pos) + ".model.impl." +
323                     name.substring(pos + 7) + "ModelImpl";
324 
325             Class<?> modelClass = Class.forName(name, true, classLoader);
326 
327             Field tableNameField = modelClass.getField("TABLE_NAME");
328             Field tableColumnsField = modelClass.getField("TABLE_COLUMNS");
329             Field tableSQLCreateField = modelClass.getField("TABLE_SQL_CREATE");
330             Field dataSourceField = modelClass.getField("DATA_SOURCE");
331 
332             String tableName = (String)tableNameField.get(null);
333             Object[][] tableColumns = (Object[][])tableColumnsField.get(null);
334             String tableSQLCreate = (String)tableSQLCreateField.get(null);
335             String dataSource = (String)dataSourceField.get(null);
336 
337             if (!dataSource.equals(Entity.DEFAULT_DATA_SOURCE)) {
338                 continue;
339             }
340 
341             UpgradeTable upgradeTable = new DefaultUpgradeTableImpl(
342                 tableName, tableColumns);
343 
344             upgradeTable.setCreateSQL(tableSQLCreate);
345 
346             upgradeTable.updateTable();
347         }
348     }
349 
350     protected void removeOldServiceComponents(String buildNamespace)
351         throws SystemException {
352 
353         int serviceComponentsCount =
354             serviceComponentPersistence.countByBuildNamespace(buildNamespace);
355 
356         if (serviceComponentsCount < _MAX_SERVICE_COMPONENTS) {
357             return;
358         }
359 
360         List<ServiceComponent> serviceComponents =
361             serviceComponentPersistence.findByBuildNamespace(
362                 buildNamespace, _MAX_SERVICE_COMPONENTS,
363                 serviceComponentsCount);
364 
365         for (int i = 0; i < serviceComponents.size(); i++) {
366             ServiceComponent serviceComponent = serviceComponents.get(i);
367 
368             serviceComponentPersistence.remove(serviceComponent);
369         }
370     }
371 
372     private static final int _MAX_SERVICE_COMPONENTS = 10;
373 
374     private static Log _log =
375         LogFactoryUtil.getLog(ServiceComponentLocalServiceImpl.class);
376 
377 }