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