001    package org.apache.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    
022    import java.io.Serializable;
023    import java.sql.Connection;
024    import java.sql.DriverManager;
025    import java.sql.SQLException;
026    import java.sql.Statement;
027    import java.util.Collections;
028    import java.util.HashSet;
029    import java.util.Set;
030    
031    import org.apache.commons.logging.Log;
032    import org.apache.commons.logging.LogFactory;
033    import org.apache.jcs.auxiliary.AuxiliaryCache;
034    import org.apache.jcs.auxiliary.AuxiliaryCacheAttributes;
035    import org.apache.jcs.auxiliary.AuxiliaryCacheFactory;
036    import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheAttributes;
037    import org.apache.jcs.auxiliary.disk.jdbc.JDBCDiskCacheManager;
038    import org.apache.jcs.engine.behavior.ICompositeCacheManager;
039    import org.apache.jcs.engine.behavior.IElementSerializer;
040    import org.apache.jcs.engine.control.CompositeCacheManager;
041    import org.apache.jcs.engine.logging.behavior.ICacheEventLogger;
042    
043    /**
044     * This factory should create mysql disk caches.
045     * <p>
046     * @author Aaron Smuts
047     */
048    public class HSQLDiskCacheFactory
049        implements AuxiliaryCacheFactory
050    {
051        /** The logger */
052        private final static Log log = LogFactory.getLog( HSQLDiskCacheFactory.class );
053    
054        /** Name for logging, etc. */
055        private String name = "HSQLDiskCacheFactory";
056    
057        /** The databases. */
058        private final Set<String> databases = Collections.synchronizedSet( new HashSet<String>() );
059    
060        /**
061         * This factory method should create an instance of the hsqlcache.
062         * <p>
063         * @param rawAttr
064         * @param arg1
065         * @param cacheEventLogger
066         * @param elementSerializer
067         * @return AuxiliaryCache
068         */
069        public <K extends Serializable, V extends Serializable> AuxiliaryCache<K, V> createCache( AuxiliaryCacheAttributes rawAttr, ICompositeCacheManager arg1,
070                                           ICacheEventLogger cacheEventLogger, IElementSerializer elementSerializer )
071        {
072            JDBCDiskCacheManager mgr = JDBCDiskCacheManager.getInstance( (JDBCDiskCacheAttributes) rawAttr,
073                                                                         CompositeCacheManager.getUnconfiguredInstance(),
074                                                                         cacheEventLogger, elementSerializer );
075            try
076            {
077                setupDatabase( (JDBCDiskCacheAttributes) rawAttr );
078            }
079            catch ( Exception e )
080            {
081                // TODO we may not want to try and get the cache at this point.
082                log.error( "Problem setting up database.", e );
083            }
084            return mgr.getCache( (JDBCDiskCacheAttributes) rawAttr );
085        }
086    
087        /**
088         * The name of the factory.
089         * <p>
090         * @param nameArg
091         */
092        public void setName( String nameArg )
093        {
094            name = nameArg;
095        }
096    
097        /**
098         * Returns the display name
099         * <p>
100         * @return name
101         */
102        public String getName()
103        {
104            return name;
105        }
106    
107        /**
108         * Creates the database if it doesn't exist, registers the driver class, etc.
109         * <p>
110         * @param attributes
111         * @throws Exception
112         */
113        protected void setupDatabase( JDBCDiskCacheAttributes attributes )
114            throws Exception
115        {
116            if ( attributes == null )
117            {
118                throw new Exception( "The attributes are null." );
119            }
120    
121            // url should start with "jdbc:hsqldb:"
122            String database = attributes.getUrl() + attributes.getDatabase();
123    
124            if ( databases.contains( database ) )
125            {
126                if ( log.isInfoEnabled() )
127                {
128                    log.info( "We already setup database [" + database + "]" );
129                }
130                return;
131            }
132    
133            // TODO get this from the attributes.
134            System.setProperty( "hsqldb.cache_scale", "8" );
135    
136            // "org.hsqldb.jdbcDriver"
137            String driver = attributes.getDriverClassName();
138            // "sa"
139            String user = attributes.getUserName();
140            // ""
141            String password = attributes.getPassword();
142    
143            new org.hsqldb.jdbcDriver();
144            try
145            {
146                Class.forName( driver ).newInstance();
147    
148                Connection cConn = DriverManager.getConnection( database, user, password );
149    
150                setupTABLE( cConn, attributes.getTableName() );
151    
152                if ( log.isInfoEnabled() )
153                {
154                    log.info( "Finished setting up database [" + database + "]" );
155                }
156    
157                databases.add( database );
158            }
159            catch ( Exception e )
160            {
161                log.error( "Fatal problem setting up the database.", e );
162            }
163        }
164    
165        /**
166         * SETUP TABLE FOR CACHE
167         * <p>
168         * @param cConn
169         * @param tableName
170         */
171        private void setupTABLE( Connection cConn, String tableName ) throws SQLException
172        {
173            boolean newT = true;
174    
175            // TODO make the cached nature of the table configurable
176            StringBuffer createSql = new StringBuffer();
177            createSql.append( "CREATE CACHED TABLE " + tableName );
178            createSql.append( "( " );
179            createSql.append( "CACHE_KEY             VARCHAR(250)          NOT NULL, " );
180            createSql.append( "REGION                VARCHAR(250)          NOT NULL, " );
181            createSql.append( "ELEMENT               BINARY, " );
182            createSql.append( "CREATE_TIME           DATE, " );
183            createSql.append( "CREATE_TIME_SECONDS   BIGINT, " );
184            createSql.append( "MAX_LIFE_SECONDS      BIGINT, " );
185            createSql.append( "SYSTEM_EXPIRE_TIME_SECONDS      BIGINT, " );
186            createSql.append( "IS_ETERNAL            CHAR(1), " );
187            createSql.append( "PRIMARY KEY (CACHE_KEY, REGION) " );
188            createSql.append( ");" );
189    
190            Statement sStatement = cConn.createStatement();
191    
192            try
193            {
194                sStatement.executeQuery( createSql.toString() );
195                sStatement.close();
196            }
197            catch ( SQLException e )
198            {
199                // FIXME: This is not reliable
200                if ( e.toString().indexOf( "already exists" ) != -1 )
201                {
202                    newT = false;
203                }
204                else
205                {
206                    throw e;
207                }
208            }
209    
210            // TODO create an index on SYSTEM_EXPIRE_TIME_SECONDS
211            String setupData[] = { "create index iKEY on " + tableName + " (CACHE_KEY, REGION)" };
212    
213            if ( newT )
214            {
215                for ( int i = 1; i < setupData.length; i++ )
216                {
217                    try
218                    {
219                        sStatement.executeQuery( setupData[i] );
220                    }
221                    catch ( SQLException e )
222                    {
223                        log.error( "Exception caught when creating index." + e );
224                    }
225                }
226            }
227        }
228    }