001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.javadoc;
016    
017    import com.liferay.portal.kernel.javadoc.BaseJavadoc;
018    import com.liferay.portal.kernel.javadoc.EmptyJavadocMethod;
019    import com.liferay.portal.kernel.javadoc.JavadocClass;
020    import com.liferay.portal.kernel.javadoc.JavadocManager;
021    import com.liferay.portal.kernel.javadoc.JavadocMethod;
022    import com.liferay.portal.kernel.javadoc.JavadocMethodImpl;
023    import com.liferay.portal.kernel.log.Log;
024    import com.liferay.portal.kernel.log.LogFactoryUtil;
025    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
026    import com.liferay.portal.kernel.util.StreamUtil;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.kernel.xml.Document;
031    import com.liferay.portal.kernel.xml.Element;
032    import com.liferay.portal.kernel.xml.UnsecureSAXReaderUtil;
033    import com.liferay.portal.util.PropsValues;
034    
035    import java.io.InputStream;
036    
037    import java.lang.reflect.Method;
038    
039    import java.net.URL;
040    
041    import java.util.Collection;
042    import java.util.HashMap;
043    import java.util.Iterator;
044    import java.util.List;
045    import java.util.Map;
046    
047    /**
048     * @author Igor Spasic
049     */
050    @DoPrivileged
051    public class JavadocManagerImpl implements JavadocManager {
052    
053            @Override
054            public void load(String servletContextName, ClassLoader classLoader) {
055                    if (!PropsValues.JAVADOC_MANAGER_ENABLED) {
056                            return;
057                    }
058    
059                    if (_log.isInfoEnabled()) {
060                            _log.info("Loading Javadocs for \"" + servletContextName + '\"');
061                    }
062    
063                    Document document = getDocument(classLoader);
064    
065                    if (document == null) {
066                            return;
067                    }
068    
069                    parseDocument(servletContextName, classLoader, document);
070    
071                    if (_log.isInfoEnabled()) {
072                            _log.info("Loaded Javadocs for \"" + servletContextName + '\"');
073                    }
074            }
075    
076            @Override
077            public JavadocClass lookupJavadocClass(Class<?> clazz) {
078                    return _javadocClasses.get(clazz);
079            }
080    
081            @Override
082            public JavadocMethod lookupJavadocMethod(Method method) {
083                    JavadocMethod javadocMethod = _javadocMethods.get(method);
084    
085                    if (javadocMethod != null) {
086                            return javadocMethod;
087                    }
088    
089                    Class<?> clazz = method.getDeclaringClass();
090    
091                    String className = clazz.getName();
092    
093                    if (!className.contains(".service.") ||
094                            !className.endsWith("ServiceUtil")) {
095    
096                            return null;
097                    }
098    
099                    String implClassName = StringUtil.replace(
100                            className, new String[] {".service.", "ServiceUtil"},
101                            new String[] {".service.impl.", "ServiceImpl"});
102    
103                    if (_log.isDebugEnabled()) {
104                            _log.debug(
105                                    "Attempting to load method from class " + implClassName +
106                                            " instead of " + className);
107                    }
108    
109                    try {
110                            Class<?> implClass = JavadocUtil.loadClass(
111                                    clazz.getClassLoader(), implClassName);
112    
113                            Method implMethod = implClass.getMethod(
114                                    method.getName(), method.getParameterTypes());
115    
116                            return _javadocMethods.get(implMethod);
117                    }
118                    catch (NoSuchMethodException nsme) {
119                            if (_log.isWarnEnabled()) {
120                                    _log.warn(
121                                            "Unable to load method " + method.getName() +
122                                                    " from class " + implClassName);
123                            }
124                    }
125                    catch (Exception e) {
126                            if (_log.isWarnEnabled()) {
127                                    _log.warn(
128                                            "Unable to load implementation class " + implClassName);
129                            }
130                    }
131    
132                    return null;
133            }
134    
135            @Override
136            public void unload(String servletContextName) {
137                    if (_log.isInfoEnabled()) {
138                            _log.info("Unloading Javadocs for \"" + servletContextName + '\"');
139                    }
140    
141                    unload(servletContextName, _javadocClasses.values());
142                    unload(servletContextName, _javadocMethods.values());
143    
144                    if (_log.isInfoEnabled()) {
145                            _log.info("Unloaded Javadocs for \"" + servletContextName + '\"');
146                    }
147            }
148    
149            protected Document getDocument(ClassLoader classLoader) {
150                    InputStream inputStream = null;
151    
152                    try {
153                            URL url = classLoader.getResource("META-INF/javadocs-rt.xml");
154    
155                            if (url == null) {
156                                    return null;
157                            }
158    
159                            inputStream = url.openStream();
160    
161                            return UnsecureSAXReaderUtil.read(inputStream, true);
162                    }
163                    catch (Exception e) {
164                            _log.error(e, e);
165                    }
166                    finally {
167                            StreamUtil.cleanUp(inputStream);
168                    }
169    
170                    return null;
171            }
172    
173            protected void parseDocument(
174                    String servletContextName, ClassLoader classLoader, Document document) {
175    
176                    Element rootElement = document.getRootElement();
177    
178                    List<Element> javadocElements = rootElement.elements("javadoc");
179    
180                    for (Element javadocElement : javadocElements) {
181                            String type = javadocElement.elementText("type");
182    
183                            Class<?> clazz = null;
184    
185                            try {
186                                    clazz = JavadocUtil.loadClass(classLoader, type);
187                            }
188                            catch (ClassNotFoundException cnfe) {
189                                    if (_log.isWarnEnabled()) {
190                                            _log.warn("Unable to load class " + type);
191                                    }
192    
193                                    continue;
194                            }
195    
196                            String className = clazz.getName();
197    
198                            if (className.endsWith("LocalServiceImpl")) {
199                                    continue;
200                            }
201    
202                            JavadocClass javadocClass = parseJavadocClass(
203                                    servletContextName, javadocElement, clazz);
204    
205                            _javadocClasses.put(clazz, javadocClass);
206    
207                            List<Element> methodElements = javadocElement.elements("method");
208    
209                            for (Element methodElement : methodElements) {
210                                    try {
211                                            JavadocMethod javadocMethod = parseJavadocMethod(
212                                                    servletContextName, clazz, methodElement);
213    
214                                            if (javadocMethod == null) {
215                                                    continue;
216                                            }
217    
218                                            _javadocMethods.put(
219                                                    javadocMethod.getMethod(), javadocMethod);
220                                    }
221                                    catch (Exception e) {
222                                            String methodName = methodElement.elementText("name");
223    
224                                            if (_log.isWarnEnabled()) {
225                                                    _log.warn(
226                                                            "Unable to load method " + methodName +
227                                                                    " from class " + type);
228                                            }
229                                    }
230                            }
231                    }
232            }
233    
234            protected JavadocClass parseJavadocClass(
235                    String servletContextName, Element javadocElement, Class<?> clazz) {
236    
237                    List<Element> authorElements = javadocElement.elements("author");
238    
239                    String[] authors = new String[authorElements.size()];
240    
241                    for (int i = 0; i < authorElements.size(); i++) {
242                            Element authorElement = authorElements.get(i);
243    
244                            authors[i] = authorElement.getText();
245                    }
246    
247                    return new JavadocClass(
248                            servletContextName, javadocElement.elementText("comment"), clazz,
249                            authors);
250            }
251    
252            protected JavadocMethod parseJavadocMethod(
253                            String servletContextName, Class<?> clazz, Element methodElement)
254                    throws Exception {
255    
256                    String name = methodElement.elementText("name");
257    
258                    if (name.equals(clazz.getSimpleName()) ||
259                            name.startsWith(StringPool.UNDERLINE)) {
260    
261                            return null;
262                    }
263    
264                    List<Element> paramElements = methodElement.elements("param");
265    
266                    Class<?>[] parameterTypeClasses = new Class<?>[paramElements.size()];
267                    String[] parameterComments = new String[paramElements.size()];
268    
269                    for (int i = 0; i < paramElements.size(); i++) {
270                            Element paramElement = paramElements.get(i);
271    
272                            String parameterType = paramElement.elementText("type");
273    
274                            Class<?> parametarTypeClass = JavadocUtil.loadClass(
275                                    clazz.getClassLoader(), parameterType);
276    
277                            parameterTypeClasses[i] = parametarTypeClass;
278    
279                            String parameterComment = paramElement.elementText("comment");
280    
281                            parameterComments[i] = parameterComment;
282                    }
283    
284                    Method method = clazz.getDeclaredMethod(name, parameterTypeClasses);
285    
286                    String comment = methodElement.elementText("comment");
287    
288                    if (Validator.isNull(comment)) {
289                            return new EmptyJavadocMethod(servletContextName, method);
290                    }
291    
292                    String returnComment = null;
293    
294                    Element returnElement = methodElement.element("return");
295    
296                    if (returnElement != null) {
297                            returnComment = returnElement.elementText("comment");
298                    }
299    
300                    List<Element> throwsElements = methodElement.elements("throws");
301    
302                    String[] throwsComments = new String[throwsElements.size()];
303    
304                    for (int i = 0; i < throwsElements.size(); i++) {
305                            Element throwElement = throwsElements.get(i);
306    
307                            throwsComments[i] = throwElement.elementText("comment");
308                    }
309    
310                    return new JavadocMethodImpl(
311                            servletContextName, comment, method, parameterComments,
312                            returnComment, throwsComments);
313            }
314    
315            protected void unload(
316                    String servletContextName,
317                    Collection<? extends BaseJavadoc> collection) {
318    
319                    Iterator<? extends BaseJavadoc> iterator = collection.iterator();
320    
321                    while (iterator.hasNext()) {
322                            BaseJavadoc javadoc = iterator.next();
323    
324                            if (servletContextName.equals(javadoc.getServletContextName())) {
325                                    iterator.remove();
326                            }
327                    }
328            }
329    
330            private static final Log _log = LogFactoryUtil.getLog(
331                    JavadocManagerImpl.class);
332    
333            private final Map<Class<?>, JavadocClass> _javadocClasses = new HashMap<>();
334            private final Map<Method, JavadocMethod> _javadocMethods = new HashMap<>();
335    
336    }