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.test.rule;
016    
017    import com.liferay.portal.deploy.hot.HookHotDeployListener;
018    import com.liferay.portal.deploy.hot.ServiceWrapperRegistry;
019    import com.liferay.portal.kernel.deploy.hot.DependencyManagementThreadLocal;
020    import com.liferay.portal.kernel.deploy.hot.HotDeployEvent;
021    import com.liferay.portal.kernel.deploy.hot.HotDeployUtil;
022    import com.liferay.portal.kernel.portlet.PortletClassLoaderUtil;
023    import com.liferay.portal.kernel.servlet.ServletContextPool;
024    import com.liferay.portal.kernel.servlet.filters.invoker.InvokerFilterHelper;
025    import com.liferay.portal.kernel.template.TemplateManagerUtil;
026    import com.liferay.portal.kernel.util.ClassLoaderPool;
027    import com.liferay.portal.kernel.util.ListUtil;
028    import com.liferay.portal.kernel.util.PortalLifecycleUtil;
029    import com.liferay.portal.kernel.util.PropsKeys;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.service.test.ServiceTestUtil;
032    import com.liferay.portal.spring.context.PortletContextLoaderListener;
033    import com.liferay.portal.test.mock.AutoDeployMockServletContext;
034    import com.liferay.portal.util.InitUtil;
035    import com.liferay.portal.util.PortalUtil;
036    import com.liferay.portal.util.PropsUtil;
037    
038    import java.lang.reflect.Method;
039    
040    import java.net.MalformedURLException;
041    import java.net.URL;
042    import java.net.URLClassLoader;
043    
044    import java.security.CodeSource;
045    import java.security.ProtectionDomain;
046    
047    import java.util.List;
048    import java.util.Map;
049    import java.util.concurrent.ConcurrentHashMap;
050    
051    import javax.servlet.ServletContext;
052    import javax.servlet.ServletContextEvent;
053    
054    import org.junit.rules.TestRule;
055    import org.junit.runner.Description;
056    import org.junit.runners.model.Statement;
057    
058    import org.springframework.core.io.ClassPathResource;
059    import org.springframework.core.io.FileSystemResourceLoader;
060    import org.springframework.core.io.Resource;
061    import org.springframework.core.io.ResourceLoader;
062    import org.springframework.mock.web.MockServletContext;
063    
064    /**
065     * @author Raymond Aug??
066     * @author Shuyang Zhou
067     */
068    public class PACLTestRule implements TestRule {
069    
070            public static final String RESOURCE_PATH =
071                    "com/liferay/portal/security/pacl/test/dependencies";
072    
073            @Override
074            public Statement apply(
075                    final Statement statement, final Description description) {
076    
077                    return new Statement() {
078    
079                            @Override
080                            public void evaluate() throws Throwable {
081                                    PortletContextLoaderListener portletContextLoaderListener =
082                                            new PortletContextLoaderListener();
083    
084                                    HotDeployEvent hotDeployEvent = null;
085    
086                                    if (description.getMethodName() != null) {
087                                            hotDeployEvent = beforeClass(
088                                                    description, portletContextLoaderListener);
089                                    }
090    
091                                    try {
092                                            invokeStatement(statement, description);
093                                    }
094                                    finally {
095                                            if (hotDeployEvent != null) {
096                                                    afterClass(
097                                                            description, hotDeployEvent,
098                                                            portletContextLoaderListener);
099                                            }
100                                    }
101                            }
102    
103                    };
104            }
105    
106            protected void afterClass(
107                    Description description, HotDeployEvent hotDeployEvent,
108                    PortletContextLoaderListener portletContextLoaderListener) {
109    
110                    HotDeployUtil.fireUndeployEvent(hotDeployEvent);
111    
112                    ClassLoaderPool.register(
113                            hotDeployEvent.getServletContextName(),
114                            hotDeployEvent.getContextClassLoader());
115                    PortletClassLoaderUtil.setServletContextName(
116                            hotDeployEvent.getServletContextName());
117    
118                    try {
119                            portletContextLoaderListener.contextDestroyed(
120                                    new ServletContextEvent(hotDeployEvent.getServletContext()));
121                    }
122                    finally {
123                            ClassLoaderPool.unregister(hotDeployEvent.getServletContextName());
124                            PortletClassLoaderUtil.setServletContextName(null);
125                    }
126            }
127    
128            protected HotDeployEvent beforeClass(
129                            Description description,
130                            PortletContextLoaderListener portletContextLoaderListener)
131                    throws ReflectiveOperationException {
132    
133                    _testClass = _loadTestClass(description.getTestClass());
134                    _instance = _testClass.newInstance();
135    
136                    ServletContext servletContext = ServletContextPool.get(
137                            PortalUtil.getServletContextName());
138    
139                    if (servletContext == null) {
140                            servletContext = new AutoDeployMockServletContext(
141                                    new FileSystemResourceLoader());
142    
143                            servletContext.setAttribute(
144                                    InvokerFilterHelper.class.getName(), new InvokerFilterHelper());
145    
146                            ServletContextPool.put(PortalUtil.getPathContext(), servletContext);
147                    }
148    
149                    HotDeployUtil.reset();
150    
151                    HotDeployUtil.registerListener(new HookHotDeployListener());
152    
153                    HotDeployUtil.setCapturePrematureEvents(false);
154    
155                    PortalLifecycleUtil.flushInits();
156    
157                    ClassLoader classLoader = _testClass.getClassLoader();
158    
159                    MockServletContext mockServletContext = new MockServletContext(
160                            new PACLResourceLoader(classLoader));
161    
162                    mockServletContext.setServletContextName("a-test-hook");
163    
164                    HotDeployEvent hotDeployEvent = getHotDeployEvent(
165                            mockServletContext, classLoader);
166    
167                    HotDeployUtil.fireDeployEvent(hotDeployEvent);
168    
169                    ClassLoaderPool.register(
170                            hotDeployEvent.getServletContextName(),
171                            hotDeployEvent.getContextClassLoader());
172                    PortletClassLoaderUtil.setServletContextName(
173                            hotDeployEvent.getServletContextName());
174    
175                    try {
176                            portletContextLoaderListener.contextInitialized(
177                                    new ServletContextEvent(mockServletContext));
178                    }
179                    finally {
180                            ClassLoaderPool.unregister(hotDeployEvent.getServletContextName());
181                            PortletClassLoaderUtil.setServletContextName(null);
182                    }
183    
184                    return hotDeployEvent;
185            }
186    
187            protected HotDeployEvent getHotDeployEvent(
188                    ServletContext servletContext, ClassLoader classLoader) {
189    
190                    boolean dependencyManagementEnabled =
191                            DependencyManagementThreadLocal.isEnabled();
192    
193                    try {
194                            DependencyManagementThreadLocal.setEnabled(false);
195    
196                            return new HotDeployEvent(servletContext, classLoader);
197                    }
198                    finally {
199                            DependencyManagementThreadLocal.setEnabled(
200                                    dependencyManagementEnabled);
201                    }
202            }
203    
204            protected void invokeStatement(Statement statement, Description description)
205                    throws Throwable {
206    
207                    String methodName = description.getMethodName();
208    
209                    if (methodName == null) {
210                            statement.evaluate();
211    
212                            return;
213                    }
214    
215                    Method method = _testClass.getMethod(description.getMethodName());
216    
217                    method.invoke(_instance);
218            }
219    
220            private static Class<?> _loadTestClass(Class<?> clazz)
221                    throws ClassNotFoundException {
222    
223                    ProtectionDomain protectionDomain = clazz.getProtectionDomain();
224    
225                    CodeSource codeSource = protectionDomain.getCodeSource();
226    
227                    ClassLoader classLoader = new PACLClassLoader(
228                            new URL[] {codeSource.getLocation()}, clazz.getClassLoader());
229    
230                    return Class.forName(clazz.getName(), true, classLoader);
231            }
232    
233            private static final String _PACKAGE_PATH =
234                    "com.liferay.portal.security.pacl.test.";
235    
236            static {
237                    List<String> configLocations = ListUtil.fromArray(
238                            PropsUtil.getArray(PropsKeys.SPRING_CONFIGS));
239    
240                    InitUtil.initWithSpring(configLocations, true);
241    
242                    ServiceTestUtil.initMainServletServices();
243                    ServiceTestUtil.initStaticServices();
244                    ServiceTestUtil.initServices();
245                    ServiceTestUtil.initPermissions();
246    
247                    new ServiceWrapperRegistry();
248    
249                    try {
250                            Class.forName(
251                                    TemplateManagerUtil.class.getName(), true,
252                                    PACLTestRule.class.getClassLoader());
253                    }
254                    catch (ClassNotFoundException cnfe) {
255                            throw new ExceptionInInitializerError(cnfe);
256                    }
257            }
258    
259            private Object _instance;
260            private Class<?> _testClass;
261    
262            private static class PACLClassLoader extends URLClassLoader {
263    
264                    public PACLClassLoader(URL[] urls, ClassLoader parentClassLoader) {
265                            super(urls, parentClassLoader);
266                    }
267    
268                    @Override
269                    public URL findResource(String name) {
270                            if (_urls.containsKey(name)) {
271                                    return _urls.get(name);
272                            }
273    
274                            URL resource = null;
275    
276                            if (!name.contains(RESOURCE_PATH)) {
277                                    String newName = name;
278    
279                                    if (!newName.startsWith(StringPool.SLASH)) {
280                                            newName = StringPool.SLASH.concat(newName);
281                                    }
282    
283                                    newName = RESOURCE_PATH.concat(newName);
284    
285                                    resource = super.findResource(newName);
286                            }
287    
288                            if ((resource == null) && !name.contains(RESOURCE_PATH)) {
289                                    String newName = name;
290    
291                                    if (!newName.startsWith(StringPool.SLASH)) {
292                                            newName = StringPool.SLASH.concat(newName);
293                                    }
294    
295                                    newName = RESOURCE_PATH.concat("/WEB-INF/classes").concat(
296                                            newName);
297    
298                                    resource = super.findResource(newName);
299                            }
300    
301                            if (resource == null) {
302                                    resource = super.findResource(name);
303                            }
304    
305                            if (resource != null) {
306                                    _urls.put(name, resource);
307                            }
308    
309                            return resource;
310                    }
311    
312                    @Override
313                    public URL getResource(String name) {
314                            if (name.equals(
315                                            "com/liferay/util/bean/PortletBeanLocatorUtil.class")) {
316    
317                                    URL url = findResource("/");
318    
319                                    String path = url.getPath();
320    
321                                    path = path.substring(
322                                            0, path.length() - RESOURCE_PATH.length() - 1);
323    
324                                    path = path.concat(name);
325    
326                                    try {
327                                            return new URL("file", null, path);
328                                    }
329                                    catch (MalformedURLException murle) {
330                                    }
331                            }
332    
333                            URL url = findResource(name);
334    
335                            if (url != null) {
336                                    return url;
337                            }
338    
339                            return super.getResource(name);
340                    }
341    
342                    @Override
343                    public Class<?> loadClass(String name) throws ClassNotFoundException {
344                            if (name.startsWith(_PACKAGE_PATH)) {
345                                    if (_classes.containsKey(name)) {
346                                            return _classes.get(name);
347                                    }
348    
349                                    Class<?> clazz = super.findClass(name);
350    
351                                    _classes.put(name, clazz);
352    
353                                    return clazz;
354                            }
355    
356                            return super.loadClass(name);
357                    }
358    
359                    @Override
360                    protected synchronized Class<?> loadClass(String name, boolean resolve)
361                            throws ClassNotFoundException {
362    
363                            if (name.startsWith(_PACKAGE_PATH)) {
364                                    if (_classes.containsKey(name)) {
365                                            return _classes.get(name);
366                                    }
367    
368                                    Class<?> clazz = super.findClass(name);
369    
370                                    _classes.put(name, clazz);
371    
372                                    return clazz;
373                            }
374    
375                            return super.loadClass(name, resolve);
376                    }
377    
378                    private final Map<String, Class<?>> _classes =
379                            new ConcurrentHashMap<>();
380                    private final Map<String, URL> _urls = new ConcurrentHashMap<>();
381    
382            }
383    
384            private static class PACLResourceLoader implements ResourceLoader {
385    
386                    public PACLResourceLoader(ClassLoader classLoader) {
387                            _classLoader = classLoader;
388                    }
389    
390                    @Override
391                    public ClassLoader getClassLoader() {
392                            return _classLoader;
393                    }
394    
395                    @Override
396                    public Resource getResource(String location) {
397                            ClassLoader classLoader = getClassLoader();
398    
399                            return new ClassPathResource(RESOURCE_PATH + location, classLoader);
400                    }
401    
402                    private final ClassLoader _classLoader;
403    
404            }
405    
406    }