Author: dkulp Date: Thu Apr 4 15:57:32 2013 New Revision: 1464611 URL: http://svn.apache.org/r1464611 Log: Merged revisions 1464607 via git cherry-pick from https://svn.apache.org/repos/asf/cxf/branches/2.6.x-fixes
........ r1464607 | dkulp | 2013-04-04 11:55:23 -0400 (Thu, 04 Apr 2013) | 18 lines Merged revisions 1464600 via git cherry-pick from https://svn.apache.org/repos/asf/cxf/branches/2.7.x-fixes ........ r1464600 | dkulp | 2013-04-04 11:49:54 -0400 (Thu, 04 Apr 2013) | 10 lines Merged revisions 1464598 via git cherry-pick from https://svn.apache.org/repos/asf/cxf/trunk ........ r1464598 | dkulp | 2013-04-04 11:40:01 -0400 (Thu, 04 Apr 2013) | 2 lines [CXF-4529] Work a bit harder to get the authentcators cleaned out. ........ ........ ........ Added: cxf/branches/2.5.x-fixes/rt/transports/http/src/test/java/org/apache/cxf/transport/http/CXFAuthenticatorCleanupTest.java Modified: cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/CXFAuthenticator.java cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java Modified: cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/CXFAuthenticator.java URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/CXFAuthenticator.java?rev=1464611&r1=1464610&r2=1464611&view=diff ============================================================================== --- cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/CXFAuthenticator.java (original) +++ cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/CXFAuthenticator.java Thu Apr 4 15:57:32 2013 @@ -38,21 +38,28 @@ import org.apache.cxf.transport.Conduit; * */ public class CXFAuthenticator extends Authenticator { - static boolean setup; - private static final CXFAuthenticator INSTANCE = new CXFAuthenticator(); + static CXFAuthenticator instance; public CXFAuthenticator() { } - + public static synchronized void addAuthenticator() { - if (!setup) { + if (instance == null) { + instance = new CXFAuthenticator(); Authenticator wrapped = null; for (final Field f : Authenticator.class.getDeclaredFields()) { if (f.getType().equals(Authenticator.class)) { ReflectionUtil.setAccessible(f); try { wrapped = (Authenticator)f.get(null); + if (wrapped != null + && wrapped.getClass().getName().equals(ReferencingAuthenticator.class.getName())) { + Method m = wrapped.getClass().getMethod("check"); + m.setAccessible(true); + m.invoke(wrapped); + } + wrapped = (Authenticator)f.get(null); } catch (Exception e) { //ignore } @@ -60,23 +67,25 @@ public class CXFAuthenticator extends Au } try { - InputStream ins = ReferencingAuthenticator.class.getResourceAsStream("ReferencingAuthenticator.class"); - final byte b[] = IOUtils.readBytesFromStream(ins); ClassLoader loader = new URLClassLoader(new URL[0], ClassLoader.getSystemClassLoader()); Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE); + + InputStream ins = ReferencingAuthenticator.class + .getResourceAsStream("ReferencingAuthenticator.class"); + byte b[] = IOUtils.readBytesFromStream(ins); + ReflectionUtil.setAccessible(m).invoke(loader, ReferencingAuthenticator.class.getName(), b, 0, b.length); Class<?> cls = loader.loadClass(ReferencingAuthenticator.class.getName()); Authenticator auth = (Authenticator)cls.getConstructor(Authenticator.class, Authenticator.class) - .newInstance(INSTANCE, wrapped); + .newInstance(instance, wrapped); Authenticator.setDefault(auth); } catch (Throwable t) { //ignore t.printStackTrace(); } - setup = true; } } Modified: cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java?rev=1464611&r1=1464610&r2=1464611&view=diff ============================================================================== --- cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java (original) +++ cxf/branches/2.5.x-fixes/rt/transports/http/src/main/java/org/apache/cxf/transport/http/ReferencingAuthenticator.java Thu Apr 4 15:57:32 2013 @@ -18,7 +18,8 @@ */ package org.apache.cxf.transport.http; -import java.lang.ref.SoftReference; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -26,14 +27,19 @@ import java.net.Authenticator; import java.net.PasswordAuthentication; public class ReferencingAuthenticator extends Authenticator { - SoftReference<Authenticator> auth; - Authenticator wrapped; + final Reference<Authenticator> auth; + final Authenticator wrapped; public ReferencingAuthenticator(Authenticator cxfauth, Authenticator wrapped) { - this.auth = new SoftReference<Authenticator>(cxfauth); + this.auth = new WeakReference<Authenticator>(cxfauth); this.wrapped = wrapped; } + @Override protected PasswordAuthentication getPasswordAuthentication() { + Authenticator cxfauth = auth.get(); + if (cxfauth == null) { + remove(); + } PasswordAuthentication pauth = null; if (wrapped != null) { try { @@ -45,22 +51,71 @@ public class ReferencingAuthenticator ex pauth = null; } } + if (cxfauth != null) { + try { + pauth = tryWith(cxfauth); + } catch (Exception e1) { + pauth = null; + } + } + return pauth; + } + + public final void check() { Authenticator cxfauth = auth.get(); if (cxfauth == null) { + remove(); + } + if (wrapped != null && wrapped.getClass().getName().equals(ReferencingAuthenticator.class.getName())) { try { - Authenticator.setDefault(wrapped); + Method m = wrapped.getClass().getMethod("check"); + m.setAccessible(true); + m.invoke(wrapped); } catch (Throwable t) { //ignore } - } else { - try { - pauth = tryWith(cxfauth); - } catch (Exception e1) { - pauth = null; + } + } + private void remove() { + try { + for (final Field f : Authenticator.class.getDeclaredFields()) { + if (f.getType().equals(Authenticator.class)) { + try { + f.setAccessible(true); + Authenticator o = (Authenticator)f.get(null); + if (o == this) { + //this is at the root of any chain of authenticators + Authenticator.setDefault(wrapped); + } else { + removeFromChain(o); + } + } catch (Exception e) { + //ignore + } + } } + } catch (Throwable t) { + //ignore } - return pauth; - } + } + private void removeFromChain(Authenticator a) { + try { + if (a.getClass().getName().equals(ReferencingAuthenticator.class.getName())) { + //multiple referencing authenticators, we can remove ourself + Field f2 = a.getClass().getDeclaredField("wrapped"); + f2.setAccessible(true); + Authenticator a2 = (Authenticator)f2.get(a); + if (a2 == this) { + f2.set(a, wrapped); + } else { + removeFromChain(a2); + } + } + } catch (Throwable t) { + //ignore + } + } + PasswordAuthentication tryWith(Authenticator a) throws Exception { if (a == null) { return null; Added: cxf/branches/2.5.x-fixes/rt/transports/http/src/test/java/org/apache/cxf/transport/http/CXFAuthenticatorCleanupTest.java URL: http://svn.apache.org/viewvc/cxf/branches/2.5.x-fixes/rt/transports/http/src/test/java/org/apache/cxf/transport/http/CXFAuthenticatorCleanupTest.java?rev=1464611&view=auto ============================================================================== --- cxf/branches/2.5.x-fixes/rt/transports/http/src/test/java/org/apache/cxf/transport/http/CXFAuthenticatorCleanupTest.java (added) +++ cxf/branches/2.5.x-fixes/rt/transports/http/src/test/java/org/apache/cxf/transport/http/CXFAuthenticatorCleanupTest.java Thu Apr 4 15:57:32 2013 @@ -0,0 +1,146 @@ +/** + * 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.cxf.transport.http; + +import java.net.Authenticator; +import java.net.InetAddress; +import java.net.PasswordAuthentication; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +/** + * + */ +public class CXFAuthenticatorCleanupTest { + + /** + * + */ + public CXFAuthenticatorCleanupTest() { + } + + + @Test + public void runCleanupTestStrongRef() throws Exception { + final List<Integer> traceLengths = new ArrayList<Integer>(); + + //create a chain of CXFAuthenticators, strongly referenced to prevent cleanups + Authenticator.setDefault(new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + traceLengths.add(Thread.currentThread().getStackTrace().length); + return super.getPasswordAuthentication(); + } + }); + + + InetAddress add = InetAddress.getLocalHost(); + Authenticator.requestPasswordAuthentication("localhost", add, + 8080, "http", "hello", "http"); + + List<CXFAuthenticator> list = new ArrayList<CXFAuthenticator>(); + for (int x = 0; x < 20; x++) { + CXFAuthenticator.addAuthenticator(); + list.add(CXFAuthenticator.instance); + CXFAuthenticator.instance = null; + } + + Authenticator.requestPasswordAuthentication("localhost", add, + 8080, "http", "hello", "http"); + for (int x = 9; x > 0; x -= 2) { + list.remove(x); + } + for (int x = 0; x < 10; x++) { + System.gc(); + Authenticator.requestPasswordAuthentication("localhost", add, + 8080, "http", "hello", "http"); + } + list.clear(); + for (int x = 0; x < 10; x++) { + System.gc(); + Authenticator.requestPasswordAuthentication("localhost", add, + 8080, "http", "hello", "http"); + } + Assert.assertEquals(22, traceLengths.size()); + //first trace would be just the raw authenticator above + int raw = traceLengths.get(0); + //second would be the trace with ALL the auths + int all = traceLengths.get(1); + //after remove of 5 and some gc's + int some = traceLengths.get(11); + //after clear and gc's + int none = traceLengths.get(traceLengths.size() - 1); + + //System.out.println(traceLengths); + Assert.assertTrue(all > (raw + 20 * 3)); //all should be A LOT above raw + Assert.assertTrue(all > raw); + Assert.assertTrue(all > some); + Assert.assertTrue(some > none); + Assert.assertEquals(raw, none); + } + @Test + public void runCleanupTestWeakRef() throws Exception { + InetAddress add = InetAddress.getLocalHost(); + final List<Integer> traceLengths = new ArrayList<Integer>(); + //create a chain of CXFAuthenticators, strongly referenced to prevent cleanups + Authenticator.setDefault(new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + traceLengths.add(Thread.currentThread().getStackTrace().length); + return super.getPasswordAuthentication(); + } + }); + Authenticator.requestPasswordAuthentication("localhost", add, + 8080, "http", "hello", "http"); + + + for (int x = 0; x < 20; x++) { + CXFAuthenticator.addAuthenticator(); + CXFAuthenticator.instance = null; + System.gc(); + } + CXFAuthenticator.addAuthenticator(); + System.gc(); + + Authenticator.requestPasswordAuthentication("localhost", add, + 8080, "http", "hello", "http"); + CXFAuthenticator.instance = null; + for (int x = 0; x < 10; x++) { + System.gc(); + Authenticator.requestPasswordAuthentication("localhost", add, + 8080, "http", "hello", "http"); + } + Assert.assertEquals(12, traceLengths.size()); + + //first trace would be just the raw authenticator above + int raw = traceLengths.get(0); + //second trace should still have an Authenticator added + int one = traceLengths.get(1); + //after clear and gc's + int none = traceLengths.get(traceLengths.size() - 1); + + //System.out.println(traceLengths); + Assert.assertTrue(one < (raw + (20 * 2))); //one should only be slightly above raw + Assert.assertTrue(one > raw); + Assert.assertTrue(one > none); + Assert.assertEquals(raw, none); + } +}
