Statistics
| Revision:

root / trunk / libraries / geotools-2.1.1-epsg-hsql / src / org / geotools / referencing / factory / epsg / HSQLDataSource.java @ 28854

History | View | Annotate | Download (9.68 KB)

1
/*
2
 * Geotools 2 - OpenSource mapping toolkit
3
 * (C) 2005, Geotools Project Managment Committee (PMC)
4
 *
5
 *    This library is free software; you can redistribute it and/or
6
 *    modify it under the terms of the GNU Lesser General Public
7
 *    License as published by the Free Software Foundation; either
8
 *    version 2.1 of the License, or (at your option) any later version.
9
 *
10
 *    This library is distributed in the hope that it will be useful,
11
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 *    Lesser General Public License for more details.
14
 *
15
 *    You should have received a copy of the GNU Lesser General Public
16
 *    License along with this library; if not, write to the Free Software
17
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 */
19
package org.geotools.referencing.factory.epsg;
20

    
21
// J2SE dependencies
22
import java.io.File;
23
import java.io.IOException;
24
import java.io.BufferedReader;
25
import java.io.InputStreamReader;
26
import java.sql.ResultSet;
27
import java.sql.Statement;
28
import java.sql.Connection;
29
import java.sql.SQLException;
30
import java.util.logging.Logger;
31

    
32
// Geotools dependencies
33
import org.geotools.factory.Hints;
34
import org.geotools.referencing.factory.FactoryGroup;
35
import org.geotools.referencing.factory.AbstractAuthorityFactory;
36

    
37
// HSQL dependencies
38
import org.hsqldb.jdbc.jdbcDataSource;
39

    
40

    
41
/**
42
 * Connection to the EPSG database in HSQL database engine format using JDBC. The EPSG
43
 * database can be downloaded from <A HREF="http://www.epsg.org">http://www.epsg.org</A>.
44
 * The SQL scripts (modified for the HSQL syntax as <A HREF="doc-files/HSQL.html">explained
45
 * here</A>) are bundled into this plugin. The database version is given in the
46
 * {@linkplain org.opengis.metadata.citation.Citation#getEdition edition attribute}
47
 * of the {@linkplain org.opengis.referencing.AuthorityFactory#getAuthority authority}.
48
 * The HSQL database is read only.
49
 * <P>
50
 * <H3>Implementation note</H3>
51
 * The SQL scripts are executed the first time a connection is required. The database
52
 * is then created as cached tables ({@code HSQL.properties} and {@code HSQL.data} files)
53
 * in a temporary directory. Future connections to the EPSG database while reuse the cached
54
 * tables, if available. Otherwise, the scripts will be executed again in order to recreate
55
 * them.
56
 *
57
 * @version $Id: HSQLDataSource.java 14624 2005-06-29 02:19:08Z desruisseaux $
58
 * @author Martin Desruisseaux
59
 * @author Didier Richard
60
 *
61
 * @since 2.2
62
 */
63
public class HSQLDataSource extends jdbcDataSource implements DataSource {
64
        // 20090518 cmartinez: Use a different tmp dir for each geotools instance
65
        private static File tmpDir = null;
66
    /**
67
     * Creates a new instance of this data source
68
     */
69
    public HSQLDataSource() {
70
        File directory = getGtTmpDir();
71
        if (directory.isDirectory() || directory.mkdir()) {
72
            directory = new File(directory, "Cached databases");
73
            if (directory.isDirectory() || directory.mkdir()) {
74
                /*
75
                 * Constructs the full path to the HSQL database. Note: we do not use
76
                 * File.toURI() because HSQL doesn't seem to expect an encoded URL
77
                 * (e.g. "%20" instead of spaces).
78
                 */
79
                final StringBuffer url = new StringBuffer("jdbc:hsqldb:file:");
80
                final String path = directory.getAbsolutePath().replace(File.separatorChar, '/');
81
                if (path.length()==0 || path.charAt(0)!='/') {
82
                    url.append('/');
83
                }
84
                url.append(path);
85
                if (url.charAt(url.length()-1) != '/') {
86
                    url.append('/');
87
                }
88
                url.append("EPSG");
89
                setDatabase(url.toString());
90
            }
91
            /*
92
             * If the temporary directory do not exists or can't be created,
93
             * lets the 'database' attribute unset. If the user do not set it
94
             * explicitly (for example through JNDI), an exception will be thrown
95
             * when 'getConnection()' will be invoked.
96
             */
97
        }
98
        setUser("SA"); // System administrator. No password.
99
    }
100

    
101
    private static File getGtTmpDir() {
102
            if (tmpDir == null) {
103
                    tmpDir = new File(System.getProperty("java.io.tmpdir", "."), "Geotools-"+System.currentTimeMillis());
104
            }
105
            return tmpDir;
106
    }
107

    
108
    /**
109
     * Returns the priority for this data source. This priority is set to a lower value than
110
     * the {@linkplain AccessDataSource}'s one in order to give the priority to the Access-backed
111
     * database, if presents. Priorities are set that way because:
112
     * <ul>
113
     *   <li>The MS-Access format is the primary EPSG database format.</li>
114
     *   <li>If a user downloads the MS-Access database himself, he probably wants to use it.</li>
115
     * </ul>
116
     */
117
    public int getPriority() {
118
        return NORMAL_PRIORITY - 30;
119
    }
120

    
121
    /**
122
     * Returns {@code true} if the database contains data. This method returns {@code false}
123
     * if an empty EPSG database has been automatically created by HSQL and not yet populated.
124
     */
125
    private static boolean dataExists(final Connection connection) throws SQLException {
126
        final ResultSet tables = connection.getMetaData().getTables(
127
                null, null, "EPSG_%", new String[] {"TABLE"});
128
        final boolean exists = tables.next();
129
        tables.close();
130
        return exists;
131
    }
132

    
133
    /**
134
     * Opens a connection to the database. If the cached tables are not available,
135
     * they will be created now from the SQL scripts bundled in this plugin.
136
     */
137
    public Connection getConnection() throws SQLException {
138
        final String database = getDatabase();
139
        if (database==null || database.trim().length()==0) {
140
            /*
141
             * The 'database' attribute is unset if the constructor has been unable
142
             * to locate the temporary directory, or to create the subdirectory.
143
             */
144
            // TODO: localize
145
            throw new SQLException("Can't write to the temporary directory.");
146
        }
147
        Connection connection = super.getConnection();
148
        if (!dataExists(connection)) {
149
            /*
150
             * HSQL has created automatically an empty database. We need to populate it.
151
             * Executes the SQL scripts bundled in the JAR. In theory, each line contains
152
             * a full SQL statement. For this plugin however, we have compressed "INSERT
153
             * INTO" statements using Compactor class in this package.
154
             */
155
            Logger.getLogger("org.geotools.referencing.factory").config("Creating cached EPSG database."); // TODO: localize
156
            final Statement statement = connection.createStatement();
157
            try {
158
                final BufferedReader in = new BufferedReader(new InputStreamReader(
159
                        HSQLDataSource.class.getResourceAsStream("EPSG.sql"), "ISO-8859-1"));
160
                StringBuffer insertStatement = null;
161
                String line;
162
                while ((line=in.readLine()) != null) {
163
                    line = line.trim();
164
                    final int length = line.length();
165
                    if (length != 0) {
166
                        if (line.startsWith("INSERT INTO")) {
167
                            /*
168
                             * We are about to insert many rows into a single table.
169
                             * The row values appear in next lines; the current line
170
                             * should stop right after the VALUES keyword.
171
                             */
172
                            insertStatement = new StringBuffer(line);
173
                            continue;
174
                        }
175
                        if (insertStatement != null) {
176
                            /*
177
                             * We are about to insert a row. Prepend the "INSERT INTO"
178
                             * statement and check if we will have more rows to insert
179
                             * after this one.
180
                             */
181
                            final int values = insertStatement.length();
182
                            insertStatement.append(line);
183
                            final boolean hasMore = (line.charAt(length-1) == ',');
184
                            if (hasMore) {
185
                                insertStatement.setLength(insertStatement.length()-1);
186
                            }
187
                            line = insertStatement.toString();
188
                            insertStatement.setLength(values);
189
                            if (!hasMore) {
190
                                insertStatement = null;
191
                            }
192
                        }
193
                        statement.execute(line);
194
                    }
195
                }
196
                in.close();
197
            } catch (IOException exception) {
198
                statement.close();
199
                SQLException e = new SQLException("Can't read the SQL script."); // TODO: localize
200
                e.initCause(exception);
201
                throw e;
202
            }
203
            statement.close();
204
            connection.close();
205
            connection = super.getConnection();
206
            assert dataExists(connection);
207
        }
208
        return connection;
209
    }
210

    
211
    /**
212
     * Open a connection and creates an {@linkplain FactoryUsingSQL EPSG factory} for it.
213
     *
214
     * @param  factories The low-level factories to use for CRS creation.
215
     * @return The EPSG factory using HSQLDB SQL syntax.
216
     * @throws SQLException if connection to the database failed.
217
     */
218
    public AbstractAuthorityFactory createFactory(final FactoryGroup factories) throws SQLException {
219
        return new FactoryUsingHSQL(factories, getConnection());
220
    }
221
}