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