001/**
002 * Copyright 2005-2018 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}