/*
* Copyright (C) 2010 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.commons.test;
import java.io.IOException;
import java.io.InputStream;
import java.security.Permission;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* You can exclude methods by adding the file <code>tsm-excludes.properties</code> into the classpath. The expected format is:
* <code>
* ${fqn-of-the-class}.${method-name}=${called-method-name}(,${called-method-name})*
* </code>
*
* @author <a href="anatoliy.bazko@exoplatform.org">Anatoliy Bazko</a>
* @version $Id: TestSecurityManager.java 2636 2010-06-16 14:18:23Z tolusha $
*
*/
public class TestSecurityManager extends SecurityManager
{
/**
* Map of methods to exclude and for each method we define a list of method called to ignore
*/
private volatile Map<String, Set<String>> excludes;
/**
* Indicates whether or not we are currently loading the file <code>tsm-excludes.properties</code>
*/
private final ThreadLocal<Boolean> loading = new ThreadLocal<Boolean>();
/**
* {@inheritDoc}
*/
@Override
public void checkPermission(Permission perm)
{
if (loading.get() != null)
{
//skip permission checking
return;
}
try
{
super.checkPermission(perm);
}
catch (SecurityException se)
{
Throwable e = se;
boolean srcCode = false;
boolean testCode = false;
// We need to lazy load the file 'tsm-excludes.properties' to prevent exception of type
// com.sun.org.apache.xml.internal.serializer.utils.WrappedRuntimeException:
// Could not load the propery file 'output_xml.properties' for output method 'xml' (check CLASSPATH)
Map<String, Set<String>> excludes = getExcludes();
while (e != null)
{
StackTraceElement[] traceElements = e.getStackTrace();
for (int i = 0; i < traceElements.length; i++)
{
String className = traceElements[i].getClassName();
String fileName = traceElements[i].getFileName();
String methodName = traceElements[i].getMethodName();
if (i - 1 >= 0 && excludes.containsKey(className + "." + methodName)
&& excludes.get(className + "." + methodName).contains(traceElements[i - 1].getMethodName()))
{
// the called method is excluded thus we ignore the exception
return;
}
if (className.startsWith("org.exoplatform"))
{
// known tests classes
if (fileName.startsWith("Test") || fileName.endsWith("Test.java")
|| fileName.endsWith("TestBase.java") || fileName.endsWith("TestCase.java")
|| fileName.equals("Probe.java") || fileName.equals("ExportBase.java")
|| fileName.equals("AbstractTestContainer.java") || fileName.equals("ContainerBuilder.java")
|| fileName.equals("WorkspaceStorageCacheBaseCase.java")
|| fileName.equals("ExoRepositoryStub.java") || fileName.equals("CloseableDataSource.java"))
{
testCode = true;
}
else
{
srcCode = true;
}
}
else if (className.startsWith("org.apache.jackrabbit.test"))
{
if (fileName.endsWith("Test.java") || fileName.equals("JCRTestResult.java")
|| fileName.equals("RepositoryHelper.java") || fileName.equals("RepositoryStub.java"))
{
testCode = true;
}
}
}
e = e.getCause();
}
// hide Exception if only test code exists
if (!srcCode && testCode)
{
return;
}
throw se;
}
}
private Map<String, Set<String>> getExcludes()
{
if (excludes == null)
{
synchronized (this)
{
if (excludes == null)
{
try
{
loading.set(Boolean.TRUE);
excludes = loadExcludes();
}
finally
{
loading.remove();
}
}
}
}
return excludes;
}
private Map<String, Set<String>> loadExcludes()
{
InputStream is = null;
try
{
is = Thread.currentThread().getContextClassLoader().getResourceAsStream("tsm-excludes.properties");
}
catch (Exception e)
{
return Collections.emptyMap();
}
if (is != null)
{
try
{
System.out.println("A file 'tsm-excludes.properties' has been found"); //NOSONAR
Properties p = new Properties();
p.load(is);
Map<String, Set<String>> excludes = new HashMap<String, Set<String>>();
for (Object key : p.keySet())
{
String[] values = p.getProperty((String)key).split(",");
excludes.put((String)key, new HashSet<String>(Arrays.asList(values)));
}
return excludes;
}
catch (Exception e)
{
e.printStackTrace(); //NOSONAR
}
finally
{
try
{
is.close();
}
catch (IOException e)
{
e.printStackTrace(); //NOSONAR
}
}
}
return Collections.emptyMap();
}
}