View Javadoc

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