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