You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by st...@apache.org on 2015/11/23 10:31:27 UTC

deltaspike git commit: DELTASPIKE-1015 support for hierarchic BeanManager

Repository: deltaspike
Updated Branches:
  refs/heads/master ada7480c8 -> bbd5fa8b2


DELTASPIKE-1015 support for hierarchic BeanManager

This is mainly needed in EARs.

Using WeakReferences will prevent mem leaks if a webapp using DS
get's undeployed. Otherwise the JVM will not be able to garbage
collect any WebAppClassLoader


Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/bbd5fa8b
Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/bbd5fa8b
Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/bbd5fa8b

Branch: refs/heads/master
Commit: bbd5fa8b24a08f0c0cda8e0dca61403872a00594
Parents: ada7480
Author: Mark Struberg <st...@apache.org>
Authored: Mon Nov 23 10:21:30 2015 +0100
Committer: Mark Struberg <st...@apache.org>
Committed: Mon Nov 23 10:31:05 2015 +0100

----------------------------------------------------------------------
 .../core/util/ParentExtensionStorage.java       | 139 +++++++
 .../impl/message/MessageBundleExtension.java    | 417 ++++++++++---------
 .../impl/extension/SecurityExtension.java       |  14 +
 3 files changed, 369 insertions(+), 201 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/deltaspike/blob/bbd5fa8b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ParentExtensionStorage.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ParentExtensionStorage.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ParentExtensionStorage.java
new file mode 100644
index 0000000..6e1f044
--- /dev/null
+++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/util/ParentExtensionStorage.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.core.util;
+
+import javax.enterprise.inject.spi.Extension;
+
+import java.lang.ref.WeakReference;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Support for Containers with 'hierarchic BeanManagers'
+ * This is mostly useful for EAR applications.
+ *
+ * Some EE Container scan the common shared EAR lib path and reuse this information
+ * for the webapps in the EAR. This is actually the only approach a container can
+ * do to prevent mem leaks and side effects spreading to different webapps.
+ * Of course this also means that the webapps get their own (different)
+ * instances of an Extension.
+ *
+ * To acknowledge this solution we provide a mechanism to lookup 'parent Extensions'
+ * which is very similar to handling parent ClassLoaders.
+ *
+ * All your Extension has to do is to register itself in
+ * {@link javax.enterprise.inject.spi.BeforeBeanDiscovery}.
+ * Later at boot time the Extension can lookup it's parent Extension instance and
+ * e.g. check which classes got scanned in the parent ClassLoader.
+ *
+ * The ExtensionInfo automatically gets removed if the webapp gets undeployed.
+ */
+public final class ParentExtensionStorage
+{
+
+    private static Set<ExtensionStorageInfo> extensionStorage = new HashSet<ExtensionStorageInfo>();
+
+    private ParentExtensionStorage()
+    {
+        // utility class ct
+    }
+
+    /**
+     * Add info about an Extension to our storage
+     * This method is usually called during boostrap via {@code &#064;Observes BeforeBeanDiscovery}.
+     */
+    public static synchronized void addExtension(Extension extension)
+    {
+        removeAbandonedExtensions();
+
+        ClassLoader classLoader = ClassUtils.getClassLoader(null);
+        extensionStorage.add(new ExtensionStorageInfo(classLoader, extension));
+    }
+
+    /**
+     * When adding a new Extension we also clean up ExtensionInfos
+     * from ClassLoaders which got unloaded.
+     */
+    private static void removeAbandonedExtensions()
+    {
+        Iterator<ExtensionStorageInfo> it = extensionStorage.iterator();
+        while (it.hasNext())
+        {
+            ExtensionStorageInfo info = it.next();
+            if (info.isAbandoned())
+            {
+                it.remove();
+            }
+        }
+    }
+
+    /**
+     * @return the Extension from the same type but registered in a hierarchic 'parent' BeanManager
+     */
+    public static synchronized <T extends Extension> T getParentExtension(Extension extension)
+    {
+        ClassLoader parentClassLoader = ClassUtils.getClassLoader(null).getParent();
+
+        Iterator<ExtensionStorageInfo> extIt = extensionStorage.iterator();
+        while (extIt.hasNext())
+        {
+            ExtensionStorageInfo extensionInfo = extIt.next();
+            if (!extensionInfo.isAbandoned() && // weak reference case
+                extension.getClass().equals(extensionInfo.getExtension().getClass()) &&
+                extensionInfo.getClassLoader().equals(parentClassLoader))
+            {
+                return (T) extensionInfo.getExtension();
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Information about an Extension instance and in which classloader it got used
+     */
+    private static class ExtensionStorageInfo
+    {
+        // we use WeakReferences to allow perfect unloading of any webapp ClassLoader
+        private final WeakReference<ClassLoader> classLoader;
+        private final WeakReference<Extension> extension;
+
+        public ExtensionStorageInfo(ClassLoader classLoader, Extension extension)
+        {
+            this.classLoader = new WeakReference<ClassLoader>(classLoader);
+            this.extension = new WeakReference<Extension>(extension);
+        }
+
+        boolean isAbandoned()
+        {
+            return classLoader.get() == null || extension.get() == null;
+        }
+
+        ClassLoader getClassLoader()
+        {
+            return classLoader.get();
+        }
+
+        Extension getExtension()
+        {
+            return extension.get();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/bbd5fa8b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/message/MessageBundleExtension.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/message/MessageBundleExtension.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/message/MessageBundleExtension.java
index 5636529..1733f5f 100644
--- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/message/MessageBundleExtension.java
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/message/MessageBundleExtension.java
@@ -1,201 +1,216 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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 org.apache.deltaspike.core.impl.message;
-
-import java.io.Serializable;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.enterprise.context.spi.CreationalContext;
-import javax.enterprise.event.Observes;
-import javax.enterprise.inject.spi.*;
-
-import org.apache.deltaspike.core.api.literal.DefaultLiteral;
-import org.apache.deltaspike.core.api.message.Message;
-import org.apache.deltaspike.core.api.message.MessageBundle;
-import org.apache.deltaspike.core.api.message.MessageTemplate;
-import org.apache.deltaspike.core.api.provider.BeanProvider;
-import org.apache.deltaspike.core.api.provider.DependentProvider;
-import org.apache.deltaspike.core.util.ClassUtils;
-import org.apache.deltaspike.core.util.bean.BeanBuilder;
-import org.apache.deltaspike.core.spi.activation.Deactivatable;
-import org.apache.deltaspike.core.util.ClassDeactivationUtils;
-import org.apache.deltaspike.core.util.metadata.builder.ContextualLifecycle;
-
-/**
- * Extension for handling {@link MessageBundle}s.
- *
- * @see MessageBundle
- * @see MessageTemplate
- */
-public class MessageBundleExtension implements Extension, Deactivatable
-{
-    private final Collection<AnnotatedType<?>> messageBundleTypes = new HashSet<AnnotatedType<?>>();
-
-    private List<String> deploymentErrors = new ArrayList<String>();
-
-    private Boolean isActivated = true;
-
-    @SuppressWarnings("UnusedDeclaration")
-    protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery)
-    {
-        isActivated = ClassDeactivationUtils.isActivated(getClass());
-    }
-
-    @SuppressWarnings("UnusedDeclaration")
-    protected void detectInterfaces(@Observes ProcessAnnotatedType processAnnotatedType)
-    {
-        if (!isActivated)
-        {
-            return;
-        }
-
-        AnnotatedType<?> type = processAnnotatedType.getAnnotatedType();
-
-        if (type.isAnnotationPresent(MessageBundle.class))
-        {
-            if (validateMessageBundle(type.getJavaClass()))
-            {
-                messageBundleTypes.add(type);
-            }
-        }
-    }
-
-    /**
-     * @return <code>true</code> if all is well
-     */
-    private boolean validateMessageBundle(Class<?> currentClass)
-    {
-        boolean ok = true;
-
-        // sanity check: annotated class must be an Interface
-        if (!currentClass.isInterface())
-        {
-            deploymentErrors.add("@MessageBundle must only be used on Interfaces, but got used on class " +
-                    currentClass.getName());
-            return false;
-        }
-
-        for (Method currentMethod : currentClass.getDeclaredMethods())
-        {
-            if (!currentMethod.isAnnotationPresent(MessageTemplate.class))
-            {
-                continue;
-            }
-            
-            if (String.class.isAssignableFrom(currentMethod.getReturnType()))
-            {
-                continue;
-            }
-
-            if (Message.class.isAssignableFrom(currentMethod.getReturnType()))
-            {
-                continue;
-            }
-
-            deploymentErrors.add(currentMethod.getReturnType().getName() + " isn't supported. Details: " +
-                    currentMethod.getDeclaringClass().getName() + "#" + currentMethod.getName() +
-                    " only " + String.class.getName() + " or " + Message.class.getName());
-            ok = false;
-        }
-
-        return ok;
-    }
-
-    @SuppressWarnings("UnusedDeclaration")
-    protected void installMessageBundleProducerBeans(@Observes AfterBeanDiscovery abd, BeanManager beanManager)
-    {
-        if (!deploymentErrors.isEmpty())
-        {
-            abd.addDefinitionError(new IllegalArgumentException("The following MessageBundle problems where found: " +
-                    Arrays.toString(deploymentErrors.toArray())));
-            return;
-        }
-
-        for (AnnotatedType<?> type : messageBundleTypes)
-        {
-            abd.addBean(createMessageBundleBean(type, beanManager));
-        }
-    }
-
-    private <T> Bean<T> createMessageBundleBean(AnnotatedType<T> annotatedType,
-                                                BeanManager beanManager)
-    {
-        BeanBuilder<T> beanBuilder = new BeanBuilder<T>(beanManager).readFromType(annotatedType);
-
-        beanBuilder.beanLifecycle(new MessageBundleLifecycle<T>(beanManager));
-
-        beanBuilder.types(annotatedType.getJavaClass(), Object.class, Serializable.class);
-        beanBuilder.addQualifier(new DefaultLiteral());
-
-        beanBuilder.passivationCapable(true);
-        beanBuilder.scope(ApplicationScoped.class); // needs to be a normalscope due to a bug in older Weld versions
-        beanBuilder.id("MessageBundleBean#" + annotatedType.getJavaClass().getName());
-
-        return beanBuilder.create();
-    }
-
-    @SuppressWarnings("UnusedDeclaration")
-    protected void cleanup(@Observes AfterDeploymentValidation afterDeploymentValidation)
-    {
-        messageBundleTypes.clear();
-    }
-
-    private static class MessageBundleLifecycle<T> implements ContextualLifecycle<T>
-    {
-        private final BeanManager beanManager;
-
-        private DependentProvider<MessageBundleInvocationHandler> invocationHandlerProvider;
-
-        private MessageBundleLifecycle(BeanManager beanManager)
-        {
-            this.beanManager = beanManager;
-        }
-
-        @Override
-        public T create(Bean<T> bean, CreationalContext<T> creationalContext)
-        {
-            invocationHandlerProvider = BeanProvider.getDependent(beanManager, MessageBundleInvocationHandler.class);
-
-            return createMessageBundleProxy((Class<T>) bean.getBeanClass(), invocationHandlerProvider.get());
-        }
-
-        @Override
-        public void destroy(Bean<T> bean, T instance, CreationalContext<T> creationalContext)
-        {
-            if (invocationHandlerProvider != null)
-            {
-                invocationHandlerProvider.destroy();
-            }
-        }
-
-        private <T> T createMessageBundleProxy(Class<T> type, MessageBundleInvocationHandler handler)
-        {
-            return type.cast(Proxy.newProxyInstance(ClassUtils.getClassLoader(null),
-                    new Class<?>[]{type, Serializable.class}, handler));
-        }
-
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.deltaspike.core.impl.message;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.event.Observes;
+import javax.enterprise.inject.spi.AfterBeanDiscovery;
+import javax.enterprise.inject.spi.AfterDeploymentValidation;
+import javax.enterprise.inject.spi.AnnotatedType;
+import javax.enterprise.inject.spi.Bean;
+import javax.enterprise.inject.spi.BeanManager;
+import javax.enterprise.inject.spi.BeforeBeanDiscovery;
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.ProcessAnnotatedType;
+
+import org.apache.deltaspike.core.api.literal.DefaultLiteral;
+import org.apache.deltaspike.core.api.message.Message;
+import org.apache.deltaspike.core.api.message.MessageBundle;
+import org.apache.deltaspike.core.api.message.MessageTemplate;
+import org.apache.deltaspike.core.api.provider.BeanProvider;
+import org.apache.deltaspike.core.api.provider.DependentProvider;
+import org.apache.deltaspike.core.util.ClassUtils;
+import org.apache.deltaspike.core.util.ParentExtensionStorage;
+import org.apache.deltaspike.core.util.bean.BeanBuilder;
+import org.apache.deltaspike.core.spi.activation.Deactivatable;
+import org.apache.deltaspike.core.util.ClassDeactivationUtils;
+import org.apache.deltaspike.core.util.metadata.builder.ContextualLifecycle;
+
+/**
+ * Extension for handling {@link MessageBundle}s.
+ *
+ * @see MessageBundle
+ * @see MessageTemplate
+ */
+public class MessageBundleExtension implements Extension, Deactivatable
+{
+    private final Collection<AnnotatedType<?>> messageBundleTypes = new HashSet<AnnotatedType<?>>();
+
+    private List<String> deploymentErrors = new ArrayList<String>();
+
+    private Boolean isActivated = true;
+
+    @SuppressWarnings("UnusedDeclaration")
+    protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery)
+    {
+        isActivated = ClassDeactivationUtils.isActivated(getClass());
+        ParentExtensionStorage.addExtension(this);
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    protected void detectInterfaces(@Observes ProcessAnnotatedType processAnnotatedType)
+    {
+        if (!isActivated)
+        {
+            return;
+        }
+
+        AnnotatedType<?> type = processAnnotatedType.getAnnotatedType();
+
+        if (type.isAnnotationPresent(MessageBundle.class))
+        {
+            if (validateMessageBundle(type.getJavaClass()))
+            {
+                messageBundleTypes.add(type);
+            }
+        }
+    }
+
+    /**
+     * @return <code>true</code> if all is well
+     */
+    private boolean validateMessageBundle(Class<?> currentClass)
+    {
+        boolean ok = true;
+
+        // sanity check: annotated class must be an Interface
+        if (!currentClass.isInterface())
+        {
+            deploymentErrors.add("@MessageBundle must only be used on Interfaces, but got used on class " +
+                    currentClass.getName());
+            return false;
+        }
+
+        for (Method currentMethod : currentClass.getDeclaredMethods())
+        {
+            if (!currentMethod.isAnnotationPresent(MessageTemplate.class))
+            {
+                continue;
+            }
+            
+            if (String.class.isAssignableFrom(currentMethod.getReturnType()))
+            {
+                continue;
+            }
+
+            if (Message.class.isAssignableFrom(currentMethod.getReturnType()))
+            {
+                continue;
+            }
+
+            deploymentErrors.add(currentMethod.getReturnType().getName() + " isn't supported. Details: " +
+                    currentMethod.getDeclaringClass().getName() + "#" + currentMethod.getName() +
+                    " only " + String.class.getName() + " or " + Message.class.getName());
+            ok = false;
+        }
+
+        return ok;
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    protected void installMessageBundleProducerBeans(@Observes AfterBeanDiscovery abd, BeanManager beanManager)
+    {
+        if (!deploymentErrors.isEmpty())
+        {
+            abd.addDefinitionError(new IllegalArgumentException("The following MessageBundle problems where found: " +
+                    Arrays.toString(deploymentErrors.toArray())));
+            return;
+        }
+
+        MessageBundleExtension parentExtension = ParentExtensionStorage.getParentExtension(this);
+        if (parentExtension != null)
+        {
+            messageBundleTypes.addAll(parentExtension.messageBundleTypes);
+        }
+
+        for (AnnotatedType<?> type : messageBundleTypes)
+        {
+            abd.addBean(createMessageBundleBean(type, beanManager));
+        }
+    }
+
+    private <T> Bean<T> createMessageBundleBean(AnnotatedType<T> annotatedType,
+                                                BeanManager beanManager)
+    {
+        BeanBuilder<T> beanBuilder = new BeanBuilder<T>(beanManager).readFromType(annotatedType);
+
+        beanBuilder.beanLifecycle(new MessageBundleLifecycle<T>(beanManager));
+
+        beanBuilder.types(annotatedType.getJavaClass(), Object.class, Serializable.class);
+        beanBuilder.addQualifier(new DefaultLiteral());
+
+        beanBuilder.passivationCapable(true);
+        beanBuilder.scope(ApplicationScoped.class); // needs to be a normalscope due to a bug in older Weld versions
+        beanBuilder.id("MessageBundleBean#" + annotatedType.getJavaClass().getName());
+
+        return beanBuilder.create();
+    }
+
+    @SuppressWarnings("UnusedDeclaration")
+    protected void cleanup(@Observes AfterDeploymentValidation afterDeploymentValidation)
+    {
+        messageBundleTypes.clear();
+    }
+
+    private static class MessageBundleLifecycle<T> implements ContextualLifecycle<T>
+    {
+        private final BeanManager beanManager;
+
+        private DependentProvider<MessageBundleInvocationHandler> invocationHandlerProvider;
+
+        private MessageBundleLifecycle(BeanManager beanManager)
+        {
+            this.beanManager = beanManager;
+        }
+
+        @Override
+        public T create(Bean<T> bean, CreationalContext<T> creationalContext)
+        {
+            invocationHandlerProvider = BeanProvider.getDependent(beanManager, MessageBundleInvocationHandler.class);
+
+            return createMessageBundleProxy((Class<T>) bean.getBeanClass(), invocationHandlerProvider.get());
+        }
+
+        @Override
+        public void destroy(Bean<T> bean, T instance, CreationalContext<T> creationalContext)
+        {
+            if (invocationHandlerProvider != null)
+            {
+                invocationHandlerProvider.destroy();
+            }
+        }
+
+        private <T> T createMessageBundleProxy(Class<T> type, MessageBundleInvocationHandler handler)
+        {
+            return type.cast(Proxy.newProxyInstance(ClassUtils.getClassLoader(null),
+                    new Class<?>[]{type, Serializable.class}, handler));
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/bbd5fa8b/deltaspike/modules/security/impl/src/main/java/org/apache/deltaspike/security/impl/extension/SecurityExtension.java
----------------------------------------------------------------------
diff --git a/deltaspike/modules/security/impl/src/main/java/org/apache/deltaspike/security/impl/extension/SecurityExtension.java b/deltaspike/modules/security/impl/src/main/java/org/apache/deltaspike/security/impl/extension/SecurityExtension.java
index 375def0..95f0fc5 100644
--- a/deltaspike/modules/security/impl/src/main/java/org/apache/deltaspike/security/impl/extension/SecurityExtension.java
+++ b/deltaspike/modules/security/impl/src/main/java/org/apache/deltaspike/security/impl/extension/SecurityExtension.java
@@ -21,6 +21,7 @@ package org.apache.deltaspike.security.impl.extension;
 
 import org.apache.deltaspike.core.spi.activation.Deactivatable;
 import org.apache.deltaspike.core.util.ClassDeactivationUtils;
+import org.apache.deltaspike.core.util.ParentExtensionStorage;
 import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
 import org.apache.deltaspike.security.api.authorization.Secures;
 import org.apache.deltaspike.security.api.authorization.SecurityDefinitionException;
@@ -56,6 +57,7 @@ public class SecurityExtension implements Extension, Deactivatable
     {
         isActivated = ClassDeactivationUtils.isActivated(getClass());
         securityMetaDataStorage = new SecurityMetaDataStorage();
+        ParentExtensionStorage.addExtension(this);
     }
 
     //workaround for OWB
@@ -136,6 +138,18 @@ public class SecurityExtension implements Extension, Deactivatable
         }
 
         SecurityMetaDataStorage metaDataStorage = getMetaDataStorage();
+
+        SecurityExtension parentExtension = ParentExtensionStorage.getParentExtension(this);
+        if (parentExtension != null)
+        {
+            // also add the authorizers from the parent extension
+            Set<Authorizer> parentAuthorizers = parentExtension.getMetaDataStorage().getAuthorizers();
+            for (Authorizer parentAuthorizer : parentAuthorizers)
+            {
+                metaDataStorage.addAuthorizer(parentAuthorizer);
+            }
+        }
+
         metaDataStorage.registerSecuredMethods();
 
         for (final AnnotatedMethod<?> method : metaDataStorage.getSecuredMethods())