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