View Javadoc

1   /*
2    * Copyright 2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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  package org.kuali.rice.core.database;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import javax.naming.NamingException;
22  import javax.sql.DataSource;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.kuali.rice.core.config.Config;
26  import org.kuali.rice.core.config.ConfigContext;
27  import org.kuali.rice.core.config.ConfigurationException;
28  import org.kuali.rice.core.lifecycle.Lifecycle;
29  import org.kuali.rice.core.util.RiceConstants;
30  import org.springframework.beans.factory.config.AbstractFactoryBean;
31  import org.springframework.jndi.JndiTemplate;
32  
33  /**
34   * A class that can be used to load the primary datasource for a Rice module from an object in the Config system or from a
35   * JNDI url specified by the Configuration system. By default, it loads these values from the following properties if the
36   * <code>useNonTransactionalDataSource</code> param is not set or is set to false:
37   * <ul>
38   * <li>{@link RiceConstants#DATASOURCE_OBJ}</li>
39   * <li>{@link RiceConstants#DATASOURCE_JNDI}</li>
40   * </ul>
41   * If the <code>useNonTransactionalDataSource</code> param is set to true the following properties will be used:<br>
42   * <br>
43   * <ul>
44   * <li>{@link RiceConstants#NON_TRANSACTIONAL_DATASOURCE_OBJ}</li>
45   * <li>{@link RiceConstants#NON_TRANSACTIONAL_DATASOURCE_JNDI}</li>
46   * </ul>
47   * If the <code>server</code> param is set to true, the following properties will be added to the end of the preferred lists:<br>
48   * <br>
49   * <ul>
50   * <li>{@link RiceConstants#SERVER_DATASOURCE_OBJ}</li>
51   * <li>{@link RiceConstants#SERVER_DATASOURCE_JNDI}</li> 
52   * <p>
53   * The config properties checked can be overridden by setting values into the list parameters
54   * <code>preferredDataSourceParams</code> and <code>preferredDataSourceJndiParams</code>
55   * 
56   * @author Kuali Rice Team (rice.collab@kuali.org)
57   */
58  public class PrimaryDataSourceFactoryBean extends AbstractFactoryBean {
59  
60      private static final String DEFAULT_DATASOURCE_PARAM = RiceConstants.DATASOURCE_OBJ;
61      private static final String DEFAULT_SERVER_DATASOURCE_PARAM = RiceConstants.SERVER_DATASOURCE_OBJ;
62      private static final String DEFAULT_NONTRANSACTIONAL_DATASOURCE_PARAM = RiceConstants.NON_TRANSACTIONAL_DATASOURCE_OBJ;
63      private static final String DEFAULT_DATASOURCE_JNDI_PARAM = RiceConstants.DATASOURCE_JNDI;
64      private static final String DEFAULT_SERVER_DATASOURCE_JNDI_PARAM = RiceConstants.SERVER_DATASOURCE_JNDI;
65      private static final String DEFAULT_NONTRANSACTIONAL_DATASOURCE_JNDI_PARAM = RiceConstants.NON_TRANSACTIONAL_DATASOURCE_JNDI;
66  
67      private JndiTemplate jndiTemplate;
68      private boolean nonTransactionalDataSource = false;
69      private String defaultDataSourceParam = DEFAULT_DATASOURCE_PARAM;
70      private String defaultNonTransactionalDataSourceParam = DEFAULT_NONTRANSACTIONAL_DATASOURCE_PARAM;
71      private String defaultDataSourceJndiParam = DEFAULT_DATASOURCE_JNDI_PARAM;
72      private String defaultNonTransactionalDataSourceJndiParam = DEFAULT_NONTRANSACTIONAL_DATASOURCE_JNDI_PARAM;
73      private List<String> preferredDataSourceParams = new ArrayList<String>();
74      private List<String> preferredDataSourceJndiParams = new ArrayList<String>();
75      private boolean serverDataSource = false;
76      private boolean nullAllowed = false;
77      
78      public PrimaryDataSourceFactoryBean() {
79          setSingleton(true);
80      }
81  
82      public Class<DataSource> getObjectType() {
83          return DataSource.class;
84      }
85      
86      @Override
87  	public void afterPropertiesSet() throws Exception {
88      	if (serverDataSource) {
89      		getPreferredDataSourceParams().add(DEFAULT_SERVER_DATASOURCE_PARAM);
90      		getPreferredDataSourceJndiParams().add(DEFAULT_SERVER_DATASOURCE_JNDI_PARAM);
91      	}
92      	super.afterPropertiesSet();
93  	}
94  
95  	@Override
96      protected Object createInstance() throws Exception {
97          Config config = ConfigContext.getCurrentContextConfig();
98          DataSource dataSource = createDataSource(config);
99          if (dataSource == null && !isNullAllowed()) {
100         	throw new ConfigurationException("Failed to configure the Primary Data Source.");
101         }
102         return dataSource;
103     }
104 
105     protected String getDefaultDataSourceParamByType() {
106         return (nonTransactionalDataSource) ? getDefaultNonTransactionalDataSourceParam() : getDefaultDataSourceParam();
107     }
108 
109     protected String getDefaultDataSourceJndiParamByType() {
110         return (nonTransactionalDataSource) ? getDefaultNonTransactionalDataSourceJndiParam() : getDefaultDataSourceJndiParam();
111     }
112 
113     protected DataSource createDataSource(Config config) throws Exception {
114         DataSource dataSource = loadPreferredDataSourceFromConfig(config);
115         if (dataSource == null) {
116 
117             Object dataSourceObject = config.getObject(getDefaultDataSourceParamByType());
118             if (dataSourceObject != null) {
119                 validateDataSource(getDefaultDataSourceParamByType(), dataSourceObject);
120                 dataSource = (DataSource) dataSourceObject;
121             } else {
122                 dataSource = getDataSourceFromJndi(config, getDefaultDataSourceJndiParamByType());
123             }
124         }
125         return dataSource;
126     }
127 
128     protected DataSource loadPreferredDataSourceFromConfig(Config config) {
129         for (String dataSourceParam : getPreferredDataSourceParams()) {
130             Object dataSource = config.getObject(dataSourceParam);
131             if (dataSource != null) {
132                 validateDataSource(dataSourceParam, dataSource);
133                 return (DataSource) dataSource;
134             }
135         }
136         if (this.jndiTemplate == null) {
137             this.jndiTemplate = new JndiTemplate();
138         }
139         for (String dataSourceJndiParam : getPreferredDataSourceJndiParams()) {
140             DataSource dataSource = getDataSourceFromJndi(config, dataSourceJndiParam);
141             if (dataSource != null) {
142                 return dataSource;
143             }
144         }
145         return null;
146     }
147 
148     protected void validateDataSource(String paramName, Object dataSourceObject) {
149         if (!(dataSourceObject instanceof DataSource)) {
150             throw new ConfigurationException("DataSource configured for parameter '" + paramName + "' was not an instance of DataSource.  Was instead " + dataSourceObject.getClass().getName());
151         }
152     }
153 
154     protected DataSource getDataSourceFromJndi(Config config, String dataSourceJndiParam) {
155         String jndiName = config.getProperty(dataSourceJndiParam);
156         if (!StringUtils.isBlank(jndiName)) {
157             try {
158                 Object dataSource = getJndiTemplate().lookup(jndiName, DataSource.class);
159                 if (dataSource != null) {
160                     validateDataSource(dataSourceJndiParam, dataSource);
161                     return (DataSource) dataSource;
162                 }
163             } catch (NamingException e) {
164                 throw new ConfigurationException("Could not locate the DataSource at the given JNDI location: '" + jndiName + "'", e);
165             }
166         }
167         return null;
168     }
169 
170     protected void destroyInstance(Object instance) throws Exception {
171         if (instance instanceof Lifecycle) {
172             ((Lifecycle) instance).stop();
173         }
174     }
175 
176     protected String getStringProperty(Config config, String propertyName) {
177         String data = config.getProperty(propertyName);
178         if (StringUtils.isEmpty(data)) {
179             throw new ConfigurationException("Could not locate a value for the given property '" + propertyName + "'.");
180         }
181         return data;
182     }
183 
184     protected int getIntProperty(Config config, String propertyName) {
185         String data = getStringProperty(config, propertyName);
186         try {
187             int intData = Integer.parseInt(data);
188             return intData;
189         } catch (NumberFormatException e) {
190             throw new ConfigurationException("The given property '" + propertyName + "' was not a valid integer.  Value was '" + data + "'");
191         }
192     }
193 
194     public JndiTemplate getJndiTemplate() {
195         return this.jndiTemplate;
196     }
197 
198     public void setJndiTemplate(JndiTemplate jndiTemplate) {
199         this.jndiTemplate = jndiTemplate;
200     }
201 
202     public String getDefaultDataSourceJndiParam() {
203         return defaultDataSourceJndiParam;
204     }
205 
206     public void setDefaultDataSourceJndiParam(String defaultDataSourceJndiParam) {
207         this.defaultDataSourceJndiParam = defaultDataSourceJndiParam;
208     }
209 
210     public String getDefaultNonTransactionalDataSourceJndiParam() {
211         return defaultNonTransactionalDataSourceJndiParam;
212     }
213 
214     public void setDefaultNonTransactionalDataSourceJndiParam(String defaultNonTransactionalDataSourceJndiParam) {
215         this.defaultNonTransactionalDataSourceJndiParam = defaultNonTransactionalDataSourceJndiParam;
216     }
217 
218     public String getDefaultDataSourceParam() {
219         return defaultDataSourceParam;
220     }
221 
222     public void setDefaultDataSourceParam(String defaultDataSourceParam) {
223         this.defaultDataSourceParam = defaultDataSourceParam;
224     }
225 
226     public String getDefaultNonTransactionalDataSourceParam() {
227         return defaultNonTransactionalDataSourceParam;
228     }
229 
230     public void setDefaultNonTransactionalDataSourceParam(String defaultNonTransactionalDataSourceParam) {
231         this.defaultNonTransactionalDataSourceParam = defaultNonTransactionalDataSourceParam;
232     }
233 
234     public List<String> getPreferredDataSourceJndiParams() {
235         return preferredDataSourceJndiParams;
236     }
237 
238     public void setPreferredDataSourceJndiParams(List<String> preferredDataSourceJndiParams) {
239         this.preferredDataSourceJndiParams = preferredDataSourceJndiParams;
240     }
241 
242     public List<String> getPreferredDataSourceParams() {
243         return preferredDataSourceParams;
244     }
245 
246     public void setPreferredDataSourceParams(List<String> preferredDataSourceParams) {
247         this.preferredDataSourceParams = preferredDataSourceParams;
248     }
249 
250     public void setNonTransactionalDataSource(boolean nonTransactionalDataSource) {
251         this.nonTransactionalDataSource = nonTransactionalDataSource;
252     }
253 
254 	public boolean isServerDataSource() {
255 		return this.serverDataSource;
256 	}
257 
258 	public void setServerDataSource(boolean serverDataSource) {
259 		this.serverDataSource = serverDataSource;
260 	}
261 
262 	public boolean isNullAllowed() {
263 		return this.nullAllowed;
264 	}
265 
266 	public void setNullAllowed(boolean nullAllowed) {
267 		this.nullAllowed = nullAllowed;
268 	}
269 
270 }