Added support for data connector failover and property config.
[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 BaseResolutionPlugIn 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     protected String failover = null;
95
96         public JDBCDataConnector(Element e) throws ResolutionPlugInException {
97
98                 super(e);
99
100         NodeList failoverNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "FailoverDependency");
101         if (failoverNodes.getLength() > 0) {
102             failover = ((Element)failoverNodes.item(0)).getAttribute("requires");
103         }
104                 //Get the query string
105                 NodeList queryNodes = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Query");
106                 Node tnode = queryNodes.item(0).getFirstChild();
107                 if (tnode != null && tnode.getNodeType() == Node.TEXT_NODE) {
108                         searchVal = tnode.getNodeValue();
109                 }
110                 if (searchVal == null || searchVal.equals("")) {
111                         log.error("Database query must be specified.");
112                         throw new ResolutionPlugInException("Database query must be specified.");
113                 }
114
115                 //Load the supplied JDBC driver
116                 String dbDriverName = e.getAttribute("dbDriver");
117                 if (dbDriverName != null && (!dbDriverName.equals(""))) {
118                         loadDriver(dbDriverName);
119                 }
120
121                 //Load site-specific implementation classes     
122                 setupAttributeExtractor(
123                         (Element) e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "AttributeExtractor").item(
124                                 0));
125                 setupStatementCreator(
126                         (Element) e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "StatementCreator").item(0));
127         
128         //Load driver properties
129         Properties props = new Properties();
130         NodeList propertiesNode = e.getElementsByTagNameNS(AttributeResolver.resolverNamespace, "Property");
131         for (int i = 0; propertiesNode.getLength() > i; i++) {
132             Element property = (Element) propertiesNode.item(i);
133             String propertiesName = property.getAttribute("name");
134             String propertiesValue = property.getAttribute("value");
135
136             if (propertiesName != null
137                 && !propertiesName.equals("")
138                 && propertiesValue != null
139                 && !propertiesValue.equals("")) {
140                 props.setProperty(propertiesName, propertiesValue);
141                 log.debug("Property: (" + propertiesName + ")");
142                 log.debug("   Value: (" + propertiesValue + ")");
143             } else {
144                 log.error("Property is malformed.");
145                 throw new ResolutionPlugInException("Property is malformed.");
146             }
147         }
148         
149                 //Initialize a pooling Data Source
150                 int maxActive = 0;
151                 int maxIdle = 0;
152                 try {
153                         if (e.getAttributeNode("maxActive") != null) {
154                                 maxActive = Integer.parseInt(e.getAttribute("maxActive"));
155                         }
156                         if (e.getAttributeNode("maxIdle") != null) {
157                                 maxIdle = Integer.parseInt(e.getAttribute("maxIdle"));
158                         }
159                 } catch (NumberFormatException ex) {
160                         log.error("Malformed pooling limits: using defaults.");
161                 }
162                 if (e.getAttribute("dbURL") == null || e.getAttribute("dbURL").equals("")) {
163                         log.error("JDBC connection requires a dbURL property");
164                         throw new ResolutionPlugInException("JDBCDataConnection requires a \"dbURL\" property");
165                 }
166                 setupDataSource(e.getAttribute("dbURL"), props, maxActive, maxIdle);
167         }
168
169         /**
170          * Initialize a Pooling Data Source
171          */
172         private void setupDataSource(String dbURL, Properties props, int maxActive, int maxIdle) throws ResolutionPlugInException {
173
174                 GenericObjectPool objectPool = new GenericObjectPool(null);
175
176                 if (maxActive > 0) {
177                         objectPool.setMaxActive(maxActive);
178                 }
179                 if (maxIdle > 0) {
180                         objectPool.setMaxIdle(maxIdle);
181                 }
182
183                 objectPool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_BLOCK);
184
185                 ConnectionFactory connFactory = null;
186                 PoolableConnectionFactory poolConnFactory = null;
187
188                 try {
189                         connFactory = new DriverManagerConnectionFactory(dbURL, props);
190                         log.debug("Connection factory initialized.");
191                 } catch (Exception ex) {
192                         log.error(
193                                 "Connection factory couldn't be initialized, ensure database URL, username and password are correct.");
194                         throw new ResolutionPlugInException("Connection factory couldn't be initialized: " + ex.getMessage());
195                 }
196
197                 try {
198                         poolConnFactory =
199                         new PoolableConnectionFactory(
200                                 connFactory,
201                                 objectPool,
202                                 new StackKeyedObjectPoolFactory(),
203                                 null,
204                                 false,
205                                         true);
206                 } catch (Exception ex) {
207                         log.debug("Poolable connection factory error");
208                 }
209
210                 dataSource = new PoolingDataSource(objectPool);
211                 log.info("Data Source initialized.");
212                 try {
213                         dataSource.setLogWriter(
214                                 new Log4jPrintWriter(Logger.getLogger(JDBCDataConnector.class.getName() + ".Pool"), Priority.DEBUG));
215                 } catch (SQLException e) {
216                         log.error("Coudn't setup logger for database connection pool.");
217                 }
218         }
219
220         /**
221          * Instantiate an Attribute Extractor, using the default if none was configured
222          */
223         private void setupAttributeExtractor(Element config) throws ResolutionPlugInException {
224
225                 String className = null;
226                 if (config != null) {
227                         className = config.getAttribute("class");
228                 }
229                 if (className == null || className.equals("")) {
230                         log.debug("Using default Attribute Extractor.");
231                         className = DefaultAE.class.getName();
232                 }
233                 try {
234                         Class aeClass = Class.forName(className);
235                         extractor = (JDBCAttributeExtractor) aeClass.newInstance();
236                         log.debug("Attribute Extractor implementation loaded.");
237
238                 } catch (ClassNotFoundException e) {
239                         log.error("The supplied Attribute Extractor class could not be found: " + e);
240                         throw new ResolutionPlugInException(
241                                 "The supplied Attribute Extractor class could not be found: " + e.getMessage());
242                 } catch (Exception e) {
243                         log.error("Unable to instantiate Attribute Extractor implementation: " + e);
244                         throw new ResolutionPlugInException(
245                                 "Unable to instantiate Attribute Extractor implementation: " + e.getMessage());
246                 }
247         }
248
249         /**
250          * Instantiate a Statement Creator, using the default if none was configured
251          */
252         private void setupStatementCreator(Element config) throws ResolutionPlugInException {
253
254                 String scClassName = null;
255                 if (config != null) {
256                         scClassName = config.getAttribute("class");
257                 }
258                 if (scClassName == null || scClassName.equals("")) {
259                         log.debug("Using default Statement Creator.");
260                         scClassName = DefaultStatementCreator.class.getName();
261                 }
262                 try {
263                         Class scClass = Class.forName(scClassName);
264
265                         Class[] params = new Class[1];
266                         params[0] = Class.forName("org.w3c.dom.Element");
267                         try {
268                                 Constructor implementorConstructor = scClass.getConstructor(params);
269                                 Object[] args = new Object[1];
270                                 args[0] = config;
271                                 log.debug("Initializing Statement Creator of type (" + scClass.getName() + ").");
272                                 statementCreator = (JDBCStatementCreator) implementorConstructor.newInstance(args);
273                         } catch (NoSuchMethodException nsme) {
274                                 log.debug(
275                                         "Implementation constructor does have a parameterized constructor, attempting to load default.");
276                                 statementCreator = (JDBCStatementCreator) scClass.newInstance();
277                         }
278                         log.debug("Statement Creator implementation loaded.");
279
280                 } catch (ClassNotFoundException e) {
281                         log.error("The supplied Statement Creator class could not be found: " + e);
282                         throw new ResolutionPlugInException(
283                                 "The supplied Statement Creator class could not be found: " + e.getMessage());
284                 } catch (Exception e) {
285                         log.error("Unable to instantiate Statement Creator implementation: " + e);
286                         throw new ResolutionPlugInException(
287                                 "Unable to instantiate Statement Creator implementation: " + e.getMessage());
288                 }
289         }
290
291         public Attributes resolve(Principal principal, String requester, Dependencies depends)
292                 throws ResolutionPlugInException {
293
294                 log.debug("Resolving connector: (" + getId() + ")");
295
296                 //Retrieve a connection from the connection pool
297                 Connection conn = null;
298                 try {
299                         conn = dataSource.getConnection();
300                         log.debug("Connection retrieved from pool");
301                 } catch (Exception e) {
302                         log.error("Unable to fetch a connection from the pool");
303                         throw new ResolutionPlugInException("Unable to fetch a connection from the pool: " + e.getMessage());
304                 }
305                 if (conn == null) {
306                         log.error("Pool didn't return a propertly initialized connection.");
307                         throw new ResolutionPlugInException("Pool didn't return a properly initialized connection.");
308                 }
309
310                 //Setup and execute a (pooled) prepared statement
311                 ResultSet rs = null;
312                 PreparedStatement preparedStatement;
313                 try {
314                         preparedStatement = conn.prepareStatement(searchVal);
315                         statementCreator.create(preparedStatement, principal, requester, depends);
316                         rs = preparedStatement.executeQuery();
317                         if (!rs.next()) {
318                                 return new BasicAttributes();
319                         }
320
321                 } catch (JDBCStatementCreatorException e) {
322                         log.error("An ERROR occured while constructing the query");
323                         throw new ResolutionPlugInException("An ERROR occured while constructing the query: " + e.getMessage());
324                 } catch (SQLException e) {
325                         log.error("An ERROR occured while executing the query");
326                         throw new ResolutionPlugInException("An ERROR occured while executing the query: " + e.getMessage());
327                 }
328
329                 //Extract attributes from the ResultSet
330                 try {
331                         return extractor.extractAttributes(rs);
332
333                 } catch (JDBCAttributeExtractorException e) {
334                         log.error("An ERROR occured while extracting attributes from result set");
335                         throw new ResolutionPlugInException(
336                                 "An ERROR occured while extracting attributes from result set: " + e.getMessage());
337                 } finally {
338                         try {
339                                 if (preparedStatement != null) {
340                                         preparedStatement.close();
341                                 }
342                         } catch (SQLException e) {
343                                 log.error("An error occured while closing the prepared statement: " + e);
344                                 throw new ResolutionPlugInException("An error occured while closing the prepared statement: " + e);
345                         }
346                         try {
347                                 rs.close();
348                         } catch (SQLException e) {
349                                 log.error("An error occured while closing the result set: " + e);
350                                 throw new ResolutionPlugInException("An error occured while closing the result set: " + e);
351                         }
352
353                         try {
354                                 conn.close();
355                         } catch (SQLException e) {
356                                 log.error("An error occured while closing the database connection: " + e);
357                                 throw new ResolutionPlugInException("An error occured while closing the database connection: " + e);
358                         }
359                 }
360         }
361
362         /** 
363          * Loads the driver used to access the database
364          * @param driver The driver used to access the database
365          * @throws ResolutionPlugInException If there is a failure to load the driver
366          */
367         public void loadDriver(String driver) throws ResolutionPlugInException {
368                 try {
369                         Class.forName(driver).newInstance();
370                         log.debug("Loading JDBC driver: " + driver);
371                 } catch (Exception e) {
372                         log.error("An error loading database driver: " + e);
373                         throw new ResolutionPlugInException(
374                                 "An IllegalAccessException occured while loading database driver: " + e.getMessage());
375                 }
376                 log.debug("Driver loaded.");
377         }
378
379     /**
380      * @see edu.internet2.middleware.shibboleth.aa.attrresolv.DataConnectorPlugIn#getFailoverDependencyId()
381      */
382     public String getFailoverDependencyId() {
383         return failover;
384     }
385
386         private class Log4jPrintWriter extends PrintWriter {
387
388                 private Priority level;
389                 private Logger logger;
390                 private StringBuffer text = new StringBuffer("");
391
392                 private Log4jPrintWriter(Logger logger, org.apache.log4j.Priority level) {
393                         super(System.err);
394                         this.level = level;
395                         this.logger = logger;
396                 }
397
398                 public void close() {
399                         flush();
400                 }
401
402                 public void flush() {
403                         if (!text.toString().equals("")) {
404                                 logger.log(level, text.toString());
405                                 text.setLength(0);
406                         }
407                 }
408
409                 public void print(boolean b) {
410                         text.append(b);
411                 }
412
413                 public void print(char c) {
414                         text.append(c);
415                 }
416
417                 public void print(char[] s) {
418                         text.append(s);
419                 }
420
421                 public void print(double d) {
422                         text.append(d);
423                 }
424
425                 public void print(float f) {
426                         text.append(f);
427                 }
428
429                 public void print(int i) {
430                         text.append(i);
431                 }
432
433                 public void print(long l) {
434                         text.append(l);
435                 }
436
437                 public void print(Object obj) {
438                         text.append(obj);
439                 }
440
441                 public void print(String s) {
442                         text.append(s);
443                 }
444
445                 public void println() {
446                         if (!text.toString().equals("")) {
447                                 logger.log(level, text.toString());
448                                 text.setLength(0);
449                         }
450                 }
451
452                 public void println(boolean x) {
453                         text.append(x);
454                         logger.log(level, text.toString());
455                         text.setLength(0);
456                 }
457
458                 public void println(char x) {
459                         text.append(x);
460                         logger.log(level, text.toString());
461                         text.setLength(0);
462                 }
463
464                 public void println(char[] x) {
465                         text.append(x);
466                         logger.log(level, text.toString());
467                         text.setLength(0);
468                 }
469
470                 public void println(double x) {
471                         text.append(x);
472                         logger.log(level, text.toString());
473                         text.setLength(0);
474                 }
475
476                 public void println(float x) {
477                         text.append(x);
478                         logger.log(level, text.toString());
479                         text.setLength(0);
480                 }
481
482                 public void println(int x) {
483                         text.append(x);
484                         logger.log(level, text.toString());
485                         text.setLength(0);
486                 }
487
488                 public void println(long x) {
489                         text.append(x);
490                         logger.log(level, text.toString());
491                         text.setLength(0);
492                 }
493
494                 public void println(Object x) {
495                         text.append(x);
496                         logger.log(level, text.toString());
497                         text.setLength(0);
498                 }
499
500                 public void println(String x) {
501                         text.append(x);
502                         logger.log(level, text.toString());
503                         text.setLength(0);
504                 }
505         }
506 }
507
508 /**
509  * The default attribute extractor. 
510  */
511 class DefaultAE implements JDBCAttributeExtractor {
512
513         private static Logger log = Logger.getLogger(DefaultAE.class.getName());
514
515         /**
516          * Method of extracting the attributes from the supplied result set.
517          *
518          * @param ResultSet The result set from the query which contains the attributes
519          * @return BasicAttributes as objects containing all the attributes
520          * @throws JDBCAttributeExtractorException If there is a complication in retrieving the attributes
521          */
522         public BasicAttributes extractAttributes(ResultSet rs) throws JDBCAttributeExtractorException {
523                 BasicAttributes attributes = new BasicAttributes();
524
525                 try {
526                         ResultSetMetaData rsmd = rs.getMetaData();
527                         int numColumns = rsmd.getColumnCount();
528                         log.debug("Number of returned columns: " + numColumns);
529
530                         for (int i = 1; i <= numColumns; i++) {
531                                 String columnName = rsmd.getColumnName(i);
532                                 String columnType = rsmd.getColumnTypeName(i);
533                                 Object columnValue = rs.getObject(columnName);
534                                 log.debug(
535                                         "("
536                                                 + i
537                                                 + ". ColumnType = "
538                                                 + columnType
539                                                 + ") "
540                                                 + columnName
541                                                 + " -> "
542                                                 + (columnValue != null ? columnValue.toString() : "(null)"));
543                                 attributes.put(new BasicAttribute(columnName, columnValue));
544                         }
545                 } catch (SQLException e) {
546                         log.error("An ERROR occured while retrieving result set meta data");
547                         throw new JDBCAttributeExtractorException(
548                                 "An ERROR occured while retrieving result set meta data: " + e.getMessage());
549                 }
550
551                 // Check for multiple rows.
552                 try {
553                         if (rs.next()) {
554                                 throw new JDBCAttributeExtractorException("Query returned more than one row.");
555                         }
556                 } catch (SQLException e) {
557                         log.error("An ERROR occured while retrieving result set meta data");
558                         throw new JDBCAttributeExtractorException(
559                                 "An ERROR occured while retrieving result set meta data: " + e.getMessage());
560                 }
561
562                 return attributes;
563         }
564 }
565
566 class DefaultStatementCreator implements JDBCStatementCreator {
567
568         private static Logger log = Logger.getLogger(DefaultStatementCreator.class.getName());
569
570         public void create(
571                 PreparedStatement preparedStatement,
572                 Principal principal,
573                 String requester,
574                 Dependencies depends)
575                 throws JDBCStatementCreatorException {
576
577                 try {
578                         log.debug("Creating prepared statement.  Substituting principal: (" + principal.getName() + ")");
579                         preparedStatement.setString(1, principal.getName());
580                 } catch (SQLException e) {
581                         log.error("Encountered an error while creating prepared statement: " + e);
582                         throw new JDBCStatementCreatorException(
583                                 "Encountered an error while creating prepared statement: " + e.getMessage());
584                 }
585         }
586 }
587
588 class DependencyStatementCreator implements JDBCStatementCreator {
589
590         private static Logger log = Logger.getLogger(DependencyStatementCreator.class.getName());
591         private ArrayList parameters = new ArrayList();
592
593         public DependencyStatementCreator(Element conf) throws JDBCStatementCreatorException {
594
595                 NodeList nodes = conf.getElementsByTagName("Parameter");
596                 for (int i = 0; i < nodes.getLength(); i++) {
597                         Element parameter = (Element) nodes.item(i);
598                         String type = "String";
599                         if (parameter.getAttribute("type") != null && (!parameter.getAttribute("type").equals(""))) {
600                                 type = parameter.getAttribute("type");
601                         }
602
603                         if (parameter.getAttribute("attributeName") == null
604                                 || parameter.getAttribute("attributeName").equals("")) {
605                                 log.error("Statement Creator Parameter must reference an attribute by name.");
606                                 throw new JDBCStatementCreatorException("Statement Creator Parameter must reference an attribute by name.");
607                         }
608
609                         if (parameter.getAttribute("connectorId") != null && (!parameter.getAttribute("connectorId").equals(""))) {
610                                 parameters.add(
611                                         new Parameter(
612                                                 type,
613                                                 parameter.getAttribute("attributeName"),
614                                                 parameter.getAttribute("connectorId")));
615                         } else {
616                                 parameters.add(new Parameter(type, parameter.getAttribute("attributeName")));
617
618                         }
619
620                         if (parameter.getAttribute("nullMissing") != null && (!parameter.getAttribute("nullMissing").equals(""))) {
621                                 if (parameter.getAttribute("nullMissing").equalsIgnoreCase("FALSE")) {
622                                         ((Parameter) parameters.get(i)).setNullMissing(false);
623                                 }
624                         }
625                 }
626                 log.debug("Parameters configured: " + parameters.size());
627         }
628
629         public void create(
630                 PreparedStatement preparedStatement,
631                 Principal principal,
632                 String requester,
633                 Dependencies depends)
634                 throws JDBCStatementCreatorException {
635
636                 try {
637                         log.debug("Creating prepared statement.  Substituting values from dependencies.");
638                         for (int i = 0; i < parameters.size(); i++) {
639                                 ((Parameter) parameters.get(i)).setParameterValue(preparedStatement, i + 1, depends);
640                         }
641
642                 } catch (Exception e) {
643                         log.error("Encountered an error while creating prepared statement: " + e);
644                         throw new JDBCStatementCreatorException(
645                                 "Encountered an error while creating prepared statement: " + e.getMessage());
646                 }
647         }
648
649         protected class Parameter {
650                 private String type;
651                 private String attributeName;
652                 private boolean referencesConnector = false;
653                 private String connectorId;
654                 private boolean nullMissing = true;
655
656                 protected Parameter(String type, String attributeName) throws JDBCStatementCreatorException {
657                         if ((!type.equalsIgnoreCase("String"))
658                                 && (!type.equalsIgnoreCase("Integer"))
659                                 && (!type.equalsIgnoreCase("Byte"))
660                                 && (!type.equalsIgnoreCase("Double"))
661                                 && (!type.equalsIgnoreCase("Float"))
662                                 && (!type.equalsIgnoreCase("Long"))
663                                 && (!type.equalsIgnoreCase("Short"))
664                                 && (!type.equalsIgnoreCase("Boolean"))
665                                 && (!type.equalsIgnoreCase("Date"))
666                                 && (!type.equalsIgnoreCase("Blob"))
667                                 && (!type.equalsIgnoreCase("Clob"))) {
668                                 log.error("Unsupported type configured for Statement Creator Parameter.");
669                                 throw new JDBCStatementCreatorException("Unsupported type on Statement Creator Parameter.");
670                         }
671                         this.type = type;
672                         this.attributeName = attributeName;
673                 }
674
675                 protected Parameter(String type, String attributeName, String connectorId)
676                         throws JDBCStatementCreatorException {
677                         this(type, attributeName);
678                         referencesConnector = true;
679                         this.connectorId = connectorId;
680
681                 }
682
683                 protected void setParameterValue(PreparedStatement preparedStatement, int valueIndex, Dependencies depends)
684                         throws JDBCStatementCreatorException {
685
686                         //handle values from DataConnectors
687                         if (referencesConnector) {
688                                 Attributes attributes = depends.getConnectorResolution(connectorId);
689                                 if (attributes == null) {
690                                         log.error(
691                                                 "Statement Creator misconfiguration: Connector ("
692                                                         + connectorId
693                                                         + ") is not a dependency of this JDBCDataConnector.");
694                                         throw new JDBCStatementCreatorException(
695                                                 "Statement Creator misconfiguration: Connector ("
696                                                         + connectorId
697                                                         + ") is not a dependency of this JDBCDataConnector.");
698                                 }
699
700                                 Attribute attribute = attributes.get(attributeName);
701                                 if (attribute == null || attribute.size() < 1) {
702                                         if (nullMissing) {
703                                                 try {
704                                                         preparedStatement.setNull(valueIndex, Types.NULL);
705                                                         return;
706                                                 } catch (SQLException e) {
707                                                         log.error(
708                                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
709                                                 }
710                                         }
711                                         log.error("Cannot parameterize prepared statement: missing dependency value.");
712                                         throw new JDBCStatementCreatorException("Cannot parameterize prepared statement: missing dependency value.");
713                                 }
714
715                                 if (attribute.size() > 1) {
716                                         log.error("Statement Creator encountered a multivalued dependent attribute.");
717                                         throw new JDBCStatementCreatorException("Statement Creator encountered a multivalued dependent attribute.");
718                                 }
719
720                                 try {
721                                         setSpecificParameter(preparedStatement, valueIndex, attribute.get());
722                                         return;
723                                 } catch (NamingException e) {
724                                         log.error(
725                                                 "Statement Creator encountered an error while extracting attributes from a Data Conector: "
726                                                         + e);
727                                         throw new JDBCStatementCreatorException(
728                                                 "Statement Creator encountered an error while extracting attributes from a Data Conector: "
729                                                         + e.getMessage());
730                                 }
731                         }
732
733                         //handle values from AttributeDefinitons
734                         ResolverAttribute attribute = depends.getAttributeResolution(attributeName);
735                         if (attribute != null) {
736                                 Iterator iterator = attribute.getValues();
737                                 if (iterator.hasNext()) {
738                                         setSpecificParameter(preparedStatement, valueIndex, iterator.next());
739                                         if (iterator.hasNext()) {
740                                                 log.error("Statement Creator encountered a multivalued dependent attribute.");
741                                                 throw new JDBCStatementCreatorException("Statement Creator encountered a multivalued dependent attribute.");
742                                         }
743                                         return;
744                                 }
745                         }
746                         if (nullMissing) {
747                                 try {
748                                         preparedStatement.setNull(valueIndex, Types.NULL);
749                                         return;
750                                 } catch (SQLException e) {
751                                         log.error(
752                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
753                                 }
754                         }
755                         log.error("Cannot parameterize prepared statement: missing dependency value.");
756                         throw new JDBCStatementCreatorException("Cannot parameterize prepared statement: missing dependency value.");
757                 }
758
759                 protected void setNullMissing(boolean nullMissing) {
760                         this.nullMissing = nullMissing;
761                 }
762
763                 private void setSpecificParameter(PreparedStatement preparedStatement, int valueIndex, Object object)
764                         throws JDBCStatementCreatorException {
765
766                         if (object == null) {
767                                 try {
768                                         preparedStatement.setNull(valueIndex, Types.NULL);
769                                         return;
770                                 } catch (SQLException e) {
771                                         log.error(
772                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
773                                         throw new JDBCStatementCreatorException("Encountered a problem while attempting to convert missing attribute value to null parameter.");
774                                 }
775                         } else if (type.equalsIgnoreCase("String")) {
776                                 setString(preparedStatement, valueIndex, object);
777                         } else if (type.equalsIgnoreCase("Integer")) {
778                                 setInteger(preparedStatement, valueIndex, object);
779                         } else if (type.equalsIgnoreCase("Byte")) {
780                                 setByte(preparedStatement, valueIndex, object);
781                         } else if (type.equalsIgnoreCase("Double")) {
782                                 setDouble(preparedStatement, valueIndex, object);
783                         } else if (type.equalsIgnoreCase("Float")) {
784                                 setFloat(preparedStatement, valueIndex, object);
785                         } else if (type.equalsIgnoreCase("Long")) {
786                                 setLong(preparedStatement, valueIndex, object);
787                         } else if (type.equalsIgnoreCase("Short")) {
788                                 setShort(preparedStatement, valueIndex, object);
789                         } else if (type.equalsIgnoreCase("Boolean")) {
790                                 setBoolean(preparedStatement, valueIndex, object);
791                         } else if (type.equalsIgnoreCase("Date")) {
792                                 setDate(preparedStatement, valueIndex, object);
793                         } else if (type.equalsIgnoreCase("Blob")) {
794                                 setBlob(preparedStatement, valueIndex, object);
795                         } else if (type.equalsIgnoreCase("Clob")) {
796                                 setClob(preparedStatement, valueIndex, object);
797                         } else {
798                                 setString(preparedStatement, valueIndex, object);
799                         }
800                 }
801
802                 private void setClob(PreparedStatement preparedStatement, int valueIndex, Object object)
803                         throws JDBCStatementCreatorException {
804                         if (object instanceof Clob) {
805                                 try {
806                                         preparedStatement.setClob(valueIndex, (Clob) object);
807                                         return;
808                                 } catch (SQLException e) {
809                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
810                                         throw new JDBCStatementCreatorException(
811                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
812                                 }
813                         }
814                         log.error("Encountered a dependency with an invalid java type.");
815                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
816                 }
817
818                 private void setBlob(PreparedStatement preparedStatement, int valueIndex, Object object)
819                         throws JDBCStatementCreatorException {
820                         if (object instanceof Blob) {
821                                 try {
822                                         preparedStatement.setBlob(valueIndex, (Blob) object);
823                                         return;
824                                 } catch (SQLException e) {
825                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
826                                         throw new JDBCStatementCreatorException(
827                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
828                                 }
829                         }
830                         log.error("Encountered a dependency with an invalid java type.");
831                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
832                 }
833
834                 private void setDate(PreparedStatement preparedStatement, int valueIndex, Object object)
835                         throws JDBCStatementCreatorException {
836
837                         if (object instanceof java.sql.Date) {
838                                 try {
839                                         preparedStatement.setDate(valueIndex, (java.sql.Date) object);
840                                         return;
841                                 } catch (SQLException e) {
842                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
843                                         throw new JDBCStatementCreatorException(
844                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
845                                 }
846                         } else if (object instanceof java.util.Date) {
847                                 try {
848                                         //If you want to be frustrated by the java class library, look no further...
849                                         preparedStatement.setDate(valueIndex, new java.sql.Date(((java.util.Date) object).getTime()));
850                                         return;
851                                 } catch (SQLException e) {
852                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
853                                         throw new JDBCStatementCreatorException(
854                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
855                                 }
856                         } else if (object instanceof Long) {
857                                 try {
858                                         preparedStatement.setDate(valueIndex, new java.sql.Date(((Long) object).longValue()));
859                                         return;
860                                 } catch (SQLException e) {
861                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
862                                         throw new JDBCStatementCreatorException(
863                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
864                                 }
865                         } else if (object instanceof String) {
866                                 try {
867                                         preparedStatement.setDate(
868                                                 valueIndex,
869                                                 new java.sql.Date(new SimpleDateFormat().parse((String) object).getTime()));
870                                         return;
871                                 } catch (Exception e) {
872                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
873                                         throw new JDBCStatementCreatorException(
874                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
875                                 }
876                         }
877                         log.error("Encountered a dependency with an invalid java type.");
878                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
879                 }
880
881                 private void setBoolean(PreparedStatement preparedStatement, int valueIndex, Object object)
882                         throws JDBCStatementCreatorException {
883                         if (object instanceof Boolean) {
884                                 try {
885                                         preparedStatement.setBoolean(valueIndex, ((Boolean) object).booleanValue());
886                                         return;
887                                 } catch (SQLException e) {
888                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
889                                         throw new JDBCStatementCreatorException(
890                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
891                                 }
892                         } else if (object instanceof String) {
893                                 try {
894                                         preparedStatement.setBoolean(valueIndex, new Boolean((String) object).booleanValue());
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 setShort(PreparedStatement preparedStatement, int valueIndex, Object object)
907                         throws JDBCStatementCreatorException {
908                         if (object instanceof Boolean) {
909                                 try {
910                                         preparedStatement.setShort(valueIndex, ((Short) object).shortValue());
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.setShort(valueIndex, new Short((String) object).shortValue());
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 setLong(PreparedStatement preparedStatement, int valueIndex, Object object)
932                         throws JDBCStatementCreatorException {
933                         if (object instanceof Long || object instanceof Integer || object instanceof Short) {
934                                 try {
935                                         preparedStatement.setLong(valueIndex, ((Number) object).longValue());
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.setLong(valueIndex, new Long((String) object).longValue());
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 setFloat(PreparedStatement preparedStatement, int valueIndex, Object object)
957                         throws JDBCStatementCreatorException {
958                         if (object instanceof Float) {
959                                 try {
960                                         preparedStatement.setFloat(valueIndex, ((Float) object).floatValue());
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.setFloat(valueIndex, new Float((String) object).floatValue());
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 setDouble(PreparedStatement preparedStatement, int valueIndex, Object object)
982                         throws JDBCStatementCreatorException {
983                         if (object instanceof Double || object instanceof Float) {
984                                 try {
985                                         preparedStatement.setDouble(valueIndex, ((Number) object).doubleValue());
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.setDouble(valueIndex, new Double((String) object).doubleValue());
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 setByte(PreparedStatement preparedStatement, int valueIndex, Object object)
1007                         throws JDBCStatementCreatorException {
1008                         if (object instanceof Byte) {
1009                                 try {
1010                                         preparedStatement.setByte(valueIndex, ((Byte) object).byteValue());
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.setByte(valueIndex, new Byte((String) object).byteValue());
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 setInteger(PreparedStatement preparedStatement, int valueIndex, Object object)
1032                         throws JDBCStatementCreatorException {
1033                         if (object instanceof Integer || object instanceof Short) {
1034                                 try {
1035                                         preparedStatement.setInt(valueIndex, ((Number) object).intValue());
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.setInt(valueIndex, new Integer((String) object).intValue());
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 setString(PreparedStatement preparedStatement, int valueIndex, Object object)
1057                         throws JDBCStatementCreatorException {
1058                         if (object instanceof String) {
1059                                 try {
1060                                         preparedStatement.setString(valueIndex, (String) object);
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                         }
1068                         log.error("Encountered a dependency with an invalid java type.");
1069                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1070                 }
1071         }
1072 }