View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    *
4    *
5    * Licensed under the Educational Community License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    * http://www.opensource.org/licenses/ecl2.php
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  package org.kuali.rice.core.ojb;
18  
19  import java.io.BufferedInputStream;
20  import java.io.BufferedOutputStream;
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.InputStream;
24  import java.util.Iterator;
25  import java.util.List;
26  
27  import javax.xml.parsers.DocumentBuilderFactory;
28  import javax.xml.transform.Transformer;
29  import javax.xml.transform.TransformerFactory;
30  import javax.xml.transform.dom.DOMSource;
31  import javax.xml.transform.stream.StreamResult;
32  import javax.xml.xpath.XPath;
33  import javax.xml.xpath.XPathConstants;
34  import javax.xml.xpath.XPathFactory;
35  
36  import org.apache.commons.lang.StringUtils;
37  import org.apache.log4j.Logger;
38  import org.apache.ojb.broker.metadata.ConnectionRepository;
39  import org.apache.ojb.broker.metadata.DescriptorRepository;
40  import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
41  import org.apache.ojb.broker.metadata.MetadataManager;
42  import org.kuali.rice.core.config.Config;
43  import org.kuali.rice.core.config.ConfigContext;
44  import org.kuali.rice.core.config.ConfigurationException;
45  import org.kuali.rice.core.lifecycle.BaseLifecycle;
46  import org.kuali.rice.core.util.ClassLoaderUtils;
47  import org.springframework.beans.factory.InitializingBean;
48  import org.springframework.core.io.DefaultResourceLoader;
49  import org.w3c.dom.Document;
50  import org.w3c.dom.Element;
51  import org.w3c.dom.NodeList;
52  import org.xml.sax.InputSource;
53  
54  /**
55   * Base Ojb Configurer implementation which configures OJB for a particular rice module.
56   *
57   * @author Kuali Rice Team (rice.collab@kuali.org)
58   */
59  public class BaseOjbConfigurer extends BaseLifecycle implements InitializingBean {
60  
61      private static final Logger LOG = Logger.getLogger(BaseOjbConfigurer.class);
62  
63      public static final String RICE_OJB_PROPERTIES_PARAM = "rice.custom.ojb.properties";
64      public static final String OJB_PROPERTIES_PROP = "OJB.properties";
65      public static final String DEFAULT_OJB_PROPERTIES = "org/kuali/rice/core/ojb/RiceOJB.properties";
66  
67      /**
68       * The OJB JCD aliases 
69       */
70      protected String[] jcdAliases;
71      /**
72       * The location of the OJB repository/metadata descriptor
73       */
74      protected String metadataLocation;
75  
76      /**
77       * No-arg constructor
78       */
79      public BaseOjbConfigurer() {
80          // nothing
81      }
82  
83      /**
84       * Constructor that derives jcd aliases and repository metadata location from the module name
85       * jcdAliases = [ moduleName.toLowerCase() + "DataSource" ]
86       * metadataLocation = "classpath:OJB-repository-" + moduleName.toLowerCase() + ".xml";
87       * 
88       * @param moduleName the module name
89       */
90      public BaseOjbConfigurer(String moduleName) {
91          this.metadataLocation = "classpath:org/kuali/rice/" + moduleName.toLowerCase() + "/config/OJB-repository-" + moduleName.toLowerCase() + ".xml";
92          this.jcdAliases = new String[] { moduleName.toLowerCase() + "DataSource" };
93      }
94  
95      /**
96       * Constructor which takes the jcdAliases and metadata location
97       * 
98       * @param jcdAliases the jcd aliases
99       * @param metadataLocation the metadata location
100      */
101     public BaseOjbConfigurer(String[] jcdAliases, String metadataLocation) {
102         this.jcdAliases = jcdAliases;
103         this.metadataLocation = metadataLocation;
104     }
105 
106     @Override
107     public void start() throws Exception {
108         // if OJB has not already been loaded, let's trigger a load using our built-in OJB properties file
109         String currentValue = System.getProperty(OJB_PROPERTIES_PROP);
110         try {
111             System.setProperty(OJB_PROPERTIES_PROP, getOjbPropertiesLocation());
112             MetadataManager mm = MetadataManager.getInstance();
113             establishConnectionMetaData(mm);
114             establishRepositoryMetaData(mm);
115         } finally {
116             if (currentValue == null) {
117                 System.getProperties().remove(OJB_PROPERTIES_PROP);
118             } else {
119                 System.setProperty(OJB_PROPERTIES_PROP, currentValue);
120             }
121         }
122         super.start();
123     }
124 
125     @Override
126     public void stop() throws Exception {
127         super.stop();
128     }
129 
130 
131 
132     protected String getOjbPropertiesLocation() {
133         String ojbPropertiesLocation = ConfigContext.getCurrentContextConfig().getProperty(RICE_OJB_PROPERTIES_PARAM);
134         if (!StringUtils.isBlank(ojbPropertiesLocation)) {
135             LOG.info("Using custom OJB.properites from: " + ojbPropertiesLocation);
136         } else {        	
137             ojbPropertiesLocation = DEFAULT_OJB_PROPERTIES;
138             ConfigContext.getCurrentContextConfig().putProperty(RICE_OJB_PROPERTIES_PARAM, ojbPropertiesLocation);
139             LOG.info("Using default OJB.properties from: " + ojbPropertiesLocation);
140         }
141         return ojbPropertiesLocation;
142     }
143 
144     protected void establishConnectionMetaData(MetadataManager mm) throws Exception {
145         String connMetadata = getMetadataLocation();
146         if (StringUtils.isBlank(connMetadata)) {
147             LOG.info("No OJB connection metadata loaded.");
148             return;
149         }
150         if (!isConnectionAlreadyConfigured(mm)) {
151             LOG.info("Loading OJB Connection Metadata from " + connMetadata);
152             DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader());
153             InputStream is = resourceLoader.getResource(connMetadata).getInputStream();
154             is = preprocessConnectionMetadata(is);
155             ConnectionRepository cr = mm.readConnectionRepository(is);
156             mm.mergeConnectionRepository(cr);
157             try {
158                 is.close();
159             } catch (Exception e) {
160                 LOG.warn("Failed to close stream to file " + connMetadata, e);
161             }
162         } else {
163             LOG.info("OJB Connections already configured for jcd aliases '" + StringUtils.join(getJcdAliases(), ", ") + "', skipping Metadata merge.");
164         }
165     }
166 
167     protected InputStream preprocessConnectionMetadata(InputStream inputStream) throws Exception {
168         Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(inputStream));
169         XPath xpath = XPathFactory.newInstance().newXPath();
170         NodeList connectionDescriptors = (NodeList)xpath.evaluate("/descriptor-repository/jdbc-connection-descriptor", document, XPathConstants.NODESET);
171         for (int index = 0; index < connectionDescriptors.getLength(); index++) {
172             Element descriptor = (Element)connectionDescriptors.item(index);
173             String currentPlatform = descriptor.getAttribute("platform");
174             if (StringUtils.isBlank(currentPlatform)) {
175                 String ojbPlatform = ConfigContext.getCurrentContextConfig().getProperty(Config.OJB_PLATFORM);
176                 if (StringUtils.isEmpty(ojbPlatform)) {
177                     throw new ConfigurationException("Could not configure OJB, the '" + Config.OJB_PLATFORM + "' configuration property was not set.");
178                 }
179                 LOG.info("Setting OJB connection descriptor database platform to '" + ojbPlatform + "'");
180                 descriptor.setAttribute("platform", ojbPlatform);
181             }
182         }
183         Transformer transformer = TransformerFactory.newInstance().newTransformer();
184         ByteArrayOutputStream baos = new ByteArrayOutputStream();
185         transformer.transform(new DOMSource(document), new StreamResult(new BufferedOutputStream(baos)));
186         return new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray()));
187     }
188 
189     @SuppressWarnings("unchecked")
190 	protected boolean isConnectionAlreadyConfigured(MetadataManager mm) {
191         List descriptors = mm.connectionRepository().getAllDescriptor();
192         for (Iterator iterator = descriptors.iterator(); iterator.hasNext();) {
193             JdbcConnectionDescriptor descriptor = (JdbcConnectionDescriptor) iterator.next();
194             for (String jcdAlias : getJcdAliases()) {
195                 if (descriptor.getJcdAlias().equals(jcdAlias)) {
196                     return true;
197                 }
198             }
199         }
200         return false;
201     }
202 
203     protected InputStream preprocessRepositoryMetadata(InputStream inputStream) throws Exception {
204         return inputStream;
205     }
206 
207     protected void establishRepositoryMetaData(MetadataManager mm) throws Exception {
208         String repoMetadata = getMetadataLocation();
209         if (StringUtils.isBlank(repoMetadata)) {
210             LOG.info("No OJB repository metadata loaded.");
211             return;
212         }
213         LOG.info("Loading OJB Metadata from " + repoMetadata);
214         DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader());
215         InputStream is = resourceLoader.getResource(repoMetadata).getInputStream();
216         is = preprocessRepositoryMetadata(is);
217         DescriptorRepository dr = mm.readDescriptorRepository(is);
218         mm.mergeDescriptorRepository(dr);
219         try {
220             is.close();
221         } catch (Exception e) {
222             LOG.warn("Failed to close stream to file " + repoMetadata, e);
223         }
224     }
225 
226     /**
227      * Return the jcd alias of the connection loaded by the connection metadata.
228      * The default implementation returns the jcd alias with which the instance was created (if any) 
229      * @return the jcd alias of the connection loaded by the connection metadata.
230      */
231     protected String[] getJcdAliases() {
232         return jcdAliases;
233     }
234 
235     /**
236      * Should return a String representing the location of a file to load OJB connection and
237      * repository metadata from.  If null or empty than no metadata will be loaded.
238      * The default implementation returns the metadata location with which the instance was created (if any)
239      */
240     protected String getMetadataLocation() {
241         return metadataLocation;
242     }
243 
244 	/***
245 	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
246 	 */
247 	public void afterPropertiesSet() throws Exception {
248 		this.start();
249 	}
250 
251 	/**
252 	 * @param jcdAliases the jcdAliases to set
253 	 */
254 	public void setJcdAliases(String[] jcdAliases) {
255 		this.jcdAliases = jcdAliases;
256 	}
257 
258 	/**
259 	 * @param metadataLocation the metadataLocation to set
260 	 */
261 	public void setMetadataLocation(String metadataLocation) {
262 		this.metadataLocation = metadataLocation;
263 	}
264 
265 }