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; either
009: * version 2.1 of the License, or (at your option) any later version.
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.oracle;
017:
018: import java.io.IOException;
019: import java.sql.SQLException;
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Map;
023: import java.util.logging.Logger;
024:
025: import javax.sql.DataSource;
026:
027: import org.geotools.data.DataSourceException;
028: import org.geotools.data.DataStore;
029: import org.geotools.data.DataStoreFactorySpi;
030: import org.geotools.data.DataStoreFactorySpi.Param;
031: import org.geotools.data.jdbc.ConnectionPool;
032: import org.geotools.data.jdbc.datasource.DataSourceUtil;
033:
034: /**
035: * Creates an Oracle datastore based on a thick OCI client connection, instead
036: * of the traditional (thin) jdbc connection. The thin JDBC connection was designed
037: * for clients requiring no more classes than just the classes12.jar (or ojdbc14.jar).
038: * The OCI JDBC drivers are based on the Oracle client software and rely mostly on the
039: * very fast C/C++ based OCI (Oracle Call Interface) runtime.
040: * <p>
041: * This leads to an easy way to speed up GeoTools when gt2 is running on the same computer
042: * as the oracle install, as the OCI drivers are already there. And I believe if a computer
043: * has the OCI correctly installed and configure it can be used on remote computers, with
044: * the faster connection. This just takes more work by the admin. Server applications like
045: * GeoServer will often be on the same computer as the databse, so it makes sense to offer
046: * admins the advantage of using the OCI jdbc driver.
047: * <p>
048: * Instead of the instance, host, port requirments of the normal oracle factory this driver
049: * just requires the 'alias', which refers to values defined by the Oracle Net Configuration
050: * assistant and stored in $ORACLE_HOME/NETWORK/ADMIN/tnsnames.ora. We have also had luck
051: * on the same computer with just leaving 'alias' as an empty string, and it seems to have
052: * a reasonable default behavior.
053: *
054: * <p>
055: * This factory should be registered in the META-INF/ folder, under services/
056: * in the DataStoreFactorySpi file.
057: * </p>
058: *
059: * @author Chris Holmes, TOPP
060: * @author Bernard de Terwangne, star.be
061: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/oracle-spatial/src/main/java/org/geotools/data/oracle/OracleOCIDataStoreFactory.java $
062: */
063: public class OracleOCIDataStoreFactory implements DataStoreFactorySpi {
064: private static final String JDBC_DRIVER = "oracle.jdbc.driver.OracleDriver";
065: private static final Logger LOGGER = org.geotools.util.logging.Logging
066: .getLogger("org.geotools.data.oracle");
067:
068: /**
069: * Creates a new instance of OracleOCIDataStoreFactory
070: */
071: public OracleOCIDataStoreFactory() {
072: }
073:
074: /**
075: * Determines whether DataStore created by this factory can process the
076: * parameters.
077: *
078: * <p>
079: * Required Parameters are:
080: * </p>
081: *
082: * <ul>
083: * <li>
084: * <code>dbtype</code> - must equal "oracle"
085: * </li>
086: * <li>
087: * <code>host</code>
088: * </li>
089: * <li>
090: * <code>port</code>
091: * </li>
092: * <li>
093: * <code>user</code>
094: * </li>
095: * <li>
096: * <code>passwd</code>
097: * </li>
098: * <li>
099: * <code>instance</code>
100: * </li>
101: * </ul>
102: *
103: * <p>
104: * There are no defaults since each parameter must be explicitly defined by
105: * the user, or another DataSourceFactorySpi should be used. This
106: * behaviour is defined in the DataStoreFactorySpi contract.
107: * </p>
108: *
109: * @param params The parameter to check.
110: *
111: * @return True if all the required parameters are supplied.
112: */
113: //this should probably switch to the new way of doing things, with the PARAMS, but I want to see canProcess done in
114: //a super class, since everyone does it the same way. -ch
115: public boolean canProcess(Map params) {
116: //J-
117: return params.containsKey("dbtype")
118: && params.get("dbtype").equals("oracle")
119: && params.containsKey("alias")
120: && params.containsKey("user")
121: && params.containsKey("passwd");
122: //J+
123: }
124:
125: /**
126: * Construct a postgis data store using the params.
127: *
128: * @param params The full set of information needed to construct a live
129: * data source. Should have dbtype equal to postgis, as well as
130: * host, user, passwd, database, and table.
131: *
132: * @return The created DataSource, this may be null if the required
133: * resource was not found or if insufficent parameters were given.
134: * Note that canProcess() should have returned false if the
135: * problem is to do with insuficent parameters.
136: *
137: * @throws IOException DOCUMENT ME!
138: * @throws DataSourceException Thrown if there were any problems creating
139: * or connecting the datasource.
140: */
141: public DataStore createDataStore(Map params) throws IOException {
142:
143: /* There are no defaults here. Calling canProcess verifies that
144: * all these variables exist.
145: */
146: String alias = (String) ALIAS.lookUp(params);
147: String user = (String) USER.lookUp(params);
148: String passwd = (String) PASSWD.lookUp(params);
149: String schema = (String) SCHEMA.lookUp(params); // checks uppercase
150: String namespace = (String) NAMESPACE.lookUp(params);
151: String dbtype = (String) DBTYPE.lookUp(params);
152: Integer maxConn = (Integer) MAXCONN.lookUp(params);
153: Integer minConn = (Integer) MINCONN.lookUp(params);
154: Boolean validateConn = (Boolean) VALIDATECONN.lookUp(params);
155:
156: if (!"oracle".equals(dbtype)) {
157: throw new IOException("Parameter 'dbtype' must be oracle");
158: }
159: if (!canProcess(params)) {
160: throw new IOException(
161: "Cannot connect using provided parameters");
162: }
163:
164: boolean validate = validateConn != null
165: && validateConn.booleanValue();
166: int maxActive = maxConn != null ? maxConn.intValue() : 10;
167: int maxIdle = minConn != null ? minConn.intValue() : 4;
168: DataSource source = getDefaultDataSource(alias, user, passwd,
169: maxActive, maxIdle, validate);
170: return new OracleDataStore(source, namespace, schema,
171: new HashMap());
172: }
173:
174: public static DataSource getDefaultDataSource(String alias,
175: String user, String passwd, int maxActive, int minIdle,
176: boolean validate) throws DataSourceException {
177: String dbUrl = "jdbc:oracle:oci:@" + alias;
178: return DataSourceUtil.buildDefaultDataSource(dbUrl,
179: JDBC_DRIVER, user, passwd, maxActive, minIdle,
180: validate ? "select sysdate from dual" : null, false, 0);
181: }
182:
183: /**
184: * Oracle cannot create a new database.
185: *
186: * @param params
187: *
188: *
189: * @throws IOException DOCUMENT ME!
190: * @throws UnsupportedOperationException Cannot create new database
191: */
192: public DataStore createNewDataStore(Map params) throws IOException {
193: throw new UnsupportedOperationException(
194: "Oracle cannot create a new Database");
195: }
196:
197: public String getDisplayName() {
198: return "Oracle (OCI)";
199: }
200:
201: /**
202: * Describe the nature of the datastore constructed by this factory.
203: *
204: * @return A human readable description that is suitable for inclusion in a
205: * list of available datasources.
206: */
207: public String getDescription() {
208: return "Oracle Spatial w/ OCI (thick) connection";
209: }
210:
211: // public DataSourceMetadataEnity createMetadata( Map params ) throws IOException {
212: // String alias = (String) ALIAS.lookUp( params );
213: // String user = (String) USER.lookUp( params );
214: // String schema = (String) SCHEMA.lookUp( params ); // checks uppercase
215: // return new DataSourceMetadataEnity( alias, alias, "Connect to oracle using schema '"+schema+"' as "+user );
216: // }
217:
218: /**
219: * Returns whether the OracleDataStoreFactory would actually be able to
220: * generate a DataStore. Depends on whether the appropriate libraries are
221: * on the classpath. For now just checks for the presence of the JDBC
222: * driver, should probably check for SDOAPI as well.
223: *
224: * @return True if the classes to make an oracle connection are present.
225: *
226: * @task Figure out a class to check the SDOAPI for, and check it.
227: */
228: public boolean isAvailable() {
229: try {
230: Class.forName(JDBC_DRIVER);
231: } catch (ClassNotFoundException cnfe) {
232: return false;
233: }
234:
235: return true;
236: }
237:
238: static final Param DBTYPE = new Param("dbtype", String.class,
239: "This must be 'oracle'.", true, "oracle");
240: static final Param ALIAS = new Param(
241: "alias",
242: String.class,
243: "The alias to the oracle server, as defined in the tnsnames.ora file",
244: true);
245: static final Param PORT = new Param("port", String.class,
246: "The port oracle is running on. " + "(Default is 1521)",
247: true, "1521");
248: static final Param USER = new Param("user", String.class,
249: "The user name to log in with.", true);
250: static final Param PASSWD = new Param("passwd", String.class,
251: "The password.", true);
252: static final Param INSTANCE = new Param("instance", String.class,
253: "The name of the Oracle instance to connect to.", true);
254: public static final Param MAXCONN = new Param("max connections",
255: Integer.class, "maximum number of open connections", false,
256: new Integer(10));
257: public static final Param MINCONN = new Param("min connections",
258: Integer.class, "minimum number of pooled connection",
259: false, new Integer(4));
260: public static final Param VALIDATECONN = new Param(
261: "validate connections", Boolean.class,
262: "check connection is alive before using it", false,
263: Boolean.FALSE);
264: /** Apparently Schema must be uppercase */
265: static final Param SCHEMA = new Param(
266: "schema",
267: String.class,
268: "The schema name to narrow down the exposed tables (must be upper case).",
269: false) {
270:
271: public Object lookUp(Map map) throws IOException {
272: if (!map.containsKey(key)) {
273: if (required) {
274: throw new IOException("Parameter " + key
275: + " is required:" + description);
276: } else {
277: return null;
278: }
279: }
280: Object value = map.get(key);
281: if (value == null) {
282: return null;
283: }
284: if (value instanceof String) {
285: String text = (String) value;
286: if (text == null) {
287: return null;
288: } else if (text.equals(text.toUpperCase())) {
289: return text;
290: } else {
291: throw new IOException(
292: "Schema must be supplied in uppercase");
293: }
294: } else {
295: throw new IOException("String required for parameter "
296: + key + ": not " + value.getClass().getName());
297: }
298: }
299: };
300: static final Param NAMESPACE = new Param("namespace", String.class,
301: "The namespace to give the DataStore", false);
302:
303: /**
304: * Describe parameters.
305: *
306: * @see org.geotools.data.DataStoreFactorySpi#getParametersInfo()
307: */
308: public Param[] getParametersInfo() {
309: return new Param[] { DBTYPE, ALIAS, USER, PASSWD, SCHEMA,
310: NAMESPACE };
311: }
312:
313: /**
314: * Returns the implementation hints. The default implementation returns en empty map.
315: */
316: public Map getImplementationHints() {
317: return Collections.EMPTY_MAP;
318: }
319: }
|