Author: markt Date: Mon Feb 13 14:13:27 2017 New Revision: 1782775 URL: http://svn.apache.org/viewvc?rev=1782775&view=rev Log: Refactor default JASPIC uthConfigFactory implementation and implement notifications.
Added: tomcat/trunk/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java Modified: tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java?rev=1782775&r1=1782774&r2=1782775&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java (original) +++ tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java Mon Feb 13 14:13:27 2017 @@ -22,7 +22,6 @@ import java.lang.reflect.InvocationTarge import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -52,7 +51,18 @@ public class AuthConfigFactoryImpl exten private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private final Map<String,RegistrationContextImpl> registrations = new ConcurrentHashMap<>(); + private static String DEFAULT_REGISTRATION_ID = getRegistrationID(null, null); + + private final Map<String,RegistrationContextImpl> layerAppContextRegistrations = + new ConcurrentHashMap<>(); + private final Map<String,RegistrationContextImpl> appContextRegistrations = + new ConcurrentHashMap<>(); + private final Map<String,RegistrationContextImpl> layerRegistrations = + new ConcurrentHashMap<>(); + // Note: Although there will only ever be a maximum of one entry in this + // Map, use a ConcurrentHashMap for consistency + private volatile Map<String,RegistrationContextImpl> defaultRegistration = + new ConcurrentHashMap<>(1); public AuthConfigFactoryImpl() { @@ -63,10 +73,12 @@ public class AuthConfigFactoryImpl exten @Override public AuthConfigProvider getConfigProvider(String layer, String appContext, RegistrationListener listener) { - String registrationID = getRegistrationID(layer, appContext); - RegistrationContextImpl registrationContext = registrations.get(registrationID); + RegistrationContextImpl registrationContext = + findRegistrationContextImpl(layer, appContext); if (registrationContext != null) { - registrationContext.addListener(null); + RegistrationListenerWrapper wrapper = new RegistrationListenerWrapper( + layer, appContext, listener); + registrationContext.addListener(wrapper); return registrationContext.getProvider(); } return null; @@ -109,8 +121,9 @@ public class AuthConfigFactoryImpl exten } String registrationID = getRegistrationID(layer, appContext); - registrations.put(registrationID, - new RegistrationContextImpl(layer, appContext, description, true, provider, properties)); + RegistrationContextImpl registrationContextImpl = new RegistrationContextImpl( + layer, appContext, description, true, provider, properties); + addRegistrationContextImpl(layer, appContext, registrationID, registrationContextImpl); return registrationID; } @@ -123,22 +136,115 @@ public class AuthConfigFactoryImpl exten provider.getClass().getName(), layer, appContext)); } String registrationID = getRegistrationID(layer, appContext); - registrations.put(registrationID, - new RegistrationContextImpl(layer, appContext, description, false, provider, null)); + RegistrationContextImpl registrationContextImpl = new RegistrationContextImpl( + layer, appContext, description, false, provider, null); + addRegistrationContextImpl(layer, appContext, registrationID, registrationContextImpl); return registrationID; } + private void addRegistrationContextImpl(String layer, String appContext, + String registrationID, RegistrationContextImpl registrationContextImpl) { + RegistrationContextImpl previous = null; + + // Add the registration, noting any registration it replaces + if (layer != null && appContext != null) { + previous = layerAppContextRegistrations.put(registrationID, registrationContextImpl); + } else if (layer == null && appContext != null) { + previous = appContextRegistrations.put(registrationID, registrationContextImpl); + } else if (layer != null && appContext == null) { + previous = layerRegistrations.put(registrationID, registrationContextImpl); + } else { + previous = defaultRegistration.put(registrationID, registrationContextImpl); + } + + if (previous == null) { + // No match with previous registration so need to check listeners + // for all less specific registrations to see if they need to be + // notified of this new registration. That there is no exact match + // with a previous registration allows a few short-cuts to be taken + if (layer != null && appContext != null) { + // Need to check existing appContext registrations + // (and layer and default) + // appContext must match + RegistrationContextImpl registration = + appContextRegistrations.get(getRegistrationID(null, appContext)); + if (registration != null) { + for (RegistrationListenerWrapper wrapper : registration.listeners) { + if (layer.equals(wrapper.getMessageLayer()) && + appContext.equals(wrapper.getAppContext())) { + registration.listeners.remove(wrapper); + wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext); + } + } + } + } + if (appContext != null) { + // Need to check existing layer registrations + // (and default) + // Need to check registrations for all layers + for (RegistrationContextImpl registration : layerRegistrations.values()) { + for (RegistrationListenerWrapper wrapper : registration.listeners) { + if (appContext.equals(wrapper.getAppContext())) { + registration.listeners.remove(wrapper); + wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext); + } + } + } + } + if (layer != null || appContext != null) { + // Need to check default + for (RegistrationContextImpl registration : defaultRegistration.values()) { + for (RegistrationListenerWrapper wrapper : registration.listeners) { + if (appContext != null && appContext.equals(wrapper.getAppContext()) || + layer != null && layer.equals(wrapper.getMessageLayer())) { + registration.listeners.remove(wrapper); + wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext); + } + } + } + } + } else { + // Replaced an existing registration so need to notify those listeners + for (RegistrationListenerWrapper wrapper : previous.listeners) { + previous.listeners.remove(wrapper); + wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext); + } + } + } + + @Override public boolean removeRegistration(String registrationID) { - return registrations.remove(registrationID) != null; + RegistrationContextImpl registration = null; + if (DEFAULT_REGISTRATION_ID.equals(registrationID)) { + registration = defaultRegistration.remove(registrationID); + } + if (registration == null) { + registration = layerAppContextRegistrations.remove(registrationID); + } + if (registration == null) { + registration = appContextRegistrations.remove(registrationID); + } + if (registration == null) { + registration = layerRegistrations.remove(registrationID); + } + + if (registration == null) { + return false; + } else { + for (RegistrationListenerWrapper wrapper : registration.listeners) { + wrapper.getListener().notify(wrapper.getMessageLayer(), wrapper.getAppContext()); + } + return true; + } } @Override public String[] detachListener(RegistrationListener listener, String layer, String appContext) { String registrationID = getRegistrationID(layer, appContext); - RegistrationContextImpl registrationContext = registrations.get(registrationID); + RegistrationContextImpl registrationContext = findRegistrationContextImpl(layer, appContext); if (registrationContext.removeListener(listener)) { return new String[] { registrationID }; } @@ -148,23 +254,47 @@ public class AuthConfigFactoryImpl exten @Override public String[] getRegistrationIDs(AuthConfigProvider provider) { + List<String> result = new ArrayList<>(); if (provider == null) { - return registrations.keySet().toArray(EMPTY_STRING_ARRAY); + result.addAll(layerAppContextRegistrations.keySet()); + result.addAll(appContextRegistrations.keySet()); + result.addAll(layerRegistrations.keySet()); + if (defaultRegistration != null) { + result.add(DEFAULT_REGISTRATION_ID); + } } else { - List<String> results = new ArrayList<>(); - for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) { - if (provider.equals(entry.getValue().getProvider())) { - results.add(entry.getKey()); - } + findProvider(provider, layerAppContextRegistrations, result); + findProvider(provider, appContextRegistrations, result); + findProvider(provider, layerRegistrations, result); + findProvider(provider, defaultRegistration, result); + } + return result.toArray(EMPTY_STRING_ARRAY); + } + + + private void findProvider(AuthConfigProvider provider, + Map<String,RegistrationContextImpl> registrations, List<String> result) { + for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) { + if (provider.equals(entry.getValue().getProvider())) { + result.add(entry.getKey()); } - return results.toArray(EMPTY_STRING_ARRAY); } } @Override public RegistrationContext getRegistrationContext(String registrationID) { - return registrations.get(registrationID); + RegistrationContext result = defaultRegistration.get(registrationID); + if (result == null) { + result = layerAppContextRegistrations.get(registrationID); + } + if (result == null) { + result = appContextRegistrations.get(registrationID); + } + if (result == null) { + result = layerRegistrations.get(registrationID); + } + return result; } @@ -174,8 +304,16 @@ public class AuthConfigFactoryImpl exten } - private String getRegistrationID(String layer, String appContext) { - return layer + ":" + appContext; + private static String getRegistrationID(String layer, String appContext) { + if (layer != null && layer.length() == 0) { + throw new IllegalArgumentException( + sm.getString("authConfigFactoryImpl.zeroLengthMessageLayer")); + } + if (appContext != null && appContext.length() == 0) { + throw new IllegalArgumentException( + sm.getString("authConfigFactoryImpl.zeroLengthAppContext")); + } + return (layer == null ? "" : layer) + ":" + (appContext == null ? "" : appContext); } @@ -200,24 +338,55 @@ public class AuthConfigFactoryImpl exten private void savePersistentRegistrations() { synchronized (CONFIG_FILE_LOCK) { Providers providers = new Providers(); - for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) { - if (entry.getValue().isPersistent()) { - Provider provider = new Provider(); - provider.setAppContext(entry.getValue().getAppContext()); - provider.setClassName(entry.getValue().getProvider().getClass().getName()); - provider.setDescription(entry.getValue().getDescription()); - provider.setLayer(entry.getValue().getMessageLayer()); - for (Entry<String,String> property : entry.getValue().getProperties().entrySet()) { - provider.addProperty(property.getKey(), property.getValue()); - } - providers.addProvider(provider); - } - } + savePersistentProviders(providers, layerAppContextRegistrations); + savePersistentProviders(providers, appContextRegistrations); + savePersistentProviders(providers, layerRegistrations); + savePersistentProviders(providers, defaultRegistration); PersistentProviderRegistrations.writeProviders(providers, CONFIG_FILE); } } + private void savePersistentProviders(Providers providers, + Map<String,RegistrationContextImpl> registrations) { + for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) { + savePersistentProvider(providers, entry.getValue()); + } + } + + + private void savePersistentProvider(Providers providers, + RegistrationContextImpl registrationContextImpl) { + if (registrationContextImpl != null && registrationContextImpl.isPersistent()) { + Provider provider = new Provider(); + provider.setAppContext(registrationContextImpl.getAppContext()); + provider.setClassName(registrationContextImpl.getProvider().getClass().getName()); + provider.setDescription(registrationContextImpl.getDescription()); + provider.setLayer(registrationContextImpl.getMessageLayer()); + for (Entry<String,String> property : registrationContextImpl.getProperties().entrySet()) { + provider.addProperty(property.getKey(), property.getValue()); + } + providers.addProvider(provider); + } + } + + + private RegistrationContextImpl findRegistrationContextImpl(String layer, String appContext) { + RegistrationContextImpl result; + result = layerAppContextRegistrations.get(getRegistrationID(layer, appContext)); + if (result == null) { + result = appContextRegistrations.get(getRegistrationID(null, appContext)); + } + if (result == null) { + result = layerRegistrations.get(getRegistrationID(layer, null)); + } + if (result == null) { + result = defaultRegistration.get(DEFAULT_REGISTRATION_ID); + } + return result; + } + + private static class RegistrationContextImpl implements RegistrationContext { private RegistrationContextImpl(String messageLayer, String appContext, String description, @@ -240,7 +409,7 @@ public class AuthConfigFactoryImpl exten private final boolean persistent; private final AuthConfigProvider provider; private final Map<String,String> properties; - private final List<RegistrationListener> listeners = new CopyOnWriteArrayList<>(); + private final List<RegistrationListenerWrapper> listeners = new CopyOnWriteArrayList<>(); @Override public String getMessageLayer() { @@ -270,7 +439,7 @@ public class AuthConfigFactoryImpl exten } - private void addListener(RegistrationListener listener) { + private void addListener(RegistrationListenerWrapper listener) { if (listener != null) { listeners.add(listener); } @@ -284,13 +453,43 @@ public class AuthConfigFactoryImpl exten private boolean removeListener(RegistrationListener listener) { boolean result = false; - Iterator<RegistrationListener> iter = listeners.iterator(); - while (iter.hasNext()) { - if (iter.next().equals(listener)) { - iter.remove(); + for (RegistrationListenerWrapper wrapper : listeners) { + if (wrapper.getListener().equals(listener)) { + listeners.remove(wrapper); } } return result; } } + + + private static class RegistrationListenerWrapper { + + private final String messageLayer; + private final String appContext; + private final RegistrationListener listener; + + + public RegistrationListenerWrapper(String messageLayer, String appContext, + RegistrationListener listener) { + this.messageLayer = messageLayer; + this.appContext = appContext; + this.listener = listener; + } + + + public String getMessageLayer() { + return messageLayer; + } + + + public String getAppContext() { + return appContext; + } + + + public RegistrationListener getListener() { + return listener; + } + } } Modified: tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties?rev=1782775&r1=1782774&r2=1782775&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties Mon Feb 13 14:13:27 2017 @@ -14,6 +14,8 @@ # limitations under the License. authConfigFactoryImpl.load=Loading persistent provider registrations from [{0}] +authConfigFactoryImpl.zeroLengthAppContext=A zero length application context name is not valid +authConfigFactoryImpl.zeroLengthMessageLayer=A zero length message layer name is not valid authConfigFactoryImpl.registerClass=Registering class [{0}] for layer [{1}] and application context [{2}] authConfigFactoryImpl.registerInstance=Registering instance of type[{0}] for layer [{1}] and application context [{2}] Added: tomcat/trunk/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java?rev=1782775&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java (added) +++ tomcat/trunk/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java Mon Feb 13 14:13:27 2017 @@ -0,0 +1,294 @@ +/** + * 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.catalina.authenticator.jaspic; + +import java.util.ArrayList; +import java.util.List; + +import javax.security.auth.message.config.AuthConfigFactory; +import javax.security.auth.message.config.AuthConfigProvider; +import javax.security.auth.message.config.RegistrationListener; + +import org.junit.Assert; +import org.junit.Test; + +public class TestAuthConfigFactoryImpl { + + @Test + public void testRegistrationNullLayer() { + doTestResistration(null, "AC_1", ":AC_1"); + } + + + @Test + public void testRegistrationNullAppContext() { + doTestResistration("L_1", null, "L_1:"); + } + + + @Test + public void testRegistrationNullLayerAndNullAppContext() { + doTestResistration(null, null, ":"); + } + + + @Test + public void testSearchNoMatch01() { + doTestSearchOrder("foo", "bar", 1); + } + + + @Test + public void testSearchNoMatch02() { + doTestSearchOrder(null, "bar", 1); + } + + + @Test + public void testSearchNoMatch03() { + doTestSearchOrder("foo", null, 1); + } + + + @Test + public void testSearchNoMatch04() { + doTestSearchOrder(null, null, 1); + } + + + @Test + public void testSearchOnlyAppContextMatch01() { + doTestSearchOrder("foo", "AC_1", 2); + } + + + @Test + public void testSearchOnlyAppContextMatch02() { + doTestSearchOrder(null, "AC_1", 2); + } + + + @Test + public void testSearchOnlyAppContextMatch03() { + doTestSearchOrder("L_2", "AC_1", 2); + } + + + @Test + public void testSearchOnlyLayerMatch01() { + doTestSearchOrder("L_1", "bar", 3); + } + + + @Test + public void testSearchOnlyLayerMatch02() { + doTestSearchOrder("L_1", null, 3); + } + + + @Test + public void testSearchOnlyLayerMatch03() { + doTestSearchOrder("L_1", "AC_2", 3); + } + + + @Test + public void testSearchBothMatch() { + doTestSearchOrder("L_2", "AC_2", 4); + } + + + private void doTestSearchOrder(String layer, String appContext, int expected) { + AuthConfigFactory factory = new AuthConfigFactoryImpl(); + AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp1, null, null, "1"); + AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp2, null, "AC_1", "2"); + AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp3, "L_1", null, "3"); + AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp4, "L_2", "AC_2", "4"); + + AuthConfigProvider searchResult = factory.getConfigProvider(layer, appContext, null); + int searchIndex; + if (searchResult == acp1) { + searchIndex = 1; + } else if (searchResult == acp2) { + searchIndex = 2; + } else if (searchResult == acp3) { + searchIndex = 3; + } else if (searchResult == acp4) { + searchIndex = 4; + } else { + searchIndex = -1; + } + Assert.assertEquals(expected, searchIndex); + } + + + private void doTestResistration(String layer, String appContext, String expectedRegId) { + AuthConfigFactory factory = new AuthConfigFactoryImpl(); + AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null); + SimpleRegistrationListener listener = new SimpleRegistrationListener(layer, appContext); + + String regId = factory.registerConfigProvider(acp1, layer, appContext, null); + Assert.assertEquals(expectedRegId, regId); + + factory.getConfigProvider(layer, appContext, listener); + factory.removeRegistration(regId); + Assert.assertTrue(listener.wasCorrectlyCalled()); + + listener.reset(); + factory.registerConfigProvider(acp1, layer, appContext, null); + factory.getConfigProvider(layer, appContext, listener); + // Replace it + AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp2, layer, appContext, null); + Assert.assertTrue(listener.wasCorrectlyCalled()); + } + + + @Test + public void testRegistrationInsertExact01() { + doTestRegistrationInsert("L_3", "AC_2", "L_3", "AC_2"); + } + + + @Test + public void testRegistrationInsertExact02() { + doTestRegistrationInsert("L_2", "AC_3", "L_2", "AC_3"); + } + + + @Test + public void testRegistrationInsertExact03() { + doTestRegistrationInsert("L_4", "AC_4", "L_4", "AC_4"); + } + + + @Test + public void testRegistrationInsertAppContext01() { + doTestRegistrationInsert(null, "AC_3", "L_2", "AC_3"); + } + + + @Test + public void testRegistrationInsertAppContext02() { + doTestRegistrationInsert(null, "AC_4", "L_4", "AC_4"); + } + + + @Test + public void testRegistrationInsertLayer01() { + doTestRegistrationInsert("L_4", null, "L_4", "AC_4"); + } + + + private void doTestRegistrationInsert(String newLayer, String newAppContext, + String expectedListenerLayer, String expectedListenerAppContext) { + // Set up + AuthConfigFactory factory = new AuthConfigFactoryImpl(); + AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp1, "L_1", "AC_1", null); + AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp2, null, "AC_2", null); + AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp3, "L_2", null, null); + AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acp4, null, null, null); + + SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1"); + factory.getConfigProvider("L_1", "AC_1", listener1); + SimpleRegistrationListener listener2 = new SimpleRegistrationListener("L_3", "AC_2"); + factory.getConfigProvider("L_3", "AC_2", listener2); + SimpleRegistrationListener listener3 = new SimpleRegistrationListener("L_2", "AC_3"); + factory.getConfigProvider("L_2", "AC_3", listener3); + SimpleRegistrationListener listener4 = new SimpleRegistrationListener("L_4", "AC_4"); + factory.getConfigProvider("L_4", "AC_4", listener4); + + List<SimpleRegistrationListener> listeners = new ArrayList<>(); + listeners.add(listener1); + listeners.add(listener2); + listeners.add(listener3); + listeners.add(listener4); + + // Register a new provider that will impact some existing registrations + AuthConfigProvider acpNew = new SimpleAuthConfigProvider(null, null); + factory.registerConfigProvider(acpNew, newLayer, newAppContext, null); + + // Check to see if the expected listener fired. + for (SimpleRegistrationListener listener : listeners) { + if (listener.wasCalled()) { + Assert.assertEquals(listener.layer, expectedListenerLayer); + Assert.assertEquals(listener.appContext, expectedListenerAppContext); + Assert.assertTrue(listener.wasCorrectlyCalled()); + } else { + Assert.assertFalse((listener.layer.equals(expectedListenerLayer) && + listener.appContext.equals(expectedListenerAppContext))); + } + } + } + + + private static class SimpleRegistrationListener implements RegistrationListener { + + private final String layer; + private final String appContext; + + private boolean called = false; + private String layerNotified; + private String appContextNotified; + + public SimpleRegistrationListener(String layer, String appContext) { + this.layer = layer; + this.appContext = appContext; + } + + @Override + public void notify(String layer, String appContext) { + called = true; + layerNotified = layer; + appContextNotified = appContext; + } + + + public boolean wasCalled() { + return called; + } + + + public boolean wasCorrectlyCalled() { + return called && areTheSame(layer, layerNotified) && + areTheSame(appContext, appContextNotified); + } + + + public void reset() { + called = false; + layerNotified = null; + appContextNotified = null; + } + + + private static boolean areTheSame(String a, String b) { + if (a == null) { + return b == null; + } + return a.equals(b); + } + } +} Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1782775&r1=1782774&r2=1782775&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Mon Feb 13 14:13:27 2017 @@ -75,6 +75,11 @@ AUTH_HEADER_NAME in SpnegoAuthenticator. Patch provided by Michael Osipov. (violetagg) </fix> + <fix> + The default JASPIC <code>AuthConfigFactory</code> now correctly notifies + registered <code>RegistrationListener</code>s when a new + <code>AuthConfigProvider</code> is registered. (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org