001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.source.impl;
018:
019: import java.sql.Connection;
020: import java.sql.PreparedStatement;
021: import java.sql.ResultSet;
022: import java.sql.SQLException;
023: import java.util.ArrayList;
024: import java.util.List;
025:
026: import org.apache.avalon.excalibur.datasource.DataSourceComponent;
027: import org.apache.avalon.framework.activity.Initializable;
028: import org.apache.avalon.framework.configuration.Configurable;
029: import org.apache.avalon.framework.configuration.Configuration;
030: import org.apache.avalon.framework.configuration.ConfigurationException;
031: import org.apache.avalon.framework.service.ServiceException;
032: import org.apache.avalon.framework.service.ServiceManager;
033: import org.apache.avalon.framework.service.ServiceSelector;
034: import org.apache.avalon.framework.service.Serviceable;
035: import org.apache.avalon.framework.thread.ThreadSafe;
036: import org.apache.cocoon.caching.Cache;
037: import org.apache.cocoon.caching.EventAware;
038: import org.apache.cocoon.caching.validity.EventValidity;
039: import org.apache.cocoon.caching.validity.NameValueEvent;
040: import org.apache.cocoon.components.source.SourceDescriptor;
041: import org.apache.cocoon.components.source.helpers.SourceProperty;
042: import org.apache.excalibur.source.Source;
043: import org.apache.excalibur.source.SourceException;
044: import org.apache.excalibur.source.SourceValidity;
045:
046: /**
047: * Simple SourceDescriptor implementation that can stores
048: * properties over JDBC.
049: *
050: * <p>
051: * The descriptor is to be configured with the name of a datasource that
052: * contains a table with the following scheme:
053: * <p>
054: * <code>
055: * CREATE TABLE SOURCEPROPS(<br>
056: * SOURCE VARCHAR NOT NULL,<br>
057: * NAMESPACE VARCHAR NOT NULL,<br>
058: * NAME VARCHAR NOT NULL,<br>
059: * VALUE VARCHAR NOT NULL,<br>
060: * CONSTRAINT SYS_CT_11 UNIQUE(SOURCE,NAMESPACE,NAME))<br>
061: * </code>
062: * </p>
063: * </p>
064: * <p>
065: * The implementation will attempt to connect to the EventAware cache in
066: * order to notify it during changes. If it can't find the EventAware cache
067: * sources that are described by this SourceDescriptor will NOT be cacheable.
068: * </p>
069: *
070: * @version $Id: SimpleJdbcSourceDescriptor.java 433543 2006-08-22 06:22:54Z crossley $
071: */
072: public class SimpleJdbcSourceDescriptor extends
073: AbstractConfigurableSourceDescriptor implements
074: SourceDescriptor, Serviceable, Configurable, Initializable,
075: ThreadSafe {
076:
077: private static final String STMT_SELECT_SINGLE = "SELECT value FROM sourceprops WHERE source=? AND namespace=? AND name=?;";
078:
079: private static final String STMT_SELECT_ALL = "SELECT namespace, name, value FROM sourceprops WHERE source=?;";
080:
081: private static final String STMT_INSERT = "INSERT INTO sourceprops (source,namespace,name,value) VALUES (?,?,?,?);";
082:
083: private static final String STMT_DELETE = "DELETE FROM sourceprops WHERE source=? AND namespace=? AND name=?;";
084:
085: private ServiceManager m_manager;
086: private EventAware m_cache;
087: private DataSourceComponent m_datasource;
088:
089: private String m_datasourceName;
090: private String m_eventName;
091:
092: // ---------------------------------------------------- Lifecycle
093:
094: public SimpleJdbcSourceDescriptor() {
095: }
096:
097: public void service(ServiceManager manager) throws ServiceException {
098: m_manager = manager;
099: if (manager.hasService(Cache.ROLE + "/EventAware")) {
100: m_cache = (EventAware) manager.lookup(Cache.ROLE
101: + "/EventAware");
102: } else {
103: getLogger()
104: .warn(
105: "EventAware cache was not found: sources won't be cacheable.");
106: }
107: }
108:
109: /**
110: * Configuration options:
111: *
112: * <ul>
113: * <li>element <code>property</code> (multiple,required)
114: * - define a property that this store should handle.</li>
115: * <li>element <code>datasource</code> (single,optional,[cocoondb])
116: * - the name of the excalibur datasource to use.</li>
117: * </ul>
118: */
119: public void configure(final Configuration configuration)
120: throws ConfigurationException {
121: super .configure(configuration);
122: m_datasourceName = configuration.getChild("datasource", true)
123: .getValue("cocoondb");
124: }
125:
126: public void initialize() throws Exception {
127: ServiceSelector datasources = (ServiceSelector) m_manager
128: .lookup(DataSourceComponent.ROLE + "Selector");
129: m_datasource = (DataSourceComponent) datasources
130: .select(m_datasourceName);
131: }
132:
133: // ---------------------------------------------------- SourceInspection
134:
135: public SourceProperty[] getSourceProperties(Source source)
136: throws SourceException {
137:
138: Connection connection = null;
139: PreparedStatement stmt = null;
140: try {
141: connection = m_datasource.getConnection();
142: stmt = connection.prepareStatement(STMT_SELECT_ALL);
143: stmt.setString(1, source.getURI());
144: ResultSet result = stmt.executeQuery();
145: List properties = new ArrayList();
146: while (result.next()) {
147: SourceProperty property = new SourceProperty(result
148: .getString(1), result.getString(2), result
149: .getString(3));
150: if (handlesProperty(property.getNamespace(), property
151: .getName())) {
152: properties.add(property);
153: }
154: }
155: result.close();
156: stmt.close();
157: return (SourceProperty[]) properties
158: .toArray(new SourceProperty[properties.size()]);
159: } catch (SQLException e) {
160: throw new SourceException(
161: "Error retrieving properties from database", e);
162: } finally {
163: if (connection != null) {
164: try {
165: connection.close();
166: } catch (SQLException e) {
167: }
168: }
169: }
170: }
171:
172: public SourceProperty doGetSourceProperty(Source source,
173: String namespace, String name) throws SourceException {
174:
175: Connection connection = null;
176: PreparedStatement stmt = null;
177: try {
178: connection = m_datasource.getConnection();
179: stmt = connection.prepareStatement(STMT_SELECT_SINGLE);
180: stmt.setString(1, source.getURI());
181: stmt.setString(2, namespace);
182: stmt.setString(3, name);
183: ResultSet result = stmt.executeQuery();
184: SourceProperty property = null;
185: if (result.next()) {
186: property = new SourceProperty(namespace, name, result
187: .getString(1));
188: }
189: result.close();
190: stmt.close();
191: return property;
192: } catch (SQLException e) {
193: throw new SourceException(
194: "Error retrieving property from database", e);
195: } finally {
196: if (connection != null) {
197: try {
198: connection.close();
199: } catch (SQLException e) {
200: }
201: }
202: }
203: }
204:
205: public void doSetSourceProperty(Source source,
206: SourceProperty property) throws SourceException {
207:
208: Connection connection = null;
209: PreparedStatement stmt = null;
210:
211: try {
212: connection = m_datasource.getConnection();
213: stmt = connection.prepareStatement(STMT_DELETE);
214: stmt.setString(1, source.getURI());
215: stmt.setString(2, property.getNamespace());
216: stmt.setString(3, property.getName());
217: int count = stmt.executeUpdate();
218: stmt.close();
219:
220: stmt = connection.prepareStatement(STMT_INSERT);
221: stmt.setString(1, source.getURI());
222: stmt.setString(2, property.getNamespace());
223: stmt.setString(3, property.getName());
224: stmt.setString(4, property.getValueAsString());
225:
226: count += stmt.executeUpdate();
227: stmt.close();
228: connection.commit();
229:
230: if (m_cache != null && count > 0) {
231: m_cache.processEvent(new NameValueEvent(m_eventName,
232: source.getURI()));
233: }
234: } catch (SQLException e) {
235: throw new SourceException("Error setting property", e);
236: } finally {
237: if (connection != null) {
238: try {
239: connection.close();
240: } catch (SQLException e) {
241: }
242: }
243: }
244: }
245:
246: public void doRemoveSourceProperty(Source source, String namespace,
247: String name) throws SourceException {
248:
249: Connection connection = null;
250: PreparedStatement stmt = null;
251:
252: try {
253: connection = m_datasource.getConnection();
254: stmt = connection.prepareStatement(STMT_DELETE);
255: stmt.setString(1, source.getURI());
256: stmt.setString(2, namespace);
257: stmt.setString(3, name);
258:
259: int count = stmt.executeUpdate();
260: stmt.close();
261: connection.commit();
262:
263: if (m_cache != null && count > 0) {
264: m_cache.processEvent(new NameValueEvent(m_eventName,
265: source.getURI()));
266: }
267: } catch (SQLException e) {
268: throw new SourceException("Error removing propery", e);
269: } finally {
270: if (connection != null) {
271: try {
272: connection.close();
273: } catch (SQLException e) {
274: }
275: }
276: }
277: }
278:
279: public SourceValidity getValidity(Source source) {
280: if (m_cache != null) {
281: return new EventValidity(new NameValueEvent(getEventName(),
282: source.getURI()));
283: }
284: return null;
285: }
286:
287: private final String getEventName() {
288: if (m_eventName == null) {
289: Connection connection = null;
290: try {
291: connection = m_datasource.getConnection();
292: String catalogName = connection.getCatalog();
293: m_eventName = (catalogName != null) ? catalogName
294: + "/sourceprops" : "sourceprops";
295: } catch (SQLException e) {
296: getLogger()
297: .warn(
298: "Error getting catalog name from jdbc connection.",
299: e);
300: m_eventName = "sourceprops";
301: } finally {
302: if (connection != null) {
303: try {
304: connection.close();
305: } catch (SQLException e) {
306: }
307: }
308: }
309: }
310: return m_eventName;
311: }
312: }
|