Factored out BaseDataConnector class
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / attrresolv / provider / JDBCDataConnector.java
1 /*
2  * Copyright (c) 2003 National Research Council of Canada
3  *
4  * Permission is hereby granted, free of charge, to any person 
5  * obtaining a copy of this software and associated documentation 
6  * files (the "Software"), to deal in the Software without 
7  * restriction, including without limitation the rights to use, 
8  * copy, modify, merge, publish, distribute, sublicense, and/or 
9  * sell copies of the Software, and to permit persons to whom the 
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be 
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
17  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
19  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
20  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
22  * OTHER DEALINGS IN THE SOFTWARE.
23  *
24  */
25
26 package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
27
28 import java.io.PrintWriter;
29 import java.lang.reflect.Constructor;
30 import java.security.Principal;
31 import java.sql.Blob;
32 import java.sql.Clob;
33 import java.sql.Connection;
34 import java.sql.PreparedStatement;
35 import java.sql.ResultSet;
36 import java.sql.ResultSetMetaData;
37 import java.sql.SQLException;
38 import java.sql.Types;
39 import java.text.SimpleDateFormat;
40 import java.util.ArrayList;
41 import java.util.Iterator;
42 import java.util.Properties;
43
44 import javax.naming.NamingException;
45 import javax.naming.directory.Attribute;
46 import javax.naming.directory.Attributes;
47 import javax.naming.directory.BasicAttribute;
48 import javax.naming.directory.BasicAttributes;
49 import javax.sql.DataSource;
50
51 import org.apache.commons.dbcp.ConnectionFactory;
52 import org.apache.commons.dbcp.DriverManagerConnectionFactory;
53 import org.apache.commons.dbcp.PoolableConnectionFactory;
54 import org.apache.commons.dbcp.PoolingDataSource;
55 import org.apache.commons.pool.impl.GenericObjectPool;
56 import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory;
57 import org.apache.log4j.Logger;
58 import org.apache.log4j.Priority;
59 import org.w3c.dom.Element;
60 import org.w3c.dom.Node;
61 import org.w3c.dom.NodeList;
62
63 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeResolver;
64 import edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn;
65 import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
66 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
67 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
68
69 /*
70  * Built at the Canada Institute for Scientific and Technical Information (CISTI 
71  * <ahref="http://www.cisti-icist.nrc-cnrc.gc.ca/">http://www.cisti-icist.nrc-cnrc.gc.ca/</a>, 
72  * the National Research Council Canada 
73  * (NRC <a href="http://www.nrc-cnrc.gc.ca/">http://www.nrc-cnrc.gc.ca/</a>)
74  * by David Dearman, COOP student from Dalhousie University,
75  * under the direction of Glen Newton, Head research (IT)
76  * <ahref="mailto:glen.newton@nrc-cnrc.gc.ca">glen.newton@nrc-cnrc.gc.ca</a>. 
77  */
78
79 /**
80  * Data Connector that uses JDBC to access user attributes stored in databases.
81  *
82  * @author David Dearman (dearman@cs.dal.ca)
83  * @author Walter Hoehn (wassa@columbia.edu)
84  * @author Scott Cantor
85  */
86
87 public class JDBCDataConnector extends BaseDataConnector implements DataConnectorPlugIn {
88
89         private static Logger log = Logger.getLogger(JDBCDataConnector.class.getName());
90         protected String searchVal;
91         protected DataSource dataSource;
92         protected JDBCAttributeExtractor extractor;
93         protected JDBCStatementCreator statementCreator;
94
95         public JDBCDataConnector(Element e) throws ResolutionPlugInException {
96
97                 super(e);
98
99                 //Get the query string
100                 NodeList queryNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Query");
101                 Node tnode = queryNodes.item(0).getFirstChild();
102                 if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
103                         searchVal = tnode.getNodeValue();
104                 }
105                 if (searchVal == null || searchVal.equals("")) {
106                         log.error("Database query must be specified.");
107                         throw new ResolutionPlugInException("Database query must be specified.");
108                 }
109
110                 //Load the supplied JDBC driver
111                 String dbDriverName = e.getAttribute("dbDriver");
112                 if (dbDriverName != null && (!dbDriverName.equals(""))) {
113                         loadDriver(dbDriverName);
114                 }
115
116                 //Load site-specific implementation classes     
117                 setupAttributeExtractor(
118                         (Element) e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "AttributeExtractor").item(
119                                 0));
120                 setupStatementCreator(
121                         (Element) e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "StatementCreator").item(0));
122         
123         //Load driver properties
124         Properties props = new Properties();
125         NodeList propertiesNode = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Property");
126         for (int i = 0; propertiesNode.getLength() > i; i++) {
127             Element property = (Element) propertiesNode.item(i);
128             String propertiesName = property.getAttribute("name");
129             String propertiesValue = property.getAttribute("value");
130
131             if (propertiesName != null
132                 && !propertiesName.equals("")
133                 && propertiesValue != null
134                 && !propertiesValue.equals("")) {
135                 props.setProperty(propertiesName, propertiesValue);
136                 log.debug("Property: (" + propertiesName + ")");
137                 log.debug("   Value: (" + propertiesValue + ")");
138             } else {
139                 log.error("Property is malformed.");
140                 throw new ResolutionPlugInException("Property is malformed.");
141             }
142         }
143         
144                 //Initialize a pooling Data Source
145                 int maxActive = 0;
146                 int maxIdle = 0;
147                 try {
148                         if (e.getAttributeNode("maxActive") != null) {
149                                 maxActive = Integer.parseInt(e.getAttribute("maxActive"));
150                         }
151                         if (e.getAttributeNode("maxIdle") != null) {
152                                 maxIdle = Integer.parseInt(e.getAttribute("maxIdle"));
153                         }
154                 } catch (NumberFormatException ex) {
155                         log.error("Malformed pooling limits: using defaults.");
156                 }
157                 if (e.getAttribute("dbURL") == null || e.getAttribute("dbURL").equals("")) {
158                         log.error("JDBC connection requires a dbURL property");
159                         throw new ResolutionPlugInException("JDBCDataConnection requires a \"dbURL\" property");
160                 }
161                 setupDataSource(e.getAttribute("dbURL"), props, maxActive, maxIdle);
162         }
163
164         /**
165          * Initialize a Pooling Data Source
166          */
167         private void setupDataSource(String dbURL, Properties props, int maxActive, int maxIdle) throws ResolutionPlugInException {
168
169                 GenericObjectPool objectPool = new GenericObjectPool(null);
170
171                 if (maxActive > 0) {
172                         objectPool.setMaxActive(maxActive);
173                 }
174                 if (maxIdle > 0) {
175                         objectPool.setMaxIdle(maxIdle);
176                 }
177
178                 objectPool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
179
180                 ConnectionFactory connFactory = null;
181                 PoolableConnectionFactory poolConnFactory = null;
182
183                 try {
184                         connFactory = new DriverManagerConnectionFactory(dbURL, props);
185                         log.debug("Connection factory initialized.");
186                 } catch (Exception ex) {
187                         log.error(
188                                 "Connection factory couldn't be initialized, ensure database URL, username and password are correct.");
189                         throw new ResolutionPlugInException("Connection factory couldn't be initialized: " + ex.getMessage());
190                 }
191
192                 try {
193                         poolConnFactory =
194                         new PoolableConnectionFactory(
195                                 connFactory,
196                                 objectPool,
197                                 new StackKeyedObjectPoolFactory(),
198                                 null,
199                                 false,
200                                         true);
201                 } catch (Exception ex) {
202                         log.debug("Poolable connection factory error");
203                 }
204
205                 dataSource = new PoolingDataSource(objectPool);
206                 log.info("Data Source initialized.");
207                 try {
208                         dataSource.setLogWriter(
209                                 new Log4jPrintWriter(Logger.getLogger(JDBCDataConnector.class.getName() + ".Pool"), Priority.DEBUG));
210                 } catch (SQLException e) {
211                         log.error("Coudn't setup logger for database connection pool.");
212                 }
213         }
214
215         /**
216          * Instantiate an Attribute Extractor, using the default if none was configured
217          */
218         private void setupAttributeExtractor(Element config) throws ResolutionPlugInException {
219
220                 String className = null;
221                 if (config != null) {
222                         className = config.getAttribute("class");
223                 }
224                 if (className == null || className.equals("")) {
225                         log.debug("Using default Attribute Extractor.");
226                         className = DefaultAE.class.getName();
227                 }
228                 try {
229                         Class aeClass = Class.forName(className);
230                         extractor = (JDBCAttributeExtractor) aeClass.newInstance();
231                         log.debug("Attribute Extractor implementation loaded.");
232
233                 } catch (ClassNotFoundException e) {
234                         log.error("The supplied Attribute Extractor class could not be found: " + e);
235                         throw new ResolutionPlugInException(
236                                 "The supplied Attribute Extractor class could not be found: " + e.getMessage());
237                 } catch (Exception e) {
238                         log.error("Unable to instantiate Attribute Extractor implementation: " + e);
239                         throw new ResolutionPlugInException(
240                                 "Unable to instantiate Attribute Extractor implementation: " + e.getMessage());
241                 }
242         }
243
244         /**
245          * Instantiate a Statement Creator, using the default if none was configured
246          */
247         private void setupStatementCreator(Element config) throws ResolutionPlugInException {
248
249                 String scClassName = null;
250                 if (config != null) {
251                         scClassName = config.getAttribute("class");
252                 }
253                 if (scClassName == null || scClassName.equals("")) {
254                         log.debug("Using default Statement Creator.");
255                         scClassName = DefaultStatementCreator.class.getName();
256                 }
257                 try {
258                         Class scClass = Class.forName(scClassName);
259
260                         Class[] params = new Class[1];
261                         params[0] = Class.forName("org.w3c.dom.Element");
262                         try {
263                                 Constructor implementorConstructor = scClass.getConstructor(params);
264                                 Object[] args = new Object[1];
265                                 args[0] = config;
266                                 log.debug("Initializing Statement Creator of type (" + scClass.getName() + ").");
267                                 statementCreator = (JDBCStatementCreator) implementorConstructor.newInstance(args);
268                         } catch (NoSuchMethodException nsme) {
269                                 log.debug(
270                                         "Implementation constructor does have a parameterized constructor, attempting to load default.");
271                                 statementCreator = (JDBCStatementCreator) scClass.newInstance();
272                         }
273                         log.debug("Statement Creator implementation loaded.");
274
275                 } catch (ClassNotFoundException e) {
276                         log.error("The supplied Statement Creator class could not be found: " + e);
277                         throw new ResolutionPlugInException(
278                                 "The supplied Statement Creator class could not be found: " + e.getMessage());
279                 } catch (Exception e) {
280                         log.error("Unable to instantiate Statement Creator implementation: " + e);
281                         throw new ResolutionPlugInException(
282                                 "Unable to instantiate Statement Creator implementation: " + e.getMessage());
283                 }
284         }
285
286         public Attributes resolve(Principal principal, String requester, Dependencies depends)
287                 throws ResolutionPlugInException {
288
289                 log.debug("Resolving connector: (" + getId() + ")");
290
291                 //Retrieve a connection from the connection pool
292                 Connection conn = null;
293                 try {
294                         conn = dataSource.getConnection();
295                         log.debug("Connection retrieved from pool");
296                 } catch (Exception e) {
297                         log.error("Unable to fetch a connection from the pool");
298                         throw new ResolutionPlugInException("Unable to fetch a connection from the pool: " + e.getMessage());
299                 }
300                 if (conn == null) {
301                         log.error("Pool didn't return a propertly initialized connection.");
302                         throw new ResolutionPlugInException("Pool didn't return a properly initialized connection.");
303                 }
304
305                 //Setup and execute a (pooled) prepared statement
306                 ResultSet rs = null;
307                 PreparedStatement preparedStatement;
308                 try {
309                         preparedStatement = conn.prepareStatement(searchVal);
310                         statementCreator.create(preparedStatement, principal, requester, depends);
311                         rs = preparedStatement.executeQuery();
312                         if (!rs.next()) {
313                                 return new BasicAttributes();
314                         }
315                 } catch (JDBCStatementCreatorException e) {
316                         log.error("An ERROR occured while constructing the query");
317                         throw new ResolutionPlugInException("An ERROR occured while constructing the query: " + e.getMessage());
318                 } catch (SQLException e) {
319                         log.error("An ERROR occured while executing the query");
320                         throw new ResolutionPlugInException("An ERROR occured while executing the query: " + e.getMessage());
321                 }
322
323                 //Extract attributes from the ResultSet
324                 try {
325                         return extractor.extractAttributes(rs);
326
327                 } catch (JDBCAttributeExtractorException e) {
328                         log.error("An ERROR occured while extracting attributes from result set");
329                         throw new ResolutionPlugInException(
330                                 "An ERROR occured while extracting attributes from result set: " + e.getMessage());
331                 } finally {
332                         try {
333                                 if (preparedStatement != null) {
334                                         preparedStatement.close();
335                                 }
336                         } catch (SQLException e) {
337                                 log.error("An error occured while closing the prepared statement: " + e);
338                                 throw new ResolutionPlugInException("An error occured while closing the prepared statement: " + e);
339                         }
340                         try {
341                                 rs.close();
342                         } catch (SQLException e) {
343                                 log.error("An error occured while closing the result set: " + e);
344                                 throw new ResolutionPlugInException("An error occured while closing the result set: " + e);
345                         }
346
347                         try {
348                                 conn.close();
349                         } catch (SQLException e) {
350                                 log.error("An error occured while closing the database connection: " + e);
351                                 throw new ResolutionPlugInException("An error occured while closing the database connection: " + e);
352                         }
353                 }
354         }
355
356         /** 
357          * Loads the driver used to access the database
358          * @param driver The driver used to access the database
359          * @throws ResolutionPlugInException If there is a failure to load the driver
360          */
361         public void loadDriver(String driver) throws ResolutionPlugInException {
362                 try {
363                         Class.forName(driver).newInstance();
364                         log.debug("Loading JDBC driver: " + driver);
365                 } catch (Exception e) {
366                         log.error("An error loading database driver: " + e);
367                         throw new ResolutionPlugInException(
368                                 "An IllegalAccessException occured while loading database driver: " + e.getMessage());
369                 }
370                 log.debug("Driver loaded.");
371         }
372
373         private class Log4jPrintWriter extends PrintWriter {
374
375                 private Priority level;
376                 private Logger logger;
377                 private StringBuffer text = new StringBuffer("");
378
379                 private Log4jPrintWriter(Logger logger, org.apache.log4j.Priority level) {
380                         super(System.err);
381                         this.level = level;
382                         this.logger = logger;
383                 }
384
385                 public void close() {
386                         flush();
387                 }
388
389                 public void flush() {
390                         if (!text.toString().equals("")) {
391                                 logger.log(level, text.toString());
392                                 text.setLength(0);
393                         }
394                 }
395
396                 public void print(boolean b) {
397                         text.append(b);
398                 }
399
400                 public void print(char c) {
401                         text.append(c);
402                 }
403
404                 public void print(char[] s) {
405                         text.append(s);
406                 }
407
408                 public void print(double d) {
409                         text.append(d);
410                 }
411
412                 public void print(float f) {
413                         text.append(f);
414                 }
415
416                 public void print(int i) {
417                         text.append(i);
418                 }
419
420                 public void print(long l) {
421                         text.append(l);
422                 }
423
424                 public void print(Object obj) {
425                         text.append(obj);
426                 }
427
428                 public void print(String s) {
429                         text.append(s);
430                 }
431
432                 public void println() {
433                         if (!text.toString().equals("")) {
434                                 logger.log(level, text.toString());
435                                 text.setLength(0);
436                         }
437                 }
438
439                 public void println(boolean x) {
440                         text.append(x);
441                         logger.log(level, text.toString());
442                         text.setLength(0);
443                 }
444
445                 public void println(char x) {
446                         text.append(x);
447                         logger.log(level, text.toString());
448                         text.setLength(0);
449                 }
450
451                 public void println(char[] x) {
452                         text.append(x);
453                         logger.log(level, text.toString());
454                         text.setLength(0);
455                 }
456
457                 public void println(double x) {
458                         text.append(x);
459                         logger.log(level, text.toString());
460                         text.setLength(0);
461                 }
462
463                 public void println(float x) {
464                         text.append(x);
465                         logger.log(level, text.toString());
466                         text.setLength(0);
467                 }
468
469                 public void println(int x) {
470                         text.append(x);
471                         logger.log(level, text.toString());
472                         text.setLength(0);
473                 }
474
475                 public void println(long x) {
476                         text.append(x);
477                         logger.log(level, text.toString());
478                         text.setLength(0);
479                 }
480
481                 public void println(Object x) {
482                         text.append(x);
483                         logger.log(level, text.toString());
484                         text.setLength(0);
485                 }
486
487                 public void println(String x) {
488                         text.append(x);
489                         logger.log(level, text.toString());
490                         text.setLength(0);
491                 }
492         }
493 }
494
495 /**
496  * The default attribute extractor. 
497  */
498 class DefaultAE implements JDBCAttributeExtractor {
499
500         private static Logger log = Logger.getLogger(DefaultAE.class.getName());
501
502         /**
503          * Method of extracting the attributes from the supplied result set.
504          *
505          * @param ResultSet The result set from the query which contains the attributes
506          * @return BasicAttributes as objects containing all the attributes
507          * @throws JDBCAttributeExtractorException If there is a complication in retrieving the attributes
508          */
509         public BasicAttributes extractAttributes(ResultSet rs) throws JDBCAttributeExtractorException {
510                 BasicAttributes attributes = new BasicAttributes();
511         int row = 0;
512         
513                 try {
514             // Get metadata about result set.
515             ResultSetMetaData rsmd = rs.getMetaData();
516             int numColumns = rsmd.getColumnCount();
517             log.debug("Number of returned columns: " + numColumns);
518
519             do {
520                 for (int i = 1; i <= numColumns; i++) {
521                     String columnName = rsmd.getColumnName(i);
522                     Object columnValue = rs.getObject(columnName);
523                     if (log.isDebugEnabled()) {
524                         log.debug(
525                             "("
526                                 + i
527                                 + ". ColumnType = " + rsmd.getColumnTypeName(i)
528                                 + ") "
529                                 + columnName
530                                 + " -> "
531                                 + (columnValue != null ? columnValue.toString() : "(null)"));
532                     }
533                     if (row == 0) {
534                         BasicAttribute ba = new BasicAttribute(columnName, true);
535                         ba.add(row,columnValue);
536                         attributes.put(ba);
537                     }
538                     else {
539                         attributes.get(columnName).add(row,columnValue);
540                     }
541                 }
542                 row++;
543             } while (rs.next());
544                 } catch (SQLException e) {
545                         log.error("An ERROR occured while processing result set");
546                         throw new JDBCAttributeExtractorException(
547                                 "An ERROR occured while processing result set: " + e.getMessage());
548                 }
549
550                 return attributes;
551         }
552 }
553
554 class DefaultStatementCreator implements JDBCStatementCreator {
555
556         private static Logger log = Logger.getLogger(DefaultStatementCreator.class.getName());
557
558         public void create(
559                 PreparedStatement preparedStatement,
560                 Principal principal,
561                 String requester,
562                 Dependencies depends)
563                 throws JDBCStatementCreatorException {
564
565                 try {
566                         log.debug("Creating prepared statement.  Substituting principal: (" + principal.getName() + ")");
567                         preparedStatement.setString(1, principal.getName());
568             //Tried using ParameterMetaData to determine param count, but it fails, so...
569             try {
570                 int i=2;
571                 while (true) {
572                     preparedStatement.setString(i++, principal.getName());
573                 }
574             } catch (SQLException e) {
575                 //Ignore any additional exceptions, assume parameters simply don't exist.
576             }
577                 } catch (SQLException e) {
578                         log.error("Encountered an error while creating prepared statement: " + e);
579                         throw new JDBCStatementCreatorException(
580                                 "Encountered an error while creating prepared statement: " + e.getMessage());
581                 }
582         }
583 }
584
585 class DependencyStatementCreator implements JDBCStatementCreator {
586
587         private static Logger log = Logger.getLogger(DependencyStatementCreator.class.getName());
588         private ArrayList parameters = new ArrayList();
589
590         public DependencyStatementCreator(Element conf) throws JDBCStatementCreatorException {
591
592                 NodeList nodes = conf.getElementsByTagName("Parameter");
593                 for (int i = 0; i < nodes.getLength(); i++) {
594                         Element parameter = (Element) nodes.item(i);
595                         String type = "String";
596                         if (parameter.getAttribute("type") != null && (!parameter.getAttribute("type").equals(""))) {
597                                 type = parameter.getAttribute("type");
598                         }
599
600                         if (parameter.getAttribute("attributeName") == null
601                                 || parameter.getAttribute("attributeName").equals("")) {
602                                 log.error("Statement Creator Parameter must reference an attribute by name.");
603                                 throw new JDBCStatementCreatorException("Statement Creator Parameter must reference an attribute by name.");
604                         }
605
606                         if (parameter.getAttribute("connectorId") != null && (!parameter.getAttribute("connectorId").equals(""))) {
607                                 parameters.add(
608                                         new Parameter(
609                                                 type,
610                                                 parameter.getAttribute("attributeName"),
611                                                 parameter.getAttribute("connectorId")));
612                         } else {
613                                 parameters.add(new Parameter(type, parameter.getAttribute("attributeName")));
614
615                         }
616
617                         if (parameter.getAttribute("nullMissing") != null && (!parameter.getAttribute("nullMissing").equals(""))) {
618                                 if (parameter.getAttribute("nullMissing").equalsIgnoreCase("FALSE")) {
619                                         ((Parameter) parameters.get(i)).setNullMissing(false);
620                                 }
621                         }
622                 }
623                 log.debug("Parameters configured: " + parameters.size());
624         }
625
626         public void create(
627                 PreparedStatement preparedStatement,
628                 Principal principal,
629                 String requester,
630                 Dependencies depends)
631                 throws JDBCStatementCreatorException {
632
633                 try {
634                         log.debug("Creating prepared statement.  Substituting values from dependencies.");
635                         for (int i = 0; i < parameters.size(); i++) {
636                                 ((Parameter) parameters.get(i)).setParameterValue(preparedStatement, i + 1, depends);
637                         }
638
639                 } catch (Exception e) {
640                         log.error("Encountered an error while creating prepared statement: " + e);
641                         throw new JDBCStatementCreatorException(
642                                 "Encountered an error while creating prepared statement: " + e.getMessage());
643                 }
644         }
645
646         protected class Parameter {
647                 private String type;
648                 private String attributeName;
649                 private boolean referencesConnector = false;
650                 private String connectorId;
651                 private boolean nullMissing = true;
652
653                 protected Parameter(String type, String attributeName) throws JDBCStatementCreatorException {
654                         if ((!type.equalsIgnoreCase("String"))
655                                 && (!type.equalsIgnoreCase("Integer"))
656                                 && (!type.equalsIgnoreCase("Byte"))
657                                 && (!type.equalsIgnoreCase("Double"))
658                                 && (!type.equalsIgnoreCase("Float"))
659                                 && (!type.equalsIgnoreCase("Long"))
660                                 && (!type.equalsIgnoreCase("Short"))
661                                 && (!type.equalsIgnoreCase("Boolean"))
662                                 && (!type.equalsIgnoreCase("Date"))
663                                 && (!type.equalsIgnoreCase("Blob"))
664                                 && (!type.equalsIgnoreCase("Clob"))) {
665                                 log.error("Unsupported type configured for Statement Creator Parameter.");
666                                 throw new JDBCStatementCreatorException("Unsupported type on Statement Creator Parameter.");
667                         }
668                         this.type = type;
669                         this.attributeName = attributeName;
670                 }
671
672                 protected Parameter(String type, String attributeName, String connectorId)
673                         throws JDBCStatementCreatorException {
674                         this(type, attributeName);
675                         referencesConnector = true;
676                         this.connectorId = connectorId;
677
678                 }
679         
680         protected int getSQLType() {
681             if (type.equalsIgnoreCase("String")) {
682                 return Types.VARCHAR;
683             } else if (type.equalsIgnoreCase("Integer")) {
684                 return Types.INTEGER;
685             } else if (type.equalsIgnoreCase("Byte")) {
686                 return Types.TINYINT;
687             } else if (type.equalsIgnoreCase("Double")) {
688                 return Types.DOUBLE;
689             } else if (type.equalsIgnoreCase("Float")) {
690                 return Types.FLOAT;
691             } else if (type.equalsIgnoreCase("Long")) {
692                 return Types.INTEGER;
693             } else if (type.equalsIgnoreCase("Short")) {
694                 return Types.SMALLINT;
695             } else if (type.equalsIgnoreCase("Boolean")) {
696                 return Types.BOOLEAN;
697             } else if (type.equalsIgnoreCase("Date")) {
698                 return Types.DATE;
699             } else if (type.equalsIgnoreCase("Blob")) {
700                 return Types.BLOB;
701             } else if (type.equalsIgnoreCase("Clob")) {
702                 return Types.CLOB;
703             } else {
704                 return Types.VARCHAR;
705             }
706         }
707
708                 protected void setParameterValue(PreparedStatement preparedStatement, int valueIndex, Dependencies depends)
709                         throws JDBCStatementCreatorException {
710
711                         //handle values from DataConnectors
712                         if (referencesConnector) {
713                                 Attributes attributes = depends.getConnectorResolution(connectorId);
714                                 if (attributes == null) {
715                                         log.error(
716                                                 "Statement Creator misconfiguration: Connector ("
717                                                         + connectorId
718                                                         + ") is not a dependency of this JDBCDataConnector.");
719                                         throw new JDBCStatementCreatorException(
720                                                 "Statement Creator misconfiguration: Connector ("
721                                                         + connectorId
722                                                         + ") is not a dependency of this JDBCDataConnector.");
723                                 }
724
725                                 Attribute attribute = attributes.get(attributeName);
726                                 if (attribute == null || attribute.size() < 1) {
727                                         if (nullMissing) {
728                                                 try {
729                                                         preparedStatement.setNull(valueIndex, getSQLType());
730                                                         return;
731                                                 } catch (SQLException e) {
732                                                         log.error(
733                                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
734                                                 }
735                                         }
736                                         log.error("Cannot parameterize prepared statement: missing dependency value.");
737                                         throw new JDBCStatementCreatorException("Cannot parameterize prepared statement: missing dependency value.");
738                                 }
739
740                                 if (attribute.size() > 1) {
741                                         log.error("Statement Creator encountered a multivalued dependent attribute.");
742                                         throw new JDBCStatementCreatorException("Statement Creator encountered a multivalued dependent attribute.");
743                                 }
744
745                                 try {
746                                         setSpecificParameter(preparedStatement, valueIndex, attribute.get());
747                                         return;
748                                 } catch (NamingException e) {
749                                         log.error(
750                                                 "Statement Creator encountered an error while extracting attributes from a Data Conector: "
751                                                         + e);
752                                         throw new JDBCStatementCreatorException(
753                                                 "Statement Creator encountered an error while extracting attributes from a Data Conector: "
754                                                         + e.getMessage());
755                                 }
756                         }
757
758                         //handle values from AttributeDefinitons
759                         ResolverAttribute attribute = depends.getAttributeResolution(attributeName);
760                         if (attribute != null) {
761                                 Iterator iterator = attribute.getValues();
762                                 if (iterator.hasNext()) {
763                                         setSpecificParameter(preparedStatement, valueIndex, iterator.next());
764                                         if (iterator.hasNext()) {
765                                                 log.error("Statement Creator encountered a multivalued dependent attribute.");
766                                                 throw new JDBCStatementCreatorException("Statement Creator encountered a multivalued dependent attribute.");
767                                         }
768                                         return;
769                                 }
770                         }
771                         if (nullMissing) {
772                                 try {
773                                         preparedStatement.setNull(valueIndex, getSQLType());
774                                         return;
775                                 } catch (SQLException e) {
776                                         log.error(
777                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
778                                 }
779                         }
780                         log.error("Cannot parameterize prepared statement: missing dependency value.");
781                         throw new JDBCStatementCreatorException("Cannot parameterize prepared statement: missing dependency value.");
782                 }
783
784                 protected void setNullMissing(boolean nullMissing) {
785                         this.nullMissing = nullMissing;
786                 }
787
788                 private void setSpecificParameter(PreparedStatement preparedStatement, int valueIndex, Object object)
789                         throws JDBCStatementCreatorException {
790
791                         if (object == null) {
792                                 try {
793                                         preparedStatement.setNull(valueIndex, getSQLType());
794                                         return;
795                                 } catch (SQLException e) {
796                                         log.error(
797                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
798                                         throw new JDBCStatementCreatorException("Encountered a problem while attempting to convert missing attribute value to null parameter.");
799                                 }
800                         } else if (type.equalsIgnoreCase("String")) {
801                                 setString(preparedStatement, valueIndex, object);
802                         } else if (type.equalsIgnoreCase("Integer")) {
803                                 setInteger(preparedStatement, valueIndex, object);
804                         } else if (type.equalsIgnoreCase("Byte")) {
805                                 setByte(preparedStatement, valueIndex, object);
806                         } else if (type.equalsIgnoreCase("Double")) {
807                                 setDouble(preparedStatement, valueIndex, object);
808                         } else if (type.equalsIgnoreCase("Float")) {
809                                 setFloat(preparedStatement, valueIndex, object);
810                         } else if (type.equalsIgnoreCase("Long")) {
811                                 setLong(preparedStatement, valueIndex, object);
812                         } else if (type.equalsIgnoreCase("Short")) {
813                                 setShort(preparedStatement, valueIndex, object);
814                         } else if (type.equalsIgnoreCase("Boolean")) {
815                                 setBoolean(preparedStatement, valueIndex, object);
816                         } else if (type.equalsIgnoreCase("Date")) {
817                                 setDate(preparedStatement, valueIndex, object);
818                         } else if (type.equalsIgnoreCase("Blob")) {
819                                 setBlob(preparedStatement, valueIndex, object);
820                         } else if (type.equalsIgnoreCase("Clob")) {
821                                 setClob(preparedStatement, valueIndex, object);
822                         } else {
823                                 setString(preparedStatement, valueIndex, object);
824                         }
825                 }
826
827                 private void setClob(PreparedStatement preparedStatement, int valueIndex, Object object)
828                         throws JDBCStatementCreatorException {
829                         if (object instanceof Clob) {
830                                 try {
831                                         preparedStatement.setClob(valueIndex, (Clob) object);
832                                         return;
833                                 } catch (SQLException e) {
834                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
835                                         throw new JDBCStatementCreatorException(
836                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
837                                 }
838                         }
839                         log.error("Encountered a dependency with an invalid java type.");
840                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
841                 }
842
843                 private void setBlob(PreparedStatement preparedStatement, int valueIndex, Object object)
844                         throws JDBCStatementCreatorException {
845                         if (object instanceof Blob) {
846                                 try {
847                                         preparedStatement.setBlob(valueIndex, (Blob) object);
848                                         return;
849                                 } catch (SQLException e) {
850                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
851                                         throw new JDBCStatementCreatorException(
852                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
853                                 }
854                         }
855                         log.error("Encountered a dependency with an invalid java type.");
856                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
857                 }
858
859                 private void setDate(PreparedStatement preparedStatement, int valueIndex, Object object)
860                         throws JDBCStatementCreatorException {
861
862                         if (object instanceof java.sql.Date) {
863                                 try {
864                                         preparedStatement.setDate(valueIndex, (java.sql.Date) object);
865                                         return;
866                                 } catch (SQLException e) {
867                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
868                                         throw new JDBCStatementCreatorException(
869                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
870                                 }
871                         } else if (object instanceof java.util.Date) {
872                                 try {
873                                         //If you want to be frustrated by the java class library, look no further...
874                                         preparedStatement.setDate(valueIndex, new java.sql.Date(((java.util.Date) object).getTime()));
875                                         return;
876                                 } catch (SQLException e) {
877                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
878                                         throw new JDBCStatementCreatorException(
879                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
880                                 }
881                         } else if (object instanceof Long) {
882                                 try {
883                                         preparedStatement.setDate(valueIndex, new java.sql.Date(((Long) object).longValue()));
884                                         return;
885                                 } catch (SQLException e) {
886                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
887                                         throw new JDBCStatementCreatorException(
888                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
889                                 }
890                         } else if (object instanceof String) {
891                                 try {
892                                         preparedStatement.setDate(
893                                                 valueIndex,
894                                                 new java.sql.Date(new SimpleDateFormat().parse((String) object).getTime()));
895                                         return;
896                                 } catch (Exception e) {
897                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
898                                         throw new JDBCStatementCreatorException(
899                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
900                                 }
901                         }
902                         log.error("Encountered a dependency with an invalid java type.");
903                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
904                 }
905
906                 private void setBoolean(PreparedStatement preparedStatement, int valueIndex, Object object)
907                         throws JDBCStatementCreatorException {
908                         if (object instanceof Boolean) {
909                                 try {
910                                         preparedStatement.setBoolean(valueIndex, ((Boolean) object).booleanValue());
911                                         return;
912                                 } catch (SQLException e) {
913                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
914                                         throw new JDBCStatementCreatorException(
915                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
916                                 }
917                         } else if (object instanceof String) {
918                                 try {
919                                         preparedStatement.setBoolean(valueIndex, new Boolean((String) object).booleanValue());
920                                         return;
921                                 } catch (Exception e) {
922                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
923                                         throw new JDBCStatementCreatorException(
924                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
925                                 }
926                         }
927                         log.error("Encountered a dependency with an invalid java type.");
928                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
929                 }
930
931                 private void setShort(PreparedStatement preparedStatement, int valueIndex, Object object)
932                         throws JDBCStatementCreatorException {
933                         if (object instanceof Boolean) {
934                                 try {
935                                         preparedStatement.setShort(valueIndex, ((Short) object).shortValue());
936                                         return;
937                                 } catch (SQLException e) {
938                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
939                                         throw new JDBCStatementCreatorException(
940                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
941                                 }
942                         } else if (object instanceof String) {
943                                 try {
944                                         preparedStatement.setShort(valueIndex, new Short((String) object).shortValue());
945                                         return;
946                                 } catch (Exception e) {
947                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
948                                         throw new JDBCStatementCreatorException(
949                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
950                                 }
951                         }
952                         log.error("Encountered a dependency with an invalid java type.");
953                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
954                 }
955
956                 private void setLong(PreparedStatement preparedStatement, int valueIndex, Object object)
957                         throws JDBCStatementCreatorException {
958                         if (object instanceof Long || object instanceof Integer || object instanceof Short) {
959                                 try {
960                                         preparedStatement.setLong(valueIndex, ((Number) object).longValue());
961                                         return;
962                                 } catch (SQLException e) {
963                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
964                                         throw new JDBCStatementCreatorException(
965                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
966                                 }
967                         } else if (object instanceof String) {
968                                 try {
969                                         preparedStatement.setLong(valueIndex, new Long((String) object).longValue());
970                                         return;
971                                 } catch (Exception e) {
972                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
973                                         throw new JDBCStatementCreatorException(
974                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
975                                 }
976                         }
977                         log.error("Encountered a dependency with an invalid java type.");
978                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
979                 }
980
981                 private void setFloat(PreparedStatement preparedStatement, int valueIndex, Object object)
982                         throws JDBCStatementCreatorException {
983                         if (object instanceof Float) {
984                                 try {
985                                         preparedStatement.setFloat(valueIndex, ((Float) object).floatValue());
986                                         return;
987                                 } catch (SQLException e) {
988                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
989                                         throw new JDBCStatementCreatorException(
990                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
991                                 }
992                         } else if (object instanceof String) {
993                                 try {
994                                         preparedStatement.setFloat(valueIndex, new Float((String) object).floatValue());
995                                         return;
996                                 } catch (Exception e) {
997                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
998                                         throw new JDBCStatementCreatorException(
999                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1000                                 }
1001                         }
1002                         log.error("Encountered a dependency with an invalid java type.");
1003                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1004                 }
1005
1006                 private void setDouble(PreparedStatement preparedStatement, int valueIndex, Object object)
1007                         throws JDBCStatementCreatorException {
1008                         if (object instanceof Double || object instanceof Float) {
1009                                 try {
1010                                         preparedStatement.setDouble(valueIndex, ((Number) object).doubleValue());
1011                                         return;
1012                                 } catch (SQLException e) {
1013                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1014                                         throw new JDBCStatementCreatorException(
1015                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1016                                 }
1017                         } else if (object instanceof String) {
1018                                 try {
1019                                         preparedStatement.setDouble(valueIndex, new Double((String) object).doubleValue());
1020                                         return;
1021                                 } catch (Exception e) {
1022                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1023                                         throw new JDBCStatementCreatorException(
1024                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1025                                 }
1026                         }
1027                         log.error("Encountered a dependency with an invalid java type.");
1028                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1029                 }
1030
1031                 private void setByte(PreparedStatement preparedStatement, int valueIndex, Object object)
1032                         throws JDBCStatementCreatorException {
1033                         if (object instanceof Byte) {
1034                                 try {
1035                                         preparedStatement.setByte(valueIndex, ((Byte) object).byteValue());
1036                                         return;
1037                                 } catch (SQLException e) {
1038                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1039                                         throw new JDBCStatementCreatorException(
1040                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1041                                 }
1042                         } else if (object instanceof String) {
1043                                 try {
1044                                         preparedStatement.setByte(valueIndex, new Byte((String) object).byteValue());
1045                                         return;
1046                                 } catch (Exception e) {
1047                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1048                                         throw new JDBCStatementCreatorException(
1049                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1050                                 }
1051                         }
1052                         log.error("Encountered a dependency with an invalid java type.");
1053                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1054                 }
1055
1056                 private void setInteger(PreparedStatement preparedStatement, int valueIndex, Object object)
1057                         throws JDBCStatementCreatorException {
1058                         if (object instanceof Integer || object instanceof Short) {
1059                                 try {
1060                                         preparedStatement.setInt(valueIndex, ((Number) object).intValue());
1061                                         return;
1062                                 } catch (SQLException e) {
1063                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1064                                         throw new JDBCStatementCreatorException(
1065                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1066                                 }
1067                         } else if (object instanceof String) {
1068                                 try {
1069                                         preparedStatement.setInt(valueIndex, new Integer((String) object).intValue());
1070                                         return;
1071                                 } catch (Exception e) {
1072                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1073                                         throw new JDBCStatementCreatorException(
1074                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1075                                 }
1076                         }
1077                         log.error("Encountered a dependency with an invalid java type.");
1078                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1079                 }
1080
1081                 private void setString(PreparedStatement preparedStatement, int valueIndex, Object object)
1082                         throws JDBCStatementCreatorException {
1083                         if (object instanceof String) {
1084                                 try {
1085                                         preparedStatement.setString(valueIndex, (String) object);
1086                                         return;
1087                                 } catch (SQLException e) {
1088                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1089                                         throw new JDBCStatementCreatorException(
1090                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1091                                 }
1092                         }
1093                         log.error("Encountered a dependency with an invalid java type.");
1094                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1095                 }
1096         }
1097 }