001package org.apache.commons.jcs.auxiliary.disk.jdbc.hsql;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.sql.Connection;
023import java.sql.DriverManager;
024import java.sql.SQLException;
025import java.sql.Statement;
026import java.util.Collections;
027import java.util.HashSet;
028import java.util.Set;
029
030import org.apache.commons.jcs.auxiliary.AuxiliaryCacheAttributes;
031import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCache;
032import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
033import org.apache.commons.jcs.auxiliary.disk.jdbc.JDBCDiskCacheFactory;
034import org.apache.commons.jcs.engine.behavior.ICompositeCacheManager;
035import org.apache.commons.jcs.engine.behavior.IElementSerializer;
036import org.apache.commons.jcs.engine.logging.behavior.ICacheEventLogger;
037import org.apache.commons.logging.Log;
038import org.apache.commons.logging.LogFactory;
039
040/**
041 * This factory should create hsql disk caches.
042 * <p>
043 * @author Aaron Smuts
044 */
045public class HSQLDiskCacheFactory
046    extends JDBCDiskCacheFactory
047{
048    /** The logger */
049    private static final Log log = LogFactory.getLog( HSQLDiskCacheFactory.class );
050
051    /** The databases. */
052    private Set<String> databases;
053
054    /**
055     * This factory method should create an instance of the hsqlcache.
056     * <p>
057     * @param rawAttr
058     * @param compositeCacheManager
059     * @param cacheEventLogger
060     * @param elementSerializer
061     * @return JDBCDiskCache
062     * @throws SQLException if the creation of the cache instance fails
063     */
064    @Override
065    public <K, V> JDBCDiskCache<K, V> createCache( AuxiliaryCacheAttributes rawAttr,
066                        ICompositeCacheManager compositeCacheManager,
067                        ICacheEventLogger cacheEventLogger,
068                        IElementSerializer elementSerializer )
069                        throws SQLException
070    {
071        setupDatabase( (JDBCDiskCacheAttributes) rawAttr );
072        return super.createCache(rawAttr, compositeCacheManager, cacheEventLogger, elementSerializer);
073    }
074
075    /**
076     * Initialize this factory
077     */
078    @Override
079    public void initialize()
080    {
081        super.initialize();
082        this.databases = Collections.synchronizedSet( new HashSet<String>() );
083    }
084
085    /**
086     * Creates the database if it doesn't exist, registers the driver class, etc.
087     * <p>
088     * @param attributes
089     * @throws SQLException
090     */
091    protected void setupDatabase( JDBCDiskCacheAttributes attributes )
092        throws SQLException
093    {
094        if ( attributes == null )
095        {
096            throw new SQLException( "The attributes are null." );
097        }
098
099        // url should start with "jdbc:hsqldb:"
100        String database = attributes.getUrl() + attributes.getDatabase();
101
102        if ( databases.contains( database ) )
103        {
104            if ( log.isInfoEnabled() )
105            {
106                log.info( "We already setup database [" + database + "]" );
107            }
108            return;
109        }
110
111        // TODO get this from the attributes.
112        System.setProperty( "hsqldb.cache_scale", "8" );
113
114        // "org.hsqldb.jdbcDriver"
115        String driver = attributes.getDriverClassName();
116        // "sa"
117        String user = attributes.getUserName();
118        // ""
119        String password = attributes.getPassword();
120
121        try
122        {
123            Class.forName( driver ).newInstance();
124        }
125        catch (Exception e)
126        {
127            throw new SQLException( "Could not initialize driver " + driver, e );
128        }
129
130        Connection cConn = DriverManager.getConnection( database, user, password );
131        setupTable( cConn, attributes.getTableName() );
132
133        if ( log.isInfoEnabled() )
134        {
135            log.info( "Finished setting up database [" + database + "]" );
136        }
137
138        databases.add( database );
139    }
140
141    /**
142     * SETUP TABLE FOR CACHE
143     * <p>
144     * @param cConn
145     * @param tableName
146     */
147    protected void setupTable( Connection cConn, String tableName ) throws SQLException
148    {
149        // TODO make the cached nature of the table configurable
150        StringBuilder createSql = new StringBuilder();
151        createSql.append( "CREATE CACHED TABLE ").append( tableName );
152        createSql.append( "( " );
153        createSql.append( "CACHE_KEY             VARCHAR(250)          NOT NULL, " );
154        createSql.append( "REGION                VARCHAR(250)          NOT NULL, " );
155        createSql.append( "ELEMENT               BINARY, " );
156        createSql.append( "CREATE_TIME           TIMESTAMP, " );
157        createSql.append( "UPDATE_TIME_SECONDS   BIGINT, " );
158        createSql.append( "MAX_LIFE_SECONDS      BIGINT, " );
159        createSql.append( "SYSTEM_EXPIRE_TIME_SECONDS      BIGINT, " );
160        createSql.append( "IS_ETERNAL            CHAR(1), " );
161        createSql.append( "PRIMARY KEY (CACHE_KEY, REGION) " );
162        createSql.append( ");" );
163
164        Statement sStatement = cConn.createStatement();
165
166        try
167        {
168            sStatement.execute( createSql.toString() );
169        }
170        catch ( SQLException e )
171        {
172            if (!"23000".equals(e.getSQLState()))
173            {
174                throw e;
175            }
176        }
177        finally
178        {
179            sStatement.close();
180        }
181    }
182}