001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.core.framework.persistence.ojb; 017 018import org.apache.commons.lang.StringUtils; 019import org.apache.log4j.Logger; 020import org.apache.ojb.broker.metadata.ConnectionRepository; 021import org.apache.ojb.broker.metadata.DescriptorRepository; 022import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor; 023import org.apache.ojb.broker.metadata.MetadataManager; 024import org.kuali.rice.core.api.config.ConfigurationException; 025import org.kuali.rice.core.api.config.property.Config; 026import org.kuali.rice.core.api.config.property.ConfigContext; 027import org.kuali.rice.core.api.lifecycle.BaseLifecycle; 028import org.kuali.rice.core.api.util.ClassLoaderUtils; 029import org.springframework.beans.factory.InitializingBean; 030import org.springframework.core.io.DefaultResourceLoader; 031import org.w3c.dom.Document; 032import org.w3c.dom.Element; 033import org.w3c.dom.NodeList; 034import org.xml.sax.InputSource; 035 036import javax.xml.parsers.DocumentBuilderFactory; 037import javax.xml.transform.Transformer; 038import javax.xml.transform.TransformerFactory; 039import javax.xml.transform.dom.DOMSource; 040import javax.xml.transform.stream.StreamResult; 041import javax.xml.xpath.XPath; 042import javax.xml.xpath.XPathConstants; 043import javax.xml.xpath.XPathFactory; 044import java.io.BufferedInputStream; 045import java.io.BufferedOutputStream; 046import java.io.ByteArrayInputStream; 047import java.io.ByteArrayOutputStream; 048import java.io.InputStream; 049import java.util.Iterator; 050import java.util.List; 051 052/** 053 * Base Ojb Configurer implementation which configures OJB for a particular rice module. 054 * 055 * @author Kuali Rice Team (rice.collab@kuali.org) 056 */ 057@Deprecated 058public class BaseOjbConfigurer extends BaseLifecycle implements InitializingBean { 059 060 private static final Logger LOG = Logger.getLogger(BaseOjbConfigurer.class); 061 062 public static final String RICE_OJB_PROPERTIES_PARAM = "rice.custom.ojb.properties"; 063 public static final String OJB_PROPERTIES_PROP = "OJB.properties"; 064 065 /** 066 * The OJB JCD aliases 067 */ 068 protected String[] jcdAliases; 069 /** 070 * The location of the OJB repository/metadata descriptor 071 */ 072 protected String metadataLocation; 073 074 protected List<String> additionalMetadataLocations; 075 076 /** 077 * No-arg constructor 078 */ 079 public BaseOjbConfigurer() { 080 // nothing 081 } 082 083 /** 084 * Constructor that derives jcd aliases and repository metadata location from the module name 085 * jcdAliases = [ moduleName.toLowerCase() + "DataSource" ] 086 * metadataLocation = "classpath:OJB-repository-" + moduleName.toLowerCase() + ".xml"; 087 * 088 * @param moduleName the module name 089 */ 090 public BaseOjbConfigurer(String moduleName) { 091 this.metadataLocation = "classpath:org/kuali/rice/" + moduleName.toLowerCase() + "/config/OJB-repository-" + moduleName.toLowerCase() + ".xml"; 092 this.jcdAliases = new String[] { moduleName.toLowerCase() + "DataSource" }; 093 } 094 095 /** 096 * Constructor which takes the jcdAliases and metadata location 097 * 098 * @param jcdAliases the jcd aliases 099 * @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 return ojbPropertiesLocation; 135 } 136 137 protected void establishConnectionMetaData(MetadataManager mm) throws Exception { 138 String connMetadata = getMetadataLocation(); 139 if (StringUtils.isBlank(connMetadata)) { 140 LOG.info("No OJB connection metadata loaded."); 141 return; 142 } 143 if (!isConnectionAlreadyConfigured(mm)) { 144 LOG.info("Loading OJB Connection Metadata from " + connMetadata); 145 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader()); 146 InputStream is = resourceLoader.getResource(connMetadata).getInputStream(); 147 is = preprocessConnectionMetadata(is); 148 ConnectionRepository cr = mm.readConnectionRepository(is); 149 mm.mergeConnectionRepository(cr); 150 try { 151 is.close(); 152 } catch (Exception e) { 153 LOG.warn("Failed to close stream to file " + connMetadata, e); 154 } 155 } else { 156 LOG.info("OJB Connections already configured for jcd aliases '" + StringUtils.join(getJcdAliases(), ", ") + "', skipping Metadata merge."); 157 } 158 } 159 160 protected InputStream preprocessConnectionMetadata(InputStream inputStream) throws Exception { 161 Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(inputStream)); 162 XPath xpath = XPathFactory.newInstance().newXPath(); 163 NodeList connectionDescriptors = (NodeList)xpath.evaluate("/descriptor-repository/jdbc-connection-descriptor", document, XPathConstants.NODESET); 164 for (int index = 0; index < connectionDescriptors.getLength(); index++) { 165 Element descriptor = (Element)connectionDescriptors.item(index); 166 String currentPlatform = descriptor.getAttribute("platform"); 167 if (StringUtils.isBlank(currentPlatform)) { 168 String ojbPlatform = ConfigContext.getCurrentContextConfig().getProperty(Config.OJB_PLATFORM); 169 if (StringUtils.isEmpty(ojbPlatform)) { 170 throw new ConfigurationException("Could not configure OJB, the '" + Config.OJB_PLATFORM + "' configuration property was not set."); 171 } 172 LOG.info("Setting OJB connection descriptor database platform to '" + ojbPlatform + "'"); 173 descriptor.setAttribute("platform", ojbPlatform); 174 } 175 } 176 Transformer transformer = TransformerFactory.newInstance().newTransformer(); 177 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 178 transformer.transform(new DOMSource(document), new StreamResult(new BufferedOutputStream(baos))); 179 return new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())); 180 } 181 182 @SuppressWarnings("unchecked") 183 protected boolean isConnectionAlreadyConfigured(MetadataManager mm) { 184 List descriptors = mm.connectionRepository().getAllDescriptor(); 185 for (Iterator iterator = descriptors.iterator(); iterator.hasNext();) { 186 JdbcConnectionDescriptor descriptor = (JdbcConnectionDescriptor) iterator.next(); 187 for (String jcdAlias : getJcdAliases()) { 188 if (descriptor.getJcdAlias().equals(jcdAlias)) { 189 return true; 190 } 191 } 192 } 193 return false; 194 } 195 196 protected InputStream preprocessRepositoryMetadata(InputStream inputStream) throws Exception { 197 return inputStream; 198 } 199 200 protected void establishRepositoryMetaData(MetadataManager mm) throws Exception { 201 String repoMetadata = getMetadataLocation(); 202 if (StringUtils.isBlank(repoMetadata)) { 203 LOG.info("No OJB repository metadata loaded."); 204 return; 205 } 206 LOG.info("Loading OJB Metadata from " + repoMetadata); 207 208 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(ClassLoaderUtils.getDefaultClassLoader()); 209 InputStream is = resourceLoader.getResource(repoMetadata).getInputStream(); 210 is = preprocessRepositoryMetadata(is); 211 DescriptorRepository dr = mm.readDescriptorRepository(is); 212 mm.mergeDescriptorRepository(dr); 213 214 try { 215 is.close(); 216 } catch (Exception e) { 217 LOG.warn("Failed to close stream to file " + repoMetadata, e); 218 } 219 220 if (additionalMetadataLocations != null) { 221 for (String metadataLocation : additionalMetadataLocations) { 222 LOG.info("Loading OJB Metadata from " + metadataLocation); 223 224 InputStream is2 = resourceLoader.getResource(metadataLocation).getInputStream(); 225 is2 = preprocessRepositoryMetadata(is2); 226 DescriptorRepository dr2 = mm.readDescriptorRepository(is2); 227 mm.mergeDescriptorRepository(dr2); 228 229 try { 230 is2.close(); 231 } catch (Exception e) { 232 LOG.warn("Failed to close stream to file " + metadataLocation, e); 233 } 234 } 235 } 236 } 237 238 /** 239 * Return the jcd alias of the connection loaded by the connection metadata. 240 * The default implementation returns the jcd alias with which the instance was created (if any) 241 * @return the jcd alias of the connection loaded by the connection metadata. 242 */ 243 protected String[] getJcdAliases() { 244 return jcdAliases; 245 } 246 247 /** 248 * Should return a String representing the location of a file to load OJB connection and 249 * repository metadata from. If null or empty than no metadata will be loaded. 250 * The default implementation returns the metadata location with which the instance was created (if any) 251 */ 252 protected String getMetadataLocation() { 253 return metadataLocation; 254 } 255 256 /*** 257 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() 258 */ 259 public void afterPropertiesSet() throws Exception { 260 this.start(); 261 } 262 263 /** 264 * @param jcdAliases the jcdAliases to set 265 */ 266 public void setJcdAliases(String[] jcdAliases) { 267 this.jcdAliases = jcdAliases; 268 } 269 270 /** 271 * @param metadataLocation the metadataLocation to set 272 */ 273 public void setMetadataLocation(String metadataLocation) { 274 this.metadataLocation = metadataLocation; 275 } 276 277 /** 278 * List of additional OJB descriptor files to include with the connect 279 * 280 * @return List<String> list of ojb files 281 */ 282 public List<String> getAdditionalMetadataLocations() { 283 return additionalMetadataLocations; 284 } 285 286 /** 287 * Setter for additional ojb metadata files 288 * 289 * @param additionalMetadataLocations 290 */ 291 public void setAdditionalMetadataLocations(List<String> additionalMetadataLocations) { 292 this.additionalMetadataLocations = additionalMetadataLocations; 293 } 294}