Added validation query so bad connections will drop.
[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                 objectPool.setTestOnBorrow(true);
187
188                 ConnectionFactory connFactory = null;
189                 PoolableConnectionFactory poolConnFactory = null;
190
191                 try {
192                         connFactory = new DriverManagerConnectionFactory(dbURL, props);
193                         log.debug("Connection factory initialized.");
194                 } catch (Exception ex) {
195                         log.error(
196                                 "Connection factory couldn't be initialized, ensure database URL, username and password are correct.");
197                         throw new ResolutionPlugInException("Connection factory couldn't be initialized: " + ex.getMessage());
198                 }
199
200                 try {
201                         poolConnFactory =
202                         new PoolableConnectionFactory(
203                                 connFactory,
204                                 objectPool,
205                                 new StackKeyedObjectPoolFactory(),
206                                 "select 1",
207                                 false,
208                                 true);
209                 } catch (Exception ex) {
210                         log.debug("Poolable connection factory error");
211                 }
212
213                 dataSource = new PoolingDataSource(objectPool);
214                 log.info("Data Source initialized.");
215                 try {
216                         dataSource.setLogWriter(
217                                 new Log4jPrintWriter(Logger.getLogger(JDBCDataConnector.class.getName() + ".Pool"), Priority.DEBUG));
218                 } catch (SQLException e) {
219                         log.error("Coudn't setup logger for database connection pool.");
220                 }
221         }
222
223         /**
224          * Instantiate an Attribute Extractor, using the default if none was configured
225          */
226         private void setupAttributeExtractor(Element config) throws ResolutionPlugInException {
227
228                 String className = null;
229                 if (config != null) {
230                         className = config.getAttribute("class");
231                 }
232                 if (className == null || className.equals("")) {
233                         log.debug("Using default Attribute Extractor.");
234                         className = DefaultAE.class.getName();
235                 }
236                 try {
237                         Class aeClass = Class.forName(className);
238                         extractor = (JDBCAttributeExtractor) aeClass.newInstance();
239                         log.debug("Attribute Extractor implementation loaded.");
240
241                 } catch (ClassNotFoundException e) {
242                         log.error("The supplied Attribute Extractor class could not be found: " + e);
243                         throw new ResolutionPlugInException(
244                                 "The supplied Attribute Extractor class could not be found: " + e.getMessage());
245                 } catch (Exception e) {
246                         log.error("Unable to instantiate Attribute Extractor implementation: " + e);
247                         throw new ResolutionPlugInException(
248                                 "Unable to instantiate Attribute Extractor implementation: " + e.getMessage());
249                 }
250         }
251
252         /**
253          * Instantiate a Statement Creator, using the default if none was configured
254          */
255         private void setupStatementCreator(Element config) throws ResolutionPlugInException {
256
257                 String scClassName = null;
258                 if (config != null) {
259                         scClassName = config.getAttribute("class");
260                 }
261                 if (scClassName == null || scClassName.equals("")) {
262                         log.debug("Using default Statement Creator.");
263                         scClassName = DefaultStatementCreator.class.getName();
264                 }
265                 try {
266                         Class scClass = Class.forName(scClassName);
267
268                         Class[] params = new Class[1];
269                         params[0] = Class.forName("org.w3c.dom.Element");
270                         try {
271                                 Constructor implementorConstructor = scClass.getConstructor(params);
272                                 Object[] args = new Object[1];
273                                 args[0] = config;
274                                 log.debug("Initializing Statement Creator of type (" + scClass.getName() + ").");
275                                 statementCreator = (JDBCStatementCreator) implementorConstructor.newInstance(args);
276                         } catch (NoSuchMethodException nsme) {
277                                 log.debug(
278                                         "Implementation constructor does have a parameterized constructor, attempting to load default.");
279                                 statementCreator = (JDBCStatementCreator) scClass.newInstance();
280                         }
281                         log.debug("Statement Creator implementation loaded.");
282
283                 } catch (ClassNotFoundException e) {
284                         log.error("The supplied Statement Creator class could not be found: " + e);
285                         throw new ResolutionPlugInException(
286                                 "The supplied Statement Creator class could not be found: " + e.getMessage());
287                 } catch (Exception e) {
288                         log.error("Unable to instantiate Statement Creator implementation: " + e);
289                         throw new ResolutionPlugInException(
290                                 "Unable to instantiate Statement Creator implementation: " + e.getMessage());
291                 }
292         }
293
294         public Attributes resolve(Principal principal, String requester, Dependencies depends)
295                 throws ResolutionPlugInException {
296
297                 log.debug("Resolving connector: (" + getId() + ")");
298
299                 //Retrieve a connection from the connection pool
300                 Connection conn = null;
301                 try {
302                         conn = dataSource.getConnection();
303                         log.debug("Connection retrieved from pool");
304                 } catch (Exception e) {
305                         log.error("Unable to fetch a connection from the pool");
306                         throw new ResolutionPlugInException("Unable to fetch a connection from the pool: " + e.getMessage());
307                 }
308                 if (conn == null) {
309                         log.error("Pool didn't return a propertly initialized connection.");
310                         throw new ResolutionPlugInException("Pool didn't return a properly initialized connection.");
311                 }
312
313                 //Setup and execute a (pooled) prepared statement
314                 ResultSet rs = null;
315                 PreparedStatement preparedStatement = null;
316                 try {
317                         preparedStatement = conn.prepareStatement(searchVal);
318                         statementCreator.create(preparedStatement, principal, requester, depends);
319                         rs = preparedStatement.executeQuery();
320                         if (!rs.next()) {
321                                 return new BasicAttributes();
322                         }
323             return extractor.extractAttributes(rs);
324                 } catch (JDBCStatementCreatorException e) {
325                         log.error("An ERROR occured while constructing the query");
326                         throw new ResolutionPlugInException("An ERROR occured while constructing the query: " + e.getMessage());
327         } catch (JDBCAttributeExtractorException e) {
328             log.error("An ERROR occured while extracting attributes from result set");
329             throw new ResolutionPlugInException("An ERROR occured while extracting attributes from result set: " + e.getMessage());
330                 } catch (SQLException e) {
331                         log.error("An ERROR occured while executing the query");
332                         throw new ResolutionPlugInException("An ERROR occured while executing the query: " + e.getMessage());
333         } finally {
334             Exception e_save = null;
335             try {
336                 if (preparedStatement != null) {
337                     preparedStatement.close();
338                 }
339             } catch (SQLException e) {
340                 log.error("An error occured while closing the prepared statement: " + e.getMessage());
341                 e_save = e;
342             }
343             try {
344                 if (rs != null) {
345                     rs.close();
346                 }
347             } catch (SQLException e) {
348                 log.error("An error occured while closing the result set: " + e.getMessage());
349                 e_save = e;
350             }
351             try {
352                 conn.close();
353             } catch (SQLException e) {
354                 log.error("An error occured while closing the database connection: " + e.getMessage());
355                 e_save = e;
356             }
357             if (e_save != null) {
358                 throw new ResolutionPlugInException("An error occured while closing database objects:" + e_save.getMessage());
359             }
360         }
361         }
362
363         /** 
364          * Loads the driver used to access the database
365          * @param driver The driver used to access the database
366          * @throws ResolutionPlugInException If there is a failure to load the driver
367          */
368         public void loadDriver(String driver) throws ResolutionPlugInException {
369                 try {
370                         Class.forName(driver).newInstance();
371                         log.debug("Loading JDBC driver: " + driver);
372                 } catch (Exception e) {
373                         log.error("An error loading database driver: " + e);
374                         throw new ResolutionPlugInException(
375                                 "An IllegalAccessException occured while loading database driver: " + e.getMessage());
376                 }
377                 log.debug("Driver loaded.");
378         }
379
380         private class Log4jPrintWriter extends PrintWriter {
381
382                 private Priority level;
383                 private Logger logger;
384                 private StringBuffer text = new StringBuffer("");
385
386                 private Log4jPrintWriter(Logger logger, org.apache.log4j.Priority level) {
387                         super(System.err);
388                         this.level = level;
389                         this.logger = logger;
390                 }
391
392                 public void close() {
393                         flush();
394                 }
395
396                 public void flush() {
397                         if (!text.toString().equals("")) {
398                                 logger.log(level, text.toString());
399                                 text.setLength(0);
400                         }
401                 }
402
403                 public void print(boolean b) {
404                         text.append(b);
405                 }
406
407                 public void print(char c) {
408                         text.append(c);
409                 }
410
411                 public void print(char[] s) {
412                         text.append(s);
413                 }
414
415                 public void print(double d) {
416                         text.append(d);
417                 }
418
419                 public void print(float f) {
420                         text.append(f);
421                 }
422
423                 public void print(int i) {
424                         text.append(i);
425                 }
426
427                 public void print(long l) {
428                         text.append(l);
429                 }
430
431                 public void print(Object obj) {
432                         text.append(obj);
433                 }
434
435                 public void print(String s) {
436                         text.append(s);
437                 }
438
439                 public void println() {
440                         if (!text.toString().equals("")) {
441                                 logger.log(level, text.toString());
442                                 text.setLength(0);
443                         }
444                 }
445
446                 public void println(boolean x) {
447                         text.append(x);
448                         logger.log(level, text.toString());
449                         text.setLength(0);
450                 }
451
452                 public void println(char x) {
453                         text.append(x);
454                         logger.log(level, text.toString());
455                         text.setLength(0);
456                 }
457
458                 public void println(char[] x) {
459                         text.append(x);
460                         logger.log(level, text.toString());
461                         text.setLength(0);
462                 }
463
464                 public void println(double x) {
465                         text.append(x);
466                         logger.log(level, text.toString());
467                         text.setLength(0);
468                 }
469
470                 public void println(float x) {
471                         text.append(x);
472                         logger.log(level, text.toString());
473                         text.setLength(0);
474                 }
475
476                 public void println(int x) {
477                         text.append(x);
478                         logger.log(level, text.toString());
479                         text.setLength(0);
480                 }
481
482                 public void println(long x) {
483                         text.append(x);
484                         logger.log(level, text.toString());
485                         text.setLength(0);
486                 }
487
488                 public void println(Object x) {
489                         text.append(x);
490                         logger.log(level, text.toString());
491                         text.setLength(0);
492                 }
493
494                 public void println(String x) {
495                         text.append(x);
496                         logger.log(level, text.toString());
497                         text.setLength(0);
498                 }
499         }
500 }
501
502 /**
503  * The default attribute extractor. 
504  */
505 class DefaultAE implements JDBCAttributeExtractor {
506
507         private static Logger log = Logger.getLogger(DefaultAE.class.getName());
508
509         /**
510          * Method of extracting the attributes from the supplied result set.
511          *
512          * @param ResultSet The result set from the query which contains the attributes
513          * @return BasicAttributes as objects containing all the attributes
514          * @throws JDBCAttributeExtractorException If there is a complication in retrieving the attributes
515          */
516         public BasicAttributes extractAttributes(ResultSet rs) throws JDBCAttributeExtractorException {
517                 BasicAttributes attributes = new BasicAttributes();
518         int row = 0;
519         
520                 try {
521             // Get metadata about result set.
522             ResultSetMetaData rsmd = rs.getMetaData();
523             int numColumns = rsmd.getColumnCount();
524             log.debug("Number of returned columns: " + numColumns);
525
526             do {
527                 for (int i = 1; i <= numColumns; i++) {
528                     String columnName = rsmd.getColumnName(i);
529                     Object columnValue = rs.getObject(columnName);
530                     if (log.isDebugEnabled()) {
531                         log.debug(
532                             "("
533                                 + i
534                                 + ". ColumnType = " + rsmd.getColumnTypeName(i)
535                                 + ") "
536                                 + columnName
537                                 + " -> "
538                                 + (columnValue != null ? columnValue.toString() : "(null)"));
539                     }
540                     if (row == 0) {
541                         BasicAttribute ba = new BasicAttribute(columnName, true);
542                         ba.add(row,columnValue);
543                         attributes.put(ba);
544                     }
545                     else {
546                         attributes.get(columnName).add(row,columnValue);
547                     }
548                 }
549                 row++;
550             } while (rs.next());
551                 } catch (SQLException e) {
552                         log.error("An ERROR occured while processing result set");
553                         throw new JDBCAttributeExtractorException(
554                                 "An ERROR occured while processing result set: " + e.getMessage());
555                 }
556
557                 return attributes;
558         }
559 }
560
561 class DefaultStatementCreator implements JDBCStatementCreator {
562
563         private static Logger log = Logger.getLogger(DefaultStatementCreator.class.getName());
564
565         public void create(
566                 PreparedStatement preparedStatement,
567                 Principal principal,
568                 String requester,
569                 Dependencies depends)
570                 throws JDBCStatementCreatorException {
571
572                 try {
573                         log.debug("Creating prepared statement.  Substituting principal: (" + principal.getName() + ")");
574                         preparedStatement.setString(1, principal.getName());
575             //Tried using ParameterMetaData to determine param count, but it fails, so...
576             try {
577                 int i=2;
578                 while (true) {
579                     preparedStatement.setString(i++, principal.getName());
580                 }
581             } catch (SQLException e) {
582                 //Ignore any additional exceptions, assume parameters simply don't exist.
583             }
584                 } catch (SQLException e) {
585                         log.error("Encountered an error while creating prepared statement: " + e);
586                         throw new JDBCStatementCreatorException(
587                                 "Encountered an error while creating prepared statement: " + e.getMessage());
588                 }
589         }
590 }
591
592 class DependencyStatementCreator implements JDBCStatementCreator {
593
594         private static Logger log = Logger.getLogger(DependencyStatementCreator.class.getName());
595         private ArrayList parameters = new ArrayList();
596
597         public DependencyStatementCreator(Element conf) throws JDBCStatementCreatorException {
598
599                 NodeList nodes = conf.getElementsByTagName("Parameter");
600                 for (int i = 0; i < nodes.getLength(); i++) {
601                         Element parameter = (Element) nodes.item(i);
602                         String type = "String";
603                         if (parameter.getAttribute("type") != null && (!parameter.getAttribute("type").equals(""))) {
604                                 type = parameter.getAttribute("type");
605                         }
606
607                         if (parameter.getAttribute("attributeName") == null
608                                 || parameter.getAttribute("attributeName").equals("")) {
609                                 log.error("Statement Creator Parameter must reference an attribute by name.");
610                                 throw new JDBCStatementCreatorException("Statement Creator Parameter must reference an attribute by name.");
611                         }
612
613                         if (parameter.getAttribute("connectorId") != null && (!parameter.getAttribute("connectorId").equals(""))) {
614                                 parameters.add(
615                                         new Parameter(
616                                                 type,
617                                                 parameter.getAttribute("attributeName"),
618                                                 parameter.getAttribute("connectorId")));
619                         } else {
620                                 parameters.add(new Parameter(type, parameter.getAttribute("attributeName")));
621
622                         }
623
624                         if (parameter.getAttribute("nullMissing") != null && (!parameter.getAttribute("nullMissing").equals(""))) {
625                                 if (parameter.getAttribute("nullMissing").equalsIgnoreCase("FALSE")) {
626                                         ((Parameter) parameters.get(i)).setNullMissing(false);
627                                 }
628                         }
629                 }
630                 log.debug("Parameters configured: " + parameters.size());
631         }
632
633         public void create(
634                 PreparedStatement preparedStatement,
635                 Principal principal,
636                 String requester,
637                 Dependencies depends)
638                 throws JDBCStatementCreatorException {
639
640                 try {
641                         log.debug("Creating prepared statement.  Substituting values from dependencies.");
642                         for (int i = 0; i < parameters.size(); i++) {
643                                 ((Parameter) parameters.get(i)).setParameterValue(preparedStatement, i + 1, depends);
644                         }
645
646                 } catch (Exception e) {
647                         log.error("Encountered an error while creating prepared statement: " + e);
648                         throw new JDBCStatementCreatorException(
649                                 "Encountered an error while creating prepared statement: " + e.getMessage());
650                 }
651         }
652
653         protected class Parameter {
654                 private String type;
655                 private String attributeName;
656                 private boolean referencesConnector = false;
657                 private String connectorId;
658                 private boolean nullMissing = true;
659
660                 protected Parameter(String type, String attributeName) throws JDBCStatementCreatorException {
661                         if ((!type.equalsIgnoreCase("String"))
662                                 && (!type.equalsIgnoreCase("Integer"))
663                                 && (!type.equalsIgnoreCase("Byte"))
664                                 && (!type.equalsIgnoreCase("Double"))
665                                 && (!type.equalsIgnoreCase("Float"))
666                                 && (!type.equalsIgnoreCase("Long"))
667                                 && (!type.equalsIgnoreCase("Short"))
668                                 && (!type.equalsIgnoreCase("Boolean"))
669                                 && (!type.equalsIgnoreCase("Date"))
670                                 && (!type.equalsIgnoreCase("Blob"))
671                                 && (!type.equalsIgnoreCase("Clob"))) {
672                                 log.error("Unsupported type configured for Statement Creator Parameter.");
673                                 throw new JDBCStatementCreatorException("Unsupported type on Statement Creator Parameter.");
674                         }
675                         this.type = type;
676                         this.attributeName = attributeName;
677                 }
678
679                 protected Parameter(String type, String attributeName, String connectorId)
680                         throws JDBCStatementCreatorException {
681                         this(type, attributeName);
682                         referencesConnector = true;
683                         this.connectorId = connectorId;
684
685                 }
686         
687         protected int getSQLType() {
688             if (type.equalsIgnoreCase("String")) {
689                 return Types.VARCHAR;
690             } else if (type.equalsIgnoreCase("Integer")) {
691                 return Types.INTEGER;
692             } else if (type.equalsIgnoreCase("Byte")) {
693                 return Types.TINYINT;
694             } else if (type.equalsIgnoreCase("Double")) {
695                 return Types.DOUBLE;
696             } else if (type.equalsIgnoreCase("Float")) {
697                 return Types.FLOAT;
698             } else if (type.equalsIgnoreCase("Long")) {
699                 return Types.INTEGER;
700             } else if (type.equalsIgnoreCase("Short")) {
701                 return Types.SMALLINT;
702             } else if (type.equalsIgnoreCase("Boolean")) {
703                 return Types.BOOLEAN;
704             } else if (type.equalsIgnoreCase("Date")) {
705                 return Types.DATE;
706             } else if (type.equalsIgnoreCase("Blob")) {
707                 return Types.BLOB;
708             } else if (type.equalsIgnoreCase("Clob")) {
709                 return Types.CLOB;
710             } else {
711                 return Types.VARCHAR;
712             }
713         }
714
715                 protected void setParameterValue(PreparedStatement preparedStatement, int valueIndex, Dependencies depends)
716                         throws JDBCStatementCreatorException {
717
718                         //handle values from DataConnectors
719                         if (referencesConnector) {
720                                 Attributes attributes = depends.getConnectorResolution(connectorId);
721                                 if (attributes == null) {
722                                         log.error(
723                                                 "Statement Creator misconfiguration: Connector ("
724                                                         + connectorId
725                                                         + ") is not a dependency of this JDBCDataConnector.");
726                                         throw new JDBCStatementCreatorException(
727                                                 "Statement Creator misconfiguration: Connector ("
728                                                         + connectorId
729                                                         + ") is not a dependency of this JDBCDataConnector.");
730                                 }
731
732                                 Attribute attribute = attributes.get(attributeName);
733                                 if (attribute == null || attribute.size() < 1) {
734                                         if (nullMissing) {
735                                                 try {
736                                                         preparedStatement.setNull(valueIndex, getSQLType());
737                                                         return;
738                                                 } catch (SQLException e) {
739                                                         log.error(
740                                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
741                                                 }
742                                         }
743                                         log.error("Cannot parameterize prepared statement: missing dependency value.");
744                                         throw new JDBCStatementCreatorException("Cannot parameterize prepared statement: missing dependency value.");
745                                 }
746
747                                 if (attribute.size() > 1) {
748                                         log.error("Statement Creator encountered a multivalued dependent attribute.");
749                                         throw new JDBCStatementCreatorException("Statement Creator encountered a multivalued dependent attribute.");
750                                 }
751
752                                 try {
753                                         setSpecificParameter(preparedStatement, valueIndex, attribute.get());
754                                         return;
755                                 } catch (NamingException e) {
756                                         log.error(
757                                                 "Statement Creator encountered an error while extracting attributes from a Data Conector: "
758                                                         + e);
759                                         throw new JDBCStatementCreatorException(
760                                                 "Statement Creator encountered an error while extracting attributes from a Data Conector: "
761                                                         + e.getMessage());
762                                 }
763                         }
764
765                         //handle values from AttributeDefinitons
766                         ResolverAttribute attribute = depends.getAttributeResolution(attributeName);
767                         if (attribute != null) {
768                                 Iterator iterator = attribute.getValues();
769                                 if (iterator.hasNext()) {
770                                         setSpecificParameter(preparedStatement, valueIndex, iterator.next());
771                                         if (iterator.hasNext()) {
772                                                 log.error("Statement Creator encountered a multivalued dependent attribute.");
773                                                 throw new JDBCStatementCreatorException("Statement Creator encountered a multivalued dependent attribute.");
774                                         }
775                                         return;
776                                 }
777                         }
778                         if (nullMissing) {
779                                 try {
780                                         preparedStatement.setNull(valueIndex, getSQLType());
781                                         return;
782                                 } catch (SQLException e) {
783                                         log.error(
784                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
785                                 }
786                         }
787                         log.error("Cannot parameterize prepared statement: missing dependency value.");
788                         throw new JDBCStatementCreatorException("Cannot parameterize prepared statement: missing dependency value.");
789                 }
790
791                 protected void setNullMissing(boolean nullMissing) {
792                         this.nullMissing = nullMissing;
793                 }
794
795                 private void setSpecificParameter(PreparedStatement preparedStatement, int valueIndex, Object object)
796                         throws JDBCStatementCreatorException {
797
798                         if (object == null) {
799                                 try {
800                                         preparedStatement.setNull(valueIndex, getSQLType());
801                                         return;
802                                 } catch (SQLException e) {
803                                         log.error(
804                                                 "Encountered a problem while attempting to convert missing attribute value to null parameter.");
805                                         throw new JDBCStatementCreatorException("Encountered a problem while attempting to convert missing attribute value to null parameter.");
806                                 }
807                         } else if (type.equalsIgnoreCase("String")) {
808                                 setString(preparedStatement, valueIndex, object);
809                         } else if (type.equalsIgnoreCase("Integer")) {
810                                 setInteger(preparedStatement, valueIndex, object);
811                         } else if (type.equalsIgnoreCase("Byte")) {
812                                 setByte(preparedStatement, valueIndex, object);
813                         } else if (type.equalsIgnoreCase("Double")) {
814                                 setDouble(preparedStatement, valueIndex, object);
815                         } else if (type.equalsIgnoreCase("Float")) {
816                                 setFloat(preparedStatement, valueIndex, object);
817                         } else if (type.equalsIgnoreCase("Long")) {
818                                 setLong(preparedStatement, valueIndex, object);
819                         } else if (type.equalsIgnoreCase("Short")) {
820                                 setShort(preparedStatement, valueIndex, object);
821                         } else if (type.equalsIgnoreCase("Boolean")) {
822                                 setBoolean(preparedStatement, valueIndex, object);
823                         } else if (type.equalsIgnoreCase("Date")) {
824                                 setDate(preparedStatement, valueIndex, object);
825                         } else if (type.equalsIgnoreCase("Blob")) {
826                                 setBlob(preparedStatement, valueIndex, object);
827                         } else if (type.equalsIgnoreCase("Clob")) {
828                                 setClob(preparedStatement, valueIndex, object);
829                         } else {
830                                 setString(preparedStatement, valueIndex, object);
831                         }
832                 }
833
834                 private void setClob(PreparedStatement preparedStatement, int valueIndex, Object object)
835                         throws JDBCStatementCreatorException {
836                         if (object instanceof Clob) {
837                                 try {
838                                         preparedStatement.setClob(valueIndex, (Clob) object);
839                                         return;
840                                 } catch (SQLException e) {
841                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
842                                         throw new JDBCStatementCreatorException(
843                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
844                                 }
845                         }
846                         log.error("Encountered a dependency with an invalid java type.");
847                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
848                 }
849
850                 private void setBlob(PreparedStatement preparedStatement, int valueIndex, Object object)
851                         throws JDBCStatementCreatorException {
852                         if (object instanceof Blob) {
853                                 try {
854                                         preparedStatement.setBlob(valueIndex, (Blob) object);
855                                         return;
856                                 } catch (SQLException e) {
857                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
858                                         throw new JDBCStatementCreatorException(
859                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
860                                 }
861                         }
862                         log.error("Encountered a dependency with an invalid java type.");
863                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
864                 }
865
866                 private void setDate(PreparedStatement preparedStatement, int valueIndex, Object object)
867                         throws JDBCStatementCreatorException {
868
869                         if (object instanceof java.sql.Date) {
870                                 try {
871                                         preparedStatement.setDate(valueIndex, (java.sql.Date) object);
872                                         return;
873                                 } catch (SQLException e) {
874                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
875                                         throw new JDBCStatementCreatorException(
876                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
877                                 }
878                         } else if (object instanceof java.util.Date) {
879                                 try {
880                                         //If you want to be frustrated by the java class library, look no further...
881                                         preparedStatement.setDate(valueIndex, new java.sql.Date(((java.util.Date) object).getTime()));
882                                         return;
883                                 } catch (SQLException e) {
884                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
885                                         throw new JDBCStatementCreatorException(
886                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
887                                 }
888                         } else if (object instanceof Long) {
889                                 try {
890                                         preparedStatement.setDate(valueIndex, new java.sql.Date(((Long) object).longValue()));
891                                         return;
892                                 } catch (SQLException e) {
893                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
894                                         throw new JDBCStatementCreatorException(
895                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
896                                 }
897                         } else if (object instanceof String) {
898                                 try {
899                                         preparedStatement.setDate(
900                                                 valueIndex,
901                                                 new java.sql.Date(new SimpleDateFormat().parse((String) object).getTime()));
902                                         return;
903                                 } catch (Exception e) {
904                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
905                                         throw new JDBCStatementCreatorException(
906                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
907                                 }
908                         }
909                         log.error("Encountered a dependency with an invalid java type.");
910                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
911                 }
912
913                 private void setBoolean(PreparedStatement preparedStatement, int valueIndex, Object object)
914                         throws JDBCStatementCreatorException {
915                         if (object instanceof Boolean) {
916                                 try {
917                                         preparedStatement.setBoolean(valueIndex, ((Boolean) object).booleanValue());
918                                         return;
919                                 } catch (SQLException e) {
920                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
921                                         throw new JDBCStatementCreatorException(
922                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
923                                 }
924                         } else if (object instanceof String) {
925                                 try {
926                                         preparedStatement.setBoolean(valueIndex, new Boolean((String) object).booleanValue());
927                                         return;
928                                 } catch (Exception e) {
929                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
930                                         throw new JDBCStatementCreatorException(
931                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
932                                 }
933                         }
934                         log.error("Encountered a dependency with an invalid java type.");
935                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
936                 }
937
938                 private void setShort(PreparedStatement preparedStatement, int valueIndex, Object object)
939                         throws JDBCStatementCreatorException {
940                         if (object instanceof Boolean) {
941                                 try {
942                                         preparedStatement.setShort(valueIndex, ((Short) object).shortValue());
943                                         return;
944                                 } catch (SQLException e) {
945                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
946                                         throw new JDBCStatementCreatorException(
947                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
948                                 }
949                         } else if (object instanceof String) {
950                                 try {
951                                         preparedStatement.setShort(valueIndex, new Short((String) object).shortValue());
952                                         return;
953                                 } catch (Exception e) {
954                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
955                                         throw new JDBCStatementCreatorException(
956                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
957                                 }
958                         }
959                         log.error("Encountered a dependency with an invalid java type.");
960                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
961                 }
962
963                 private void setLong(PreparedStatement preparedStatement, int valueIndex, Object object)
964                         throws JDBCStatementCreatorException {
965                         if (object instanceof Long || object instanceof Integer || object instanceof Short) {
966                                 try {
967                                         preparedStatement.setLong(valueIndex, ((Number) object).longValue());
968                                         return;
969                                 } catch (SQLException e) {
970                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
971                                         throw new JDBCStatementCreatorException(
972                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
973                                 }
974                         } else if (object instanceof String) {
975                                 try {
976                                         preparedStatement.setLong(valueIndex, new Long((String) object).longValue());
977                                         return;
978                                 } catch (Exception e) {
979                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
980                                         throw new JDBCStatementCreatorException(
981                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
982                                 }
983                         }
984                         log.error("Encountered a dependency with an invalid java type.");
985                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
986                 }
987
988                 private void setFloat(PreparedStatement preparedStatement, int valueIndex, Object object)
989                         throws JDBCStatementCreatorException {
990                         if (object instanceof Float) {
991                                 try {
992                                         preparedStatement.setFloat(valueIndex, ((Float) object).floatValue());
993                                         return;
994                                 } catch (SQLException e) {
995                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
996                                         throw new JDBCStatementCreatorException(
997                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
998                                 }
999                         } else if (object instanceof String) {
1000                                 try {
1001                                         preparedStatement.setFloat(valueIndex, new Float((String) object).floatValue());
1002                                         return;
1003                                 } catch (Exception e) {
1004                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1005                                         throw new JDBCStatementCreatorException(
1006                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1007                                 }
1008                         }
1009                         log.error("Encountered a dependency with an invalid java type.");
1010                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1011                 }
1012
1013                 private void setDouble(PreparedStatement preparedStatement, int valueIndex, Object object)
1014                         throws JDBCStatementCreatorException {
1015                         if (object instanceof Double || object instanceof Float) {
1016                                 try {
1017                                         preparedStatement.setDouble(valueIndex, ((Number) object).doubleValue());
1018                                         return;
1019                                 } catch (SQLException e) {
1020                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1021                                         throw new JDBCStatementCreatorException(
1022                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1023                                 }
1024                         } else if (object instanceof String) {
1025                                 try {
1026                                         preparedStatement.setDouble(valueIndex, new Double((String) object).doubleValue());
1027                                         return;
1028                                 } catch (Exception e) {
1029                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1030                                         throw new JDBCStatementCreatorException(
1031                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1032                                 }
1033                         }
1034                         log.error("Encountered a dependency with an invalid java type.");
1035                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1036                 }
1037
1038                 private void setByte(PreparedStatement preparedStatement, int valueIndex, Object object)
1039                         throws JDBCStatementCreatorException {
1040                         if (object instanceof Byte) {
1041                                 try {
1042                                         preparedStatement.setByte(valueIndex, ((Byte) object).byteValue());
1043                                         return;
1044                                 } catch (SQLException e) {
1045                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1046                                         throw new JDBCStatementCreatorException(
1047                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1048                                 }
1049                         } else if (object instanceof String) {
1050                                 try {
1051                                         preparedStatement.setByte(valueIndex, new Byte((String) object).byteValue());
1052                                         return;
1053                                 } catch (Exception e) {
1054                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1055                                         throw new JDBCStatementCreatorException(
1056                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1057                                 }
1058                         }
1059                         log.error("Encountered a dependency with an invalid java type.");
1060                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1061                 }
1062
1063                 private void setInteger(PreparedStatement preparedStatement, int valueIndex, Object object)
1064                         throws JDBCStatementCreatorException {
1065                         if (object instanceof Integer || object instanceof Short) {
1066                                 try {
1067                                         preparedStatement.setInt(valueIndex, ((Number) object).intValue());
1068                                         return;
1069                                 } catch (SQLException e) {
1070                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1071                                         throw new JDBCStatementCreatorException(
1072                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1073                                 }
1074                         } else if (object instanceof String) {
1075                                 try {
1076                                         preparedStatement.setInt(valueIndex, new Integer((String) object).intValue());
1077                                         return;
1078                                 } catch (Exception e) {
1079                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1080                                         throw new JDBCStatementCreatorException(
1081                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1082                                 }
1083                         }
1084                         log.error("Encountered a dependency with an invalid java type.");
1085                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1086                 }
1087
1088                 private void setString(PreparedStatement preparedStatement, int valueIndex, Object object)
1089                         throws JDBCStatementCreatorException {
1090                         if (object instanceof String) {
1091                                 try {
1092                                         preparedStatement.setString(valueIndex, (String) object);
1093                                         return;
1094                                 } catch (SQLException e) {
1095                                         log.error("Encountered an error while adding parameter to prepared statement: " + e);
1096                                         throw new JDBCStatementCreatorException(
1097                                                 "Encountered an error while adding parameter to prepared statement: " + e.getMessage());
1098                                 }
1099                         }
1100                         log.error("Encountered a dependency with an invalid java type.");
1101                         throw new JDBCStatementCreatorException("Encountered a dependency with an invalid java type.");
1102                 }
1103         }
1104 }