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