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.devtools.jpa.eclipselink.conv.ojb;
017
018import org.apache.commons.logging.Log;
019import org.apache.commons.logging.LogFactory;
020import org.apache.ojb.broker.metadata.ClassDescriptor;
021import org.apache.ojb.broker.metadata.CollectionDescriptor;
022import org.apache.ojb.broker.metadata.ConnectionDescriptorXmlHandler;
023import org.apache.ojb.broker.metadata.ConnectionRepository;
024import org.apache.ojb.broker.metadata.DescriptorRepository;
025import org.apache.ojb.broker.metadata.FieldDescriptor;
026import org.apache.ojb.broker.metadata.MetadataException;
027import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
028import org.apache.ojb.broker.metadata.RepositoryXmlHandler;
029import org.apache.ojb.broker.util.ClassHelper;
030import org.xml.sax.InputSource;
031import org.xml.sax.SAXException;
032import org.xml.sax.XMLReader;
033
034import javax.xml.parsers.ParserConfigurationException;
035import javax.xml.parsers.SAXParser;
036import javax.xml.parsers.SAXParserFactory;
037import java.io.File;
038import java.io.IOException;
039import java.io.InputStream;
040import java.net.MalformedURLException;
041import java.net.URL;
042import java.net.URLConnection;
043import java.util.ArrayList;
044import java.util.Collection;
045import java.util.HashSet;
046import java.util.Map;
047import java.util.Set;
048
049public final class OjbUtil {
050
051    private static final Log LOG = LogFactory.getLog(OjbUtil.class);
052
053    private OjbUtil() {
054        throw new UnsupportedOperationException("do not call");
055    }
056
057    /**
058     * Starting with a root class, get the entire tree of mapped objects including collections and references.
059     * Cycles are correctly handled.
060     *
061     * @param rootClass the top level class to start with.
062     * @return a collection of classes to process
063     */
064    public static Collection<String> getMappedTree(String rootClass, Collection<DescriptorRepository> descriptorRepositories) {
065        final Set<String> processed = new HashSet<String>();
066        getMappedTree(rootClass, descriptorRepositories, processed);
067        return processed;
068    }
069
070    private static void getMappedTree(String rootClass, Collection<DescriptorRepository> descriptorRepositories, Set<String> processed) {
071        if (processed.contains(rootClass)) {
072            return;
073        }
074
075        processed.add(rootClass);
076        final ClassDescriptor cd = findClassDescriptor(rootClass, descriptorRepositories);
077        if (cd != null) {
078            final Collection<ObjectReferenceDescriptor> ords = cd.getObjectReferenceDescriptors();
079            if (ords != null) {
080                for (ObjectReferenceDescriptor ord : ords) {
081                    getMappedTree(ord.getItemClassName(), descriptorRepositories, processed);
082                }
083            }
084
085            final Collection<CollectionDescriptor> clds = cd.getCollectionDescriptors();
086            if (clds != null) {
087                for (ObjectReferenceDescriptor cld : clds) {
088                    getMappedTree(cld.getItemClassName(), descriptorRepositories, processed);
089                }
090            }
091
092        } else {
093            LOG.warn("ClassDescriptor not found for " + rootClass);
094        }
095    }
096
097    public static boolean isMappedColumn(String clazz, String fieldName, Collection<DescriptorRepository> descriptorRepositories) {
098        final ClassDescriptor cd = findClassDescriptor(clazz, descriptorRepositories);
099        if (cd != null) {
100            return cd.getFieldDescriptorByName(fieldName) != null ||
101                    cd.getObjectReferenceDescriptorByName(fieldName) != null ||
102                    cd.getCollectionDescriptorByName(fieldName) != null;
103        }
104        return false;
105    }
106
107    public static Collection<DescriptorRepository> getDescriptorRepositories(Collection<String> ojbFiles) throws Exception {
108        final Collection<DescriptorRepository> drs = new ArrayList<DescriptorRepository>();
109
110        //first parse & get all of the mapped classes
111        for (String file : ojbFiles) {
112            DescriptorRepository repository = OjbUtil.readDescriptorRepository(file);
113            if ( repository != null ) {
114                drs.add(repository);
115            }
116        }
117
118        return drs;
119    }
120
121    public static ClassDescriptor findClassDescriptor(String clazz, Collection<DescriptorRepository> descriptorRepositories) {
122        for (DescriptorRepository dr : descriptorRepositories) {
123            ClassDescriptor cd = (ClassDescriptor) dr.getDescriptorTable().get(clazz);
124
125            if (cd != null) {
126                //handle extends.  don't return class descriptor for extent classes
127                if (cd.getExtentClassNames() == null || cd.getExtentClassNames().isEmpty()) {
128                    return cd;
129                }
130            }
131        }
132        return null;
133    }
134
135    public static FieldDescriptor findFieldDescriptor(String clazz, String fieldName, Collection<DescriptorRepository> descriptorRepositories) {
136        final ClassDescriptor cd = findClassDescriptor(clazz, descriptorRepositories);
137        return cd != null ? cd.getFieldDescriptorByName(fieldName) : null;
138    }
139
140    public static ObjectReferenceDescriptor findObjectReferenceDescriptor(String clazz, String fieldName, Collection<DescriptorRepository> descriptorRepositories) {
141        final ClassDescriptor cd = findClassDescriptor(clazz, descriptorRepositories);
142        return cd != null ? cd.getObjectReferenceDescriptorByName(fieldName) : null;
143    }
144
145    public static CollectionDescriptor findCollectionDescriptor(String clazz, String fieldName, Collection<DescriptorRepository> descriptorRepositories) {
146        final ClassDescriptor cd = findClassDescriptor(clazz, descriptorRepositories);
147        return cd != null ? cd.getCollectionDescriptorByName(fieldName) : null;
148    }
149
150    public static Collection<String> getPrimaryKeyNames(String clazz, Collection<DescriptorRepository> descriptorRepositories) {
151        final Collection<String> pks = new ArrayList<String>();
152        final ClassDescriptor cd = OjbUtil.findClassDescriptor(clazz, descriptorRepositories);
153        for(FieldDescriptor pk : cd.getPkFields()) {
154            pks.add(pk.getAttributeName());
155        }
156        return pks;
157    }
158
159    /**
160     * Parses a repository file and populates an ojb datastructure representing the file.
161     * @param filename the file to parse
162     * @return a DescriptorRepository or null
163     */
164    public static DescriptorRepository readDescriptorRepository(String filename) {
165        LOG.info( "Processing Repository: " + filename);
166        try {
167            return (DescriptorRepository) buildRepository(filename, DescriptorRepository.class);
168        } catch (Exception e) {
169            LOG.error("Unable to process descriptor repository: " + filename);
170            LOG.error( e.getMessage() );
171            // Explicitly not logging the exception - it has already been dumped by earlier logging 
172        }
173        return null;
174    }
175
176    /**
177     * Gets all the mapped classes
178     */
179    public static Set<String> mappedClasses(Collection<DescriptorRepository> descriptors) throws Exception {
180        final Set<String> mappedClasses = new HashSet<String>();
181        for (DescriptorRepository dr : descriptors) {
182            for (Map.Entry<String, ClassDescriptor> entry : ((Map<String, ClassDescriptor>) dr.getDescriptorTable()).entrySet()) {
183                final Collection<String> extents = entry.getValue().getExtentClassNames();
184                if (extents != null && !extents.isEmpty()) {
185                    mappedClasses.addAll(extents);
186                } else {
187                    mappedClasses.add(entry.getKey());
188                }
189            }
190        }
191        return mappedClasses;
192    }
193
194
195    /**
196     * Gets all the super classes & stopping when the super class matches a package prefix
197     */
198    public static Set<String> getSuperClasses(String clazzName, String packagePrefixToStop) throws Exception {
199
200        final Set<String> superClasses = new HashSet<String>();
201
202        Class<?> clazz = Class.forName(clazzName);
203        for (Class<?> sc = clazz.getSuperclass(); sc != null && sc != Object.class && !sc.getName().startsWith(packagePrefixToStop);) {
204            superClasses.add(sc.getName());
205            sc = sc.getSuperclass();
206        }
207
208        return superClasses;
209    }
210
211    private static Object buildRepository(String repositoryFileName, Class targetRepository) throws IOException, ParserConfigurationException, SAXException {
212        URL url = buildURL(repositoryFileName);
213
214        String pathName = url.toExternalForm();
215
216        LOG.debug("Building repository from :" + pathName);
217        InputSource source = new InputSource(pathName);
218        URLConnection conn = url.openConnection();
219        conn.setUseCaches(false);
220        conn.connect();
221        InputStream i = conn.getInputStream();
222        source.setByteStream(i);
223        try {
224            return readMetadataFromXML(source, targetRepository);
225        } finally {
226            try {
227                i.close();
228            } catch (IOException x) {
229                LOG.warn("unable to close repository input stream [" + x.getMessage() + "]", x);
230            }
231        }
232    }
233
234
235    private static Object readMetadataFromXML(InputSource source, Class target) throws ParserConfigurationException, SAXException, IOException {
236        // get a xml reader instance:
237        SAXParserFactory factory = SAXParserFactory.newInstance();
238        LOG.debug("RepositoryPersistor using SAXParserFactory : " + factory.getClass().getName());
239
240        SAXParser p = factory.newSAXParser();
241        XMLReader reader = p.getXMLReader();
242
243        Object result;
244        if (DescriptorRepository.class.equals(target)) {
245            // create an empty repository:
246            DescriptorRepository repository = new DescriptorRepository();
247            // create handler for building the repository structure
248            org.xml.sax.ContentHandler handler = new RepositoryXmlHandler(repository);
249            // tell parser to use our handler:
250            reader.setContentHandler(handler);
251            reader.parse(source);
252            result = repository;
253        } else if (ConnectionRepository.class.equals(target)) {
254            // create an empty repository:
255            ConnectionRepository repository = new ConnectionRepository();
256            // create handler for building the repository structure
257            org.xml.sax.ContentHandler handler = new ConnectionDescriptorXmlHandler(repository);
258            // tell parser to use our handler:
259            reader.setContentHandler(handler);
260            reader.parse(source);
261            //LoggerFactory.getBootLogger().info("loading XML took " + (stop - start) + " msecs");
262            result = repository;
263        } else
264            throw new MetadataException("Could not build a repository instance for '" + target +
265                    "', using source " + source);
266        return result;
267    }
268
269    private static URL buildURL(String repositoryFileName) throws MalformedURLException {
270        //j2ee compliant lookup of resources
271        URL url = ClassHelper.getResource(repositoryFileName);
272
273        // don't be too strict: if resource is not on the classpath, try ordinary file lookup
274        if (url == null) {
275            try {
276                url = new File(repositoryFileName).toURL();
277            }
278            catch (MalformedURLException ignore) {
279            }
280        }
281
282        if (url != null) {
283            LOG.info("OJB Descriptor Repository: " + url);
284        } else {
285            throw new MalformedURLException("did not find resource " + repositoryFileName);
286        }
287        return url;
288    }
289}