diff --git a/changelog.txt b/changelog.txt index 243552eff7..902b08b125 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,8 @@ +Changes in version 0.7 (2004-xx-xx) +----------------------------------- + +* Added MethodDefinitionSourceAdvisor for performance and autoproxying + Changes in version 0.6.1 (2004-09-25) ------------------------------------- diff --git a/core/src/main/java/org/acegisecurity/intercept/method/MethodDefinitionSourceAdvisor.java b/core/src/main/java/org/acegisecurity/intercept/method/MethodDefinitionSourceAdvisor.java new file mode 100644 index 0000000000..f347e3dd42 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/intercept/method/MethodDefinitionSourceAdvisor.java @@ -0,0 +1,120 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.intercept.method; + +import org.aopalliance.intercept.MethodInvocation; + +import org.springframework.aop.framework.AopConfigException; +import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Method; + + +/** + * Advisor driven by a {@link MethodDefinitionSource}, used to exclude a {@link + * MethodSecurityInterceptor} from public (ie non-secure) methods. + * + *

+ * Because the AOP framework caches advice calculations, this is normally + * faster than just letting the MethodSecurityInterceptor run and + * find out itself that it has no work to do. + *

+ * + *

+ * This class also allows the use of Spring's + * DefaultAdvisorAutoProxyCreator, which makes configuration + * easier than setup a ProxyFactoryBean for each object requiring + * security. Note that autoproxying is not supported for BeanFactory + * implementations, as post-processing is automatic only for application + * contexts. + *

+ * + *

+ * Based on Spring's TransactionAttributeSourceAdvisor. + *

+ * + * @author Ben Alex + * @version $Id$ + */ +public class MethodDefinitionSourceAdvisor + extends StaticMethodMatcherPointcutAdvisor { + //~ Instance fields ======================================================== + + private MethodDefinitionSource transactionAttributeSource; + + //~ Constructors =========================================================== + + public MethodDefinitionSourceAdvisor(MethodSecurityInterceptor advice) { + super(advice); + + if (advice.getObjectDefinitionSource() == null) { + throw new AopConfigException( + "Cannot construct a MethodDefinitionSourceAdvisor using a " + + "MethodSecurityInterceptor that has no ObjectDefinitionSource configured"); + } + + this.transactionAttributeSource = advice.getObjectDefinitionSource(); + } + + //~ Methods ================================================================ + + public boolean matches(Method m, Class targetClass) { + MethodInvocation methodInvocation = new InternalMethodInvocation(m); + + return (this.transactionAttributeSource.getAttributes(methodInvocation) != null); + } + + //~ Inner Classes ========================================================== + + /** + * Represents a MethodInvocation. + * + *

+ * Required as MethodDefinitionSource only supports lookup of + * configuration attributes for MethodInvocations. + *

+ */ + private class InternalMethodInvocation implements MethodInvocation { + Method method; + + public InternalMethodInvocation(Method method) { + this.method = method; + } + + private InternalMethodInvocation() {} + + public Object[] getArguments() { + throw new UnsupportedOperationException(); + } + + public Method getMethod() { + return this.method; + } + + public AccessibleObject getStaticPart() { + throw new UnsupportedOperationException(); + } + + public Object getThis() { + throw new UnsupportedOperationException(); + } + + public Object proceed() throws Throwable { + throw new UnsupportedOperationException(); + } + } +} diff --git a/core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceAdvisorTests.java b/core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceAdvisorTests.java new file mode 100644 index 0000000000..4e6154bad9 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/intercept/method/MethodDefinitionSourceAdvisorTests.java @@ -0,0 +1,98 @@ +/* Copyright 2004 Acegi Technology Pty Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.sf.acegisecurity.intercept.method; + +import junit.framework.TestCase; + +import net.sf.acegisecurity.TargetObject; + +import org.springframework.aop.framework.AopConfigException; + +import java.lang.reflect.Method; + + +/** + * Tests {@link MethodDefinitionSourceAdvisor}. + * + * @author Ben Alex + * @version $Id$ + */ +public class MethodDefinitionSourceAdvisorTests extends TestCase { + //~ Constructors =========================================================== + + public MethodDefinitionSourceAdvisorTests() { + super(); + } + + public MethodDefinitionSourceAdvisorTests(String arg0) { + super(arg0); + } + + //~ Methods ================================================================ + + public final void setUp() throws Exception { + super.setUp(); + } + + public static void main(String[] args) { + junit.textui.TestRunner.run(MethodDefinitionSourceAdvisorTests.class); + } + + public void testAdvisorReturnsFalseWhenMethodInvocationNotDefined() + throws Exception { + Class clazz = TargetObject.class; + Method method = clazz.getMethod("makeLowerCase", + new Class[] {String.class}); + + MethodDefinitionSourceAdvisor advisor = new MethodDefinitionSourceAdvisor(getInterceptor()); + assertFalse(advisor.matches(method, clazz)); + } + + public void testAdvisorReturnsTrueWhenMethodInvocationIsDefined() + throws Exception { + Class clazz = TargetObject.class; + Method method = clazz.getMethod("countLength", + new Class[] {String.class}); + + MethodDefinitionSourceAdvisor advisor = new MethodDefinitionSourceAdvisor(getInterceptor()); + assertTrue(advisor.matches(method, clazz)); + } + + public void testDetectsImproperlyConfiguredAdvice() { + MethodSecurityInterceptor msi = new MethodSecurityInterceptor(); + + try { + new MethodDefinitionSourceAdvisor(msi); + fail( + "Should have detected null ObjectDefinitionSource and thrown AopConfigException"); + } catch (AopConfigException expected) { + assertTrue(true); + } + } + + private MethodSecurityInterceptor getInterceptor() { + MethodDefinitionSourceEditor editor = new MethodDefinitionSourceEditor(); + editor.setAsText( + "net.sf.acegisecurity.TargetObject.countLength=ROLE_NOT_USED"); + + MethodDefinitionMap map = (MethodDefinitionMap) editor.getValue(); + + MethodSecurityInterceptor msi = new MethodSecurityInterceptor(); + msi.setObjectDefinitionSource(map); + + return msi; + } +} diff --git a/samples/attributes/src/applicationContext.xml b/samples/attributes/src/applicationContext.xml index f8e0150755..643223e8a7 100644 --- a/samples/attributes/src/applicationContext.xml +++ b/samples/attributes/src/applicationContext.xml @@ -75,20 +75,23 @@ - - - - - securityInterceptor - - + + + - - - - - - - + + +