001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: */
016: package org.geotools.data.postgis;
017:
018: import java.io.IOException;
019: import java.sql.SQLException;
020: import java.util.Map;
021:
022: import javax.sql.DataSource;
023:
024: import org.geotools.data.AbstractDataStoreFactory;
025: import org.geotools.data.DataSourceException;
026: import org.geotools.data.DataStore;
027: import org.geotools.data.jdbc.ConnectionPool;
028: import org.geotools.data.jdbc.datasource.DataSourceUtil;
029: import org.geotools.data.jdbc.datasource.ManageableDataSource;
030:
031: /**
032: * Creates a PostgisDataStore baed on the correct params.
033: *
034: * <p>
035: * This factory should be registered in the META-INF/ folder, under services/
036: * in the DataStoreFactorySpi file.
037: * </p>
038: *
039: * @author Jody Garnett, Refractions Research
040: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/postgis/src/main/java/org/geotools/data/postgis/PostgisDataStoreFactory.java $
041: */
042: public class PostgisDataStoreFactory extends AbstractDataStoreFactory
043: implements org.geotools.data.DataStoreFactorySpi {
044: /** Creates PostGIS-specific JDBC driver class. */
045: private static final String DRIVER_CLASS = "org.postgresql.Driver";
046:
047: public static final Param DBTYPE = new Param("dbtype",
048: String.class, "must be 'postgis'", true, "postgis");
049:
050: public static final Param HOST = new Param("host", String.class,
051: "postgis host machine", true, "localhost");
052:
053: public static final Param PORT = new Param("port", Integer.class,
054: "postgis connection port (default is 5432)", true,
055: new Integer(5432));
056:
057: public static final Param DATABASE = new Param("database",
058: String.class, "postgis database");
059:
060: public static final Param SCHEMA = new Param("schema",
061: String.class, "postgis schema", false, "public");
062:
063: public static final Param USER = new Param("user", String.class,
064: "user name to login as");
065:
066: public static final Param PASSWD = new Param("passwd",
067: String.class, "password used to login", false);
068:
069: public static final Param MAXCONN = new Param("max connections",
070: Integer.class, "maximum number of open connections", false,
071: new Integer(10));
072:
073: public static final Param MINCONN = new Param("min connections",
074: Integer.class, "minimum number of pooled connection",
075: false, new Integer(4));
076:
077: public static final Param VALIDATECONN = new Param(
078: "validate connections", Boolean.class,
079: "check connection is alive before using it", false,
080: Boolean.FALSE);
081:
082: public static final Param NAMESPACE = new Param("namespace",
083: String.class, "namespace prefix used", false);
084:
085: public static final Param WKBENABLED = new Param("wkb enabled",
086: Boolean.class,
087: "set to true if Well Known Binary should be used to read PostGIS "
088: + "data (experimental)", false, new Boolean(true));
089:
090: public static final Param LOOSEBBOX = new Param("loose bbox",
091: Boolean.class,
092: "set to true if the Bounding Box should be 'loose', faster but "
093: + "not as deadly accurate", false,
094: new Boolean(true));
095:
096: public static final Param ESTIMATEDEXTENT = new Param(
097: "estimated extent",
098: Boolean.class,
099: "set to true if the bounds for a table should be computed using the "
100: + "'estimated_extent' function, but beware that this function is less accurate, "
101: + "and in some cases *far* less accurate if the data within the actual bounds "
102: + "does not follow a uniform distribution. It also relies on the fact that you have"
103: + "accurate table stats available. So it is a good idea to 'VACUUM ANALYZE' "
104: + "the postgis table.", false, new Boolean(false));
105:
106: /**
107: * Creates a new instance of PostgisDataStoreFactory
108: */
109: public PostgisDataStoreFactory() {
110: }
111:
112: /**
113: * Checks to see if all the postgis params are there.
114: *
115: * <p>
116: * Should have:
117: * </p>
118: *
119: * <ul>
120: * <li>
121: * dbtype: equal to postgis
122: * </li>
123: * <li>
124: * host
125: * </li>
126: * <li>
127: * user
128: * </li>
129: * <li>
130: * passwd
131: * </li>
132: * <li>
133: * database
134: * </li>
135: * <li>
136: * charset
137: * </li>
138: * </ul>
139: *
140: *
141: * @param params Set of parameters needed for a postgis data store.
142: *
143: * @return <code>true</code> if dbtype equals postgis, and contains keys
144: * for host, user, passwd, and database.
145: */
146: public boolean canProcess(Map params) {
147: if (!super .canProcess(params)) {
148: return false; // was not in agreement with getParametersInfo
149: }
150: if (!(((String) params.get("dbtype"))
151: .equalsIgnoreCase("postgis"))) {
152: return (false);
153: } else {
154: return (true);
155: }
156: }
157:
158: /**
159: * Construct a postgis data store using the params.
160: *
161: * @param params The full set of information needed to construct a live
162: * data source. Should have dbtype equal to postgis, as well as
163: * host, user, passwd, database, and table.
164: *
165: * @return The created DataSource, this may be null if the required
166: * resource was not found or if insufficent parameters were given.
167: * Note that canProcess() should have returned false if the
168: * problem is to do with insuficent parameters.
169: *
170: * @throws IOException See DataSourceException
171: * @throws DataSourceException Thrown if there were any problems creating
172: * or connecting the datasource.
173: */
174: public DataStore createDataStore(Map params) throws IOException {
175: // lookup will throw error message for
176: // miscoversion or lack of required param
177: //
178: String host = (String) HOST.lookUp(params);
179: String user = (String) USER.lookUp(params);
180: String passwd = (String) PASSWD.lookUp(params);
181: Integer port = (Integer) PORT.lookUp(params);
182: Integer maxConn = (Integer) MAXCONN.lookUp(params);
183: Integer minConn = (Integer) MINCONN.lookUp(params);
184: Boolean validateConn = (Boolean) VALIDATECONN.lookUp(params);
185: String schema = (String) SCHEMA.lookUp(params);
186: String database = (String) DATABASE.lookUp(params);
187: Boolean wkb_enabled = (Boolean) WKBENABLED.lookUp(params);
188: Boolean is_loose_bbox = (Boolean) LOOSEBBOX.lookUp(params);
189: Boolean is_estimated_extent = (Boolean) ESTIMATEDEXTENT
190: .lookUp(params);
191: String namespace = (String) NAMESPACE.lookUp(params);
192:
193: // Try processing params first so we can get real IO
194: // error message back to the user
195: //
196: if (!canProcess(params)) {
197: throw new IOException("The parameters map isn't correct!!");
198: }
199:
200: boolean validate = validateConn != null
201: && validateConn.booleanValue();
202: int maxActive = maxConn != null ? maxConn.intValue() : 10;
203: int maxIdle = minConn != null ? minConn.intValue() : 4;
204: DataSource source = getDefaultDataSource(host, user, passwd,
205: port.intValue(), database, maxActive, maxIdle, validate);
206:
207: PostgisDataStore dataStore = createDataStoreInternal(source,
208: namespace, schema);
209:
210: if (wkb_enabled != null) {
211: dataStore.setWKBEnabled(wkb_enabled.booleanValue());
212: }
213:
214: if (is_loose_bbox != null) {
215: dataStore.setLooseBbox(is_loose_bbox.booleanValue());
216: }
217:
218: if (is_estimated_extent != null) {
219: //ensure optimize mode set to OPTIMIZE_SQL
220: dataStore.setOptimizeMode(PostgisDataStore.OPTIMIZE_SQL);
221: dataStore.setEstimatedExtent(is_estimated_extent
222: .booleanValue());
223: }
224: return dataStore;
225: }
226:
227: public static ManageableDataSource getDefaultDataSource(
228: String host, String user, String passwd, int port,
229: String database, int maxActive, int minIdle,
230: boolean validate) throws DataSourceException {
231: String url = "jdbc:postgresql" + "://" + host + ":" + port
232: + "/" + database;
233: String driver = "org.postgresql.Driver";
234: return DataSourceUtil.buildDefaultDataSource(url, driver, user,
235: passwd, maxActive, minIdle, validate ? "select now()"
236: : null, false, 0);
237: }
238:
239: protected PostgisDataStore createDataStoreInternal(
240: DataSource dataSource, String namespace, String schema)
241: throws IOException {
242:
243: if (schema == null && namespace == null)
244: return new PostgisDataStore(dataSource);
245:
246: if (schema == null && namespace != null) {
247: return new PostgisDataStore(dataSource, namespace);
248: }
249:
250: return new PostgisDataStore(dataSource, schema, namespace);
251: }
252:
253: /**
254: * Postgis cannot create a new database.
255: *
256: * @param params
257: *
258: *
259: * @throws IOException See UnsupportedOperationException
260: * @throws UnsupportedOperationException Cannot create new database
261: */
262: public DataStore createNewDataStore(Map params) throws IOException {
263: throw new UnsupportedOperationException(
264: "Postgis cannot create a new Database");
265: }
266:
267: public String getDisplayName() {
268: return "Postgis";
269: }
270:
271: /**
272: * Describe the nature of the datasource constructed by this factory.
273: *
274: * @return A human readable description that is suitable for inclusion in a
275: * list of available datasources.
276: */
277: public String getDescription() {
278: return "PostGIS spatial database";
279: }
280:
281: /**
282: * Determines if the appropriate libraries are present for this datastore
283: * factory to successfully produce postgis datastores.
284: *
285: * @return <tt>true</tt> if the postgresql jar is on the classpath.
286: */
287: public boolean isAvailable() {
288: try {
289: Class.forName(DRIVER_CLASS);
290: } catch (ClassNotFoundException cnfe) {
291: return false;
292: }
293: return true;
294: }
295:
296: /**
297: * Describe parameters.
298: *
299: *
300: * @see org.geotools.data.DataStoreFactorySpi#getParametersInfo()
301: */
302: public Param[] getParametersInfo() {
303: return new Param[] { DBTYPE, HOST, PORT, SCHEMA, DATABASE,
304: USER, PASSWD, MAXCONN, MINCONN, VALIDATECONN,
305: WKBENABLED, LOOSEBBOX, ESTIMATEDEXTENT, NAMESPACE };
306: }
307: }
|