| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.cocoon.components.source.impl; |
| |
| import java.sql.Connection; |
| import java.sql.PreparedStatement; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.apache.avalon.excalibur.datasource.DataSourceComponent; |
| import org.apache.avalon.framework.activity.Initializable; |
| import org.apache.avalon.framework.configuration.Configurable; |
| import org.apache.avalon.framework.configuration.Configuration; |
| import org.apache.avalon.framework.configuration.ConfigurationException; |
| import org.apache.avalon.framework.service.ServiceException; |
| import org.apache.avalon.framework.service.ServiceManager; |
| import org.apache.avalon.framework.service.ServiceSelector; |
| import org.apache.avalon.framework.service.Serviceable; |
| import org.apache.avalon.framework.thread.ThreadSafe; |
| import org.apache.cocoon.caching.Cache; |
| import org.apache.cocoon.caching.EventAware; |
| import org.apache.cocoon.caching.validity.EventValidity; |
| import org.apache.cocoon.caching.validity.NameValueEvent; |
| import org.apache.cocoon.components.source.SourceDescriptor; |
| import org.apache.cocoon.components.source.helpers.SourceProperty; |
| import org.apache.excalibur.source.Source; |
| import org.apache.excalibur.source.SourceException; |
| import org.apache.excalibur.source.SourceValidity; |
| |
| /** |
| * Simple SourceDescriptor implementation that can stores |
| * properties over JDBC. |
| * |
| * <p> |
| * The descriptor is to be configured with the name of a datasource that |
| * contains a table with the following scheme: |
| * <p> |
| * <code> |
| * CREATE TABLE SOURCEPROPS(<br> |
| * SOURCE VARCHAR NOT NULL,<br> |
| * NAMESPACE VARCHAR NOT NULL,<br> |
| * NAME VARCHAR NOT NULL,<br> |
| * VALUE VARCHAR NOT NULL,<br> |
| * CONSTRAINT SYS_CT_11 UNIQUE(SOURCE,NAMESPACE,NAME))<br> |
| * </code> |
| * </p> |
| * </p> |
| * <p> |
| * The implementation will attempt to connect to the EventAware cache in |
| * order to notify it during changes. If it can't find the EventAware cache |
| * sources that are described by this SourceDescriptor will NOT be cacheable. |
| * </p> |
| * |
| * @version $Id$ |
| */ |
| public class SimpleJdbcSourceDescriptor |
| extends AbstractConfigurableSourceDescriptor |
| implements SourceDescriptor, Serviceable, Configurable, Initializable, ThreadSafe { |
| |
| |
| private static final String STMT_SELECT_SINGLE = |
| "SELECT value FROM sourceprops WHERE source=? AND namespace=? AND name=?;"; |
| |
| private static final String STMT_SELECT_ALL = |
| "SELECT namespace, name, value FROM sourceprops WHERE source=?;"; |
| |
| private static final String STMT_INSERT = |
| "INSERT INTO sourceprops (source,namespace,name,value) VALUES (?,?,?,?);"; |
| |
| private static final String STMT_DELETE = |
| "DELETE FROM sourceprops WHERE source=? AND namespace=? AND name=?;"; |
| |
| private ServiceManager m_manager; |
| private EventAware m_cache; |
| private DataSourceComponent m_datasource; |
| |
| private String m_datasourceName; |
| private String m_eventName; |
| |
| |
| // ---------------------------------------------------- Lifecycle |
| |
| public SimpleJdbcSourceDescriptor() { |
| } |
| |
| public void service(ServiceManager manager) throws ServiceException { |
| m_manager = manager; |
| if (manager.hasService(Cache.ROLE + "/EventAware")) { |
| m_cache = (EventAware) manager.lookup(Cache.ROLE + "/EventAware"); |
| } else { |
| getLogger().warn("EventAware cache was not found: sources won't be cacheable."); |
| } |
| } |
| |
| /** |
| * Configuration options: |
| * |
| * <ul> |
| * <li>element <code>property</code> (multiple,required) |
| * - define a property that this store should handle.</li> |
| * <li>element <code>datasource</code> (single,optional,[cocoondb]) |
| * - the name of the excalibur datasource to use.</li> |
| * </ul> |
| */ |
| public void configure(final Configuration configuration) throws ConfigurationException { |
| super.configure(configuration); |
| m_datasourceName = configuration.getChild("datasource",true).getValue("cocoondb"); |
| } |
| |
| public void initialize() throws Exception { |
| ServiceSelector datasources = (ServiceSelector) m_manager.lookup( |
| DataSourceComponent.ROLE + "Selector"); |
| m_datasource = (DataSourceComponent) datasources.select(m_datasourceName); |
| } |
| |
| // ---------------------------------------------------- SourceInspection |
| |
| public SourceProperty[] getSourceProperties(Source source) |
| throws SourceException { |
| |
| Connection connection = null; |
| PreparedStatement stmt = null; |
| try { |
| connection = m_datasource.getConnection(); |
| stmt = connection.prepareStatement(STMT_SELECT_ALL); |
| stmt.setString(1,source.getURI()); |
| ResultSet result = stmt.executeQuery(); |
| List properties = new ArrayList(); |
| while (result.next()) { |
| SourceProperty property = new SourceProperty( |
| result.getString(1),result.getString(2),result.getString(3)); |
| if (handlesProperty(property.getNamespace(),property.getName())) { |
| properties.add(property); |
| } |
| } |
| result.close(); |
| stmt.close(); |
| return (SourceProperty[]) properties.toArray( |
| new SourceProperty[properties.size()]); |
| } |
| catch (SQLException e) { |
| throw new SourceException("Error retrieving properties from database",e); |
| } |
| finally { |
| if (connection != null) { |
| try { |
| connection.close(); |
| } |
| catch (SQLException e) {} |
| } |
| } |
| } |
| |
| public SourceProperty doGetSourceProperty( |
| Source source, |
| String namespace, |
| String name) |
| throws SourceException { |
| |
| Connection connection = null; |
| PreparedStatement stmt = null; |
| try { |
| connection = m_datasource.getConnection(); |
| stmt = connection.prepareStatement(STMT_SELECT_SINGLE); |
| stmt.setString(1,source.getURI()); |
| stmt.setString(2,namespace); |
| stmt.setString(3,name); |
| ResultSet result = stmt.executeQuery(); |
| SourceProperty property = null; |
| if (result.next()) { |
| property = new SourceProperty( |
| namespace, |
| name, |
| result.getString(1)); |
| } |
| result.close(); |
| stmt.close(); |
| return property; |
| } |
| catch (SQLException e) { |
| throw new SourceException("Error retrieving property from database",e); |
| } |
| finally { |
| if (connection != null) { |
| try { |
| connection.close(); |
| } |
| catch (SQLException e) {} |
| } |
| } |
| } |
| |
| public void doSetSourceProperty(Source source, SourceProperty property) |
| throws SourceException { |
| |
| Connection connection = null; |
| PreparedStatement stmt = null; |
| |
| try { |
| connection = m_datasource.getConnection(); |
| stmt = connection.prepareStatement(STMT_DELETE); |
| stmt.setString(1,source.getURI()); |
| stmt.setString(2,property.getNamespace()); |
| stmt.setString(3,property.getName()); |
| int count = stmt.executeUpdate(); |
| stmt.close(); |
| |
| stmt = connection.prepareStatement(STMT_INSERT); |
| stmt.setString(1,source.getURI()); |
| stmt.setString(2,property.getNamespace()); |
| stmt.setString(3,property.getName()); |
| stmt.setString(4,property.getValueAsString()); |
| |
| count += stmt.executeUpdate(); |
| stmt.close(); |
| connection.commit(); |
| |
| if (m_cache != null && count > 0) { |
| m_cache.processEvent(new NameValueEvent(m_eventName,source.getURI())); |
| } |
| } |
| catch (SQLException e) { |
| throw new SourceException("Error setting property",e); |
| } |
| finally { |
| if (connection != null) { |
| try { |
| connection.close(); |
| } catch (SQLException e) {} |
| } |
| } |
| } |
| |
| public void doRemoveSourceProperty( |
| Source source, |
| String namespace, |
| String name) |
| throws SourceException { |
| |
| Connection connection = null; |
| PreparedStatement stmt = null; |
| |
| try { |
| connection = m_datasource.getConnection(); |
| stmt = connection.prepareStatement(STMT_DELETE); |
| stmt.setString(1,source.getURI()); |
| stmt.setString(2,namespace); |
| stmt.setString(3,name); |
| |
| int count = stmt.executeUpdate(); |
| stmt.close(); |
| connection.commit(); |
| |
| if (m_cache != null && count > 0) { |
| m_cache.processEvent(new NameValueEvent(m_eventName,source.getURI())); |
| } |
| } |
| catch (SQLException e) { |
| throw new SourceException("Error removing propery",e); |
| } |
| finally { |
| if (connection != null) { |
| try { |
| connection.close(); |
| } catch (SQLException e) {} |
| } |
| } |
| } |
| |
| public SourceValidity getValidity(Source source) { |
| if (m_cache != null) { |
| return new EventValidity(new NameValueEvent(getEventName(),source.getURI())); |
| } |
| return null; |
| } |
| |
| private final String getEventName() { |
| if (m_eventName == null) { |
| Connection connection = null; |
| try { |
| connection = m_datasource.getConnection(); |
| String catalogName = connection.getCatalog(); |
| m_eventName = (catalogName != null) |
| ? catalogName + "/sourceprops" |
| : "sourceprops"; |
| } |
| catch (SQLException e) { |
| getLogger().warn("Error getting catalog name from jdbc connection.",e); |
| m_eventName = "sourceprops"; |
| } |
| finally { |
| if (connection != null) { |
| try { |
| connection.close(); |
| } catch (SQLException e) {} |
| } |
| } |
| } |
| return m_eventName; |
| } |
| } |