1   /**
2    * Copyright (c) 2000-2008 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.util.HttpUtil;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.kernel.util.StringUtil;
31  import com.liferay.portal.model.ModelHintsUtil;
32  import com.liferay.portal.model.ServiceComponent;
33  import com.liferay.portal.service.base.ServiceComponentLocalServiceBaseImpl;
34  import com.liferay.portal.tools.servicebuilder.Entity;
35  import com.liferay.portal.tools.sql.DBUtil;
36  import com.liferay.portal.upgrade.util.DefaultUpgradeTableImpl;
37  import com.liferay.portal.upgrade.util.UpgradeTable;
38  import com.liferay.util.xml.XMLFormatter;
39  
40  import java.io.IOException;
41  import java.io.StringReader;
42  
43  import java.lang.reflect.Field;
44  
45  import java.util.ArrayList;
46  import java.util.Iterator;
47  import java.util.List;
48  
49  import javax.servlet.ServletContext;
50  
51  import org.apache.commons.logging.Log;
52  import org.apache.commons.logging.LogFactory;
53  
54  import org.dom4j.Document;
55  import org.dom4j.DocumentException;
56  import org.dom4j.DocumentFactory;
57  import org.dom4j.Element;
58  import org.dom4j.io.SAXReader;
59  
60  /**
61   * <a href="ServiceComponentLocalServiceImpl.java.html"><b><i>View Source</i>
62   * </b></a>
63   *
64   * @author Brian Wing Shun Chan
65   *
66   */
67  public class ServiceComponentLocalServiceImpl
68      extends ServiceComponentLocalServiceBaseImpl {
69  
70      public ServiceComponent updateServiceComponent(
71              ServletContext servletContext, ClassLoader classLoader,
72              String buildNamespace, long buildNumber, long buildDate)
73          throws PortalException, SystemException {
74  
75          try {
76              ModelHintsUtil.read(
77                  classLoader, "META-INF/portlet-model-hints.xml");
78          }
79          catch (Exception e) {
80              throw new SystemException(e);
81          }
82  
83          ServiceComponent serviceComponent = null;
84          ServiceComponent previousServiceComponent = null;
85  
86          List<ServiceComponent> serviceComponents =
87              serviceComponentPersistence.findByBuildNamespace(
88                  buildNamespace, 0, 1);
89  
90          if (serviceComponents.size() == 0) {
91              long serviceComponentId = counterLocalService.increment();
92  
93              serviceComponent = serviceComponentPersistence.create(
94                  serviceComponentId);
95  
96              serviceComponent.setBuildNamespace(buildNamespace);
97              serviceComponent.setBuildNumber(buildNumber);
98              serviceComponent.setBuildDate(buildDate);
99          }
100         else {
101             serviceComponent = serviceComponents.get(0);
102 
103             if (serviceComponent.getBuildNumber() < buildNumber) {
104                 previousServiceComponent = serviceComponent;
105 
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 if (serviceComponent.getBuildNumber() > buildNumber) {
116                 throw new OldServiceComponentException(
117                     "Build namespace " + buildNamespace + " has build number " +
118                         serviceComponent.getBuildNumber() +
119                             " which is newer than " + buildNumber);
120             }
121             else {
122                 return serviceComponent;
123             }
124         }
125 
126         try {
127             DocumentFactory docFactory = DocumentFactory.getInstance();
128 
129             Document doc = docFactory.createDocument(StringPool.UTF8);
130 
131             Element data = doc.addElement("data");
132 
133             String tablesSQL = HttpUtil.URLtoString(servletContext.getResource(
134                 "/WEB-INF/sql/tables.sql"));
135 
136             data.addElement("tables-sql").addCDATA(tablesSQL);
137 
138             String sequencesSQL = HttpUtil.URLtoString(
139                 servletContext.getResource("/WEB-INF/sql/sequences.sql"));
140 
141             data.addElement("sequences-sql").addCDATA(sequencesSQL);
142 
143             String indexesSQL = HttpUtil.URLtoString(servletContext.getResource(
144                 "/WEB-INF/sql/indexes.sql"));
145 
146             data.addElement("indexes-sql").addCDATA(indexesSQL);
147 
148             String dataXML = XMLFormatter.toString(doc);
149 
150             serviceComponent.setData(dataXML);
151 
152             serviceComponentPersistence.update(serviceComponent, false);
153 
154             upgradeDB(
155                 classLoader, buildNamespace, buildNumber,
156                 previousServiceComponent, tablesSQL, sequencesSQL, indexesSQL);
157 
158             removeOldServiceComponents(buildNamespace);
159 
160             return serviceComponent;
161         }
162         catch (Exception e) {
163             throw new SystemException(e);
164         }
165     }
166 
167     protected String[] getModels(ClassLoader classLoader)
168         throws DocumentException, IOException {
169 
170         List<String> models = new ArrayList<String>();
171 
172         String xml = StringUtil.read(
173             classLoader, "META-INF/portlet-model-hints.xml");
174 
175         SAXReader reader = new SAXReader();
176 
177         Document doc = reader.read(new StringReader(xml));
178 
179         Element root = doc.getRootElement();
180 
181         Iterator<Element> itr = root.elements("model").iterator();
182 
183         while (itr.hasNext()) {
184             Element modelEl = itr.next();
185 
186             String name = modelEl.attributeValue("name");
187 
188             models.add(name);
189         }
190 
191         return models.toArray(new String[models.size()]);
192     }
193 
194     protected void upgradeDB(
195             ClassLoader classLoader, String buildNamespace, long buildNumber,
196             ServiceComponent previousServiceComponent, String tablesSQL,
197             String sequencesSQL, String indexesSQL)
198         throws Exception {
199 
200         DBUtil dbUtil = DBUtil.getInstance();
201 
202         if (previousServiceComponent == null) {
203             if (_log.isInfoEnabled()) {
204                 _log.info(
205                     "Running " + buildNamespace +
206                         " SQL scripts for the first time");
207             }
208 
209             dbUtil.runSQLTemplateString(tablesSQL, true, false);
210             dbUtil.runSQLTemplateString(sequencesSQL, true, false);
211             dbUtil.runSQLTemplateString(indexesSQL, true, false);
212         }
213         else {
214             if (_log.isInfoEnabled()) {
215                 _log.info(
216                     "Upgrading " + buildNamespace +
217                         " database to build number " + buildNumber);
218             }
219 
220             if (!tablesSQL.equals(
221                     previousServiceComponent.getTablesSQL())) {
222 
223                 if (_log.isInfoEnabled()) {
224                     _log.info("Upgrading database with tables.sql");
225                 }
226 
227                 dbUtil.runSQLTemplateString(tablesSQL, true, false);
228 
229                 upgradeModels(classLoader);
230             }
231 
232             if (!sequencesSQL.equals(
233                     previousServiceComponent.getSequencesSQL())) {
234 
235                 if (_log.isInfoEnabled()) {
236                     _log.info("Upgrading database with sequences.sql");
237                 }
238 
239                 dbUtil.runSQLTemplateString(sequencesSQL, true, false);
240             }
241 
242             if (!indexesSQL.equals(
243                     previousServiceComponent.getIndexesSQL())) {
244 
245                 if (_log.isInfoEnabled()) {
246                     _log.info("Upgrading database with indexes.sql");
247                 }
248 
249                 dbUtil.runSQLTemplateString(indexesSQL, true, false);
250             }
251         }
252     }
253 
254     protected void upgradeModels(ClassLoader classLoader) throws Exception {
255         String[] models = getModels(classLoader);
256 
257         for (int i = 0; i < models.length; i++) {
258             String name = models[i];
259 
260             int pos = name.lastIndexOf(".model.");
261 
262             name =
263                 name.substring(0, pos) + ".model.impl." +
264                     name.substring(pos + 7) + "ModelImpl";
265 
266             Class<?> modelClass = Class.forName(name, true, classLoader);
267 
268             Field tableNameField = modelClass.getField("TABLE_NAME");
269             Field tableColumnsField = modelClass.getField("TABLE_COLUMNS");
270             Field tableSQLCreateField = modelClass.getField("TABLE_SQL_CREATE");
271             Field dataSourceField = modelClass.getField("DATA_SOURCE");
272 
273             String tableName = (String)tableNameField.get(null);
274             Object[][] tableColumns = (Object[][])tableColumnsField.get(null);
275             String tableSQLCreate = (String)tableSQLCreateField.get(null);
276             String dataSource = (String)dataSourceField.get(null);
277 
278             if (!dataSource.equals(Entity.DEFAULT_DATA_SOURCE)) {
279                 continue;
280             }
281 
282             UpgradeTable upgradeTable = new DefaultUpgradeTableImpl(
283                 tableName, tableColumns);
284 
285             upgradeTable.setCreateSQL(tableSQLCreate);
286 
287             upgradeTable.updateTable();
288         }
289     }
290 
291     protected void removeOldServiceComponents(String buildNamespace)
292         throws SystemException {
293 
294         int serviceComponentsCount =
295             serviceComponentPersistence.countByBuildNamespace(buildNamespace);
296 
297         if (serviceComponentsCount < _MAX_SERVICE_COMPONENTS) {
298             return;
299         }
300 
301         List<ServiceComponent> serviceComponents =
302             serviceComponentPersistence.findByBuildNamespace(
303                 buildNamespace, _MAX_SERVICE_COMPONENTS,
304                 serviceComponentsCount);
305 
306         for (int i = 0; i < serviceComponents.size(); i++) {
307             ServiceComponent serviceComponent = serviceComponents.get(i);
308 
309             serviceComponentPersistence.remove(serviceComponent);
310         }
311     }
312 
313     private static final int _MAX_SERVICE_COMPONENTS = 10;
314 
315     private static Log _log =
316         LogFactory.getLog(ServiceComponentLocalServiceImpl.class);
317 
318 }