View Javadoc

1   /*
2    * Copyright 2007 University Corporation for Advanced Internet Development, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package edu.internet2.middleware.shibboleth.common.config.attribute.resolver.dataConnector;
18  
19  import java.beans.PropertyVetoException;
20  import java.util.Hashtable;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.naming.InitialContext;
25  import javax.naming.NamingException;
26  import javax.sql.DataSource;
27  import javax.xml.namespace.QName;
28  
29  import org.opensaml.xml.util.DatatypeHelper;
30  import org.opensaml.xml.util.XMLHelper;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  import org.springframework.beans.factory.BeanCreationException;
34  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
35  import org.springframework.beans.factory.xml.ParserContext;
36  import org.w3c.dom.Element;
37  
38  import com.mchange.v2.c3p0.ComboPooledDataSource;
39  
40  import edu.internet2.middleware.shibboleth.common.config.SpringConfigurationUtils;
41  
42  /**
43   * Spring bean definition parser for stored ID data connector.
44   */
45  public class StoredIDDataConnectorBeanDefinitionParser extends BaseDataConnectorBeanDefinitionParser {
46  
47      /** Schema type name. */
48      public static final QName TYPE_NAME = new QName(DataConnectorNamespaceHandler.NAMESPACE, "StoredId");
49  
50      /** Class logger. */
51      private final Logger log = LoggerFactory.getLogger(StoredIDDataConnectorBeanDefinitionParser.class);
52  
53      /** {@inheritDoc} */
54      protected Class getBeanClass(Element element) {
55          return StoredIDDataConnectorBeanFactory.class;
56      }
57  
58      /** {@inheritDoc} */
59      protected void doParse(String pluginId, Element pluginConfig, Map<QName, List<Element>> pluginConfigChildren,
60              BeanDefinitionBuilder pluginBuilder, ParserContext parserContext) {
61          super.doParse(pluginId, pluginConfig, pluginConfigChildren, pluginBuilder, parserContext);
62  
63          processConnectionManagement(pluginId, pluginConfig, pluginConfigChildren, pluginBuilder, parserContext);
64  
65          long queryTimeout = 5 * 1000;
66          if (pluginConfig.hasAttributeNS(null, "queryTimeout")) {
67              queryTimeout = SpringConfigurationUtils.parseDurationToMillis(
68                      "queryTimeout on relational database connector " + pluginId, pluginConfig.getAttributeNS(null,
69                              "queryTimeout"), 0);
70          }
71          log.debug("Data connector {} SQL query timeout: {}ms", queryTimeout);
72          pluginBuilder.addPropertyValue("queryTimeout", queryTimeout);
73  
74          String generatedAttributeId = "storedId";
75          if (pluginConfig.hasAttributeNS(null, "generatedAttributeID")) {
76              generatedAttributeId = DatatypeHelper.safeTrimOrNullString(pluginConfig.getAttributeNS(null,
77                      "generatedAttributeID"));
78          }
79          pluginBuilder.addPropertyValue("generatedAttribute", generatedAttributeId);
80          log.debug("Data connector {} generated attribute ID: {}", pluginId, generatedAttributeId);
81  
82          String sourceAttribute = DatatypeHelper.safeTrimOrNullString(pluginConfig.getAttributeNS(null,
83                  "sourceAttributeID"));
84          log.debug("Data connector {} source attribute ID: {}", pluginId, sourceAttribute);
85          pluginBuilder.addPropertyValue("sourceAttribute", sourceAttribute);
86  
87          String salt = DatatypeHelper.safeTrimOrNullString(pluginConfig.getAttributeNS(null, "salt"));
88          log.debug("Data connector {} salt: {}", pluginId, salt);
89          pluginBuilder.addPropertyValue("salt", salt.getBytes());
90      }
91  
92      /**
93       * Processes the connection management configuration.
94       * 
95       * @param pluginId ID of this data connector
96       * @param pluginConfig configuration element for this data connector
97       * @param pluginConfigChildren configuration elements for this connector
98       * @param pluginBuilder bean definition builder
99       * @param parserContext current configuration parsing context
100      */
101     protected void processConnectionManagement(String pluginId, Element pluginConfig,
102             Map<QName, List<Element>> pluginConfigChildren, BeanDefinitionBuilder pluginBuilder,
103             ParserContext parserContext) {
104         DataSource datasource;
105 
106         List<Element> cmc = pluginConfigChildren.get(new QName(DataConnectorNamespaceHandler.NAMESPACE,
107                 "ContainerManagedConnection"));
108         if (cmc != null && cmc.get(0) != null) {
109             datasource = buildContainerManagedConnection(pluginId, cmc.get(0));
110         } else {
111             datasource = buildApplicationManagedConnection(pluginId, pluginConfigChildren.get(
112                     new QName(DataConnectorNamespaceHandler.NAMESPACE, "ApplicationManagedConnection")).get(0));
113         }
114 
115         pluginBuilder.addPropertyValue("datasource", datasource);
116     }
117 
118     /**
119      * Builds a JDBC {@link DataSource} from a ContainerManagedConnection configuration element.
120      * 
121      * @param pluginId ID of this data connector
122      * @param cmc the container managed configuration element
123      * 
124      * @return the built data source
125      */
126     protected DataSource buildContainerManagedConnection(String pluginId, Element cmc) {
127         String jndiResource = cmc.getAttributeNS(null, "resourceName");
128         jndiResource = DatatypeHelper.safeTrim(jndiResource);
129 
130         Hashtable<String, String> initCtxProps = buildProperties(XMLHelper.getChildElementsByTagNameNS(cmc,
131                 DataConnectorNamespaceHandler.NAMESPACE, "JNDIConnectionProperty"));
132         try {
133             InitialContext initCtx = new InitialContext(initCtxProps);
134             DataSource dataSource = (DataSource) initCtx.lookup(jndiResource);
135             if (dataSource == null) {
136                 log.error("DataSource " + jndiResource + " did not exist in JNDI directory");
137                 throw new BeanCreationException("DataSource " + jndiResource + " did not exist in JNDI directory");
138             }
139             if (log.isDebugEnabled()) {
140                 log.debug("Retrieved data source for data connector {} from JNDI location {} using properties ",
141                         pluginId, initCtxProps);
142             }
143             return dataSource;
144         } catch (NamingException e) {
145             log.error("Unable to retrieve data source for data connector " + pluginId + " from JNDI location "
146                     + jndiResource + " using properties " + initCtxProps, e);
147             return null;
148         }
149     }
150 
151     /**
152      * Builds a JDBC {@link DataSource} from an ApplicationManagedConnection configuration element.
153      * 
154      * @param pluginId ID of this data connector
155      * @param amc the application managed configuration element
156      * 
157      * @return the built data source
158      */
159     protected DataSource buildApplicationManagedConnection(String pluginId, Element amc) {
160         ComboPooledDataSource datasource = new ComboPooledDataSource();
161 
162         String driverClass = DatatypeHelper.safeTrim(amc.getAttributeNS(null, "jdbcDriver"));
163         ClassLoader classLoader = this.getClass().getClassLoader();
164         try {
165             classLoader.loadClass(driverClass);
166         } catch (ClassNotFoundException e) {
167             log.error("Unable to create relational database connector, JDBC driver can not be found on the classpath");
168             throw new BeanCreationException(
169                     "Unable to create relational database connector, JDBC driver can not be found on the classpath");
170         }
171 
172         try {
173             datasource.setDriverClass(driverClass);
174             datasource.setJdbcUrl(DatatypeHelper.safeTrim(amc.getAttributeNS(null, "jdbcURL")));
175             datasource.setUser(DatatypeHelper.safeTrim(amc.getAttributeNS(null, "jdbcUserName")));
176             datasource.setPassword(DatatypeHelper.safeTrim(amc.getAttributeNS(null, "jdbcPassword")));
177 
178             if (amc.hasAttributeNS(null, "poolAcquireIncrement")) {
179                 datasource.setAcquireIncrement(Integer.parseInt(DatatypeHelper.safeTrim(amc.getAttributeNS(null,
180                         "poolAcquireIncrement"))));
181             } else {
182                 datasource.setAcquireIncrement(3);
183             }
184 
185             if (amc.hasAttributeNS(null, "poolAcquireRetryAttempts")) {
186                 datasource.setAcquireRetryAttempts(Integer.parseInt(DatatypeHelper.safeTrim(amc.getAttributeNS(null,
187                         "poolAcquireRetryAttempts"))));
188             } else {
189                 datasource.setAcquireRetryAttempts(36);
190             }
191 
192             if (amc.hasAttributeNS(null, "poolAcquireRetryDelay")) {
193                 datasource.setAcquireRetryDelay(Integer.parseInt(DatatypeHelper.safeTrim(amc.getAttributeNS(null,
194                         "poolAcquireRetryDelay"))));
195             } else {
196                 datasource.setAcquireRetryDelay(5000);
197             }
198 
199             if (amc.hasAttributeNS(null, "poolBreakAfterAcquireFailure")) {
200                 datasource.setBreakAfterAcquireFailure(XMLHelper.getAttributeValueAsBoolean(amc.getAttributeNodeNS(
201                         null, "poolBreakAfterAcquireFailure")));
202             } else {
203                 datasource.setBreakAfterAcquireFailure(true);
204             }
205 
206             if (amc.hasAttributeNS(null, "poolMinSize")) {
207                 datasource.setMinPoolSize(Integer.parseInt(DatatypeHelper.safeTrim(amc.getAttributeNS(null,
208                         "poolMinSize"))));
209             } else {
210                 datasource.setMinPoolSize(2);
211             }
212 
213             if (amc.hasAttributeNS(null, "poolMaxSize")) {
214                 datasource.setMaxPoolSize(Integer.parseInt(DatatypeHelper.safeTrim(amc.getAttributeNS(null,
215                         "poolMaxSize"))));
216             } else {
217                 datasource.setMaxPoolSize(50);
218             }
219 
220             if (amc.hasAttributeNS(null, "poolMaxIdleTime")) {
221                 datasource.setMaxIdleTime(Integer.parseInt(DatatypeHelper.safeTrim(amc.getAttributeNS(null,
222                         "poolMaxIdleTime"))));
223             } else {
224                 datasource.setMaxIdleTime(600);
225             }
226 
227             if (amc.hasAttributeNS(null, "poolIdleTestPeriod")) {
228                 datasource.setIdleConnectionTestPeriod(Integer.parseInt(DatatypeHelper.safeTrim(amc.getAttributeNS(
229                         null, "poolIdleTestPeriod"))));
230             } else {
231                 datasource.setIdleConnectionTestPeriod(180);
232             }
233 
234             datasource.setMaxStatementsPerConnection(10);
235 
236             log.debug("Created application managed data source for data connector {}", pluginId);
237             return datasource;
238         } catch (PropertyVetoException e) {
239             log.error("Unable to create data source for data connector {} with JDBC driver class {}", pluginId,
240                     driverClass);
241             return null;
242         }
243     }
244 
245     /**
246      * Builds a hash from PropertyType elements.
247      * 
248      * @param propertyElements properties elements
249      * 
250      * @return properties extracted from elements, key is the property name.
251      */
252     protected Hashtable<String, String> buildProperties(List<Element> propertyElements) {
253         if (propertyElements == null || propertyElements.size() < 1) {
254             return null;
255         }
256 
257         Hashtable<String, String> properties = new Hashtable<String, String>();
258 
259         String propName;
260         String propValue;
261         for (Element propertyElement : propertyElements) {
262             propName = DatatypeHelper.safeTrim(propertyElement.getAttributeNS(null, "name"));
263             propValue = DatatypeHelper.safeTrim(propertyElement.getAttributeNS(null, "value"));
264             properties.put(propName, propValue);
265         }
266 
267         return properties;
268     }
269 }