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