1414 * See the License for the specific language governing permissions and
1515 * limitations under the License.
1616 */
17+
1718package org .apache .commons .io .channels ;
1819
1920import java .lang .reflect .InvocationHandler ;
2829
2930final class CloseShieldChannelHandler implements InvocationHandler {
3031
32+ /**
33+ * Tests whether the given method is allowed to be called after the shield is closed.
34+ *
35+ * @param declaringClass The class declaring the method.
36+ * @param name The method name.
37+ * @param parameterCount The number of parameters.
38+ * @return {@code true} if the method is allowed after {@code close()}, {@code false} otherwise.
39+ */
40+ private static boolean isAllowedAfterClose (final Class <?> declaringClass , final String name , final int parameterCount ) {
41+ // JDK explicitly allows NetworkChannel.supportedOptions() post-close
42+ return parameterCount == 0 && name .equals ("supportedOptions" ) && NetworkChannel .class .equals (declaringClass );
43+ }
44+
45+ /**
46+ * Tests whether the given method returns 'this' (the channel) as per JDK spec.
47+ *
48+ * @param declaringClass The class declaring the method.
49+ * @param name The method name.
50+ * @param parameterCount The number of parameters.
51+ * @return {@code true} if the method returns 'this', {@code false} otherwise.
52+ */
53+ private static boolean returnsThis (final Class <?> declaringClass , final String name , final int parameterCount ) {
54+ if (SeekableByteChannel .class .equals (declaringClass )) {
55+ // SeekableByteChannel.position(long) and truncate(long) return 'this'
56+ return parameterCount == 1 && (name .equals ("position" ) || name .equals ("truncate" ));
57+ }
58+ if (NetworkChannel .class .equals (declaringClass )) {
59+ // NetworkChannel.bind and NetworkChannel.setOption returns 'this'
60+ return parameterCount == 1 && name .equals ("bind" ) || parameterCount == 2 && name .equals ("setOption" );
61+ }
62+ return false ;
63+ }
64+
3165 private final Channel delegate ;
3266 private volatile boolean closed ;
3367
@@ -40,96 +74,57 @@ public Object invoke(final Object proxy, final Method method, final Object[] arg
4074 final Class <?> declaringClass = method .getDeclaringClass ();
4175 final String name = method .getName ();
4276 final int parameterCount = method .getParameterCount ();
43-
4477 // 1) java.lang.Object methods
4578 if (declaringClass == Object .class ) {
4679 return invokeObjectMethod (proxy , method , args );
4780 }
48-
4981 // 2) Channel.close(): mark shield closed, do NOT close the delegate
5082 if (parameterCount == 0 && name .equals ("close" )) {
5183 closed = true ;
5284 return null ;
5385 }
54-
5586 // 3) Channel.isOpen(): reflect shield state only
5687 if (parameterCount == 0 && name .equals ("isOpen" )) {
5788 return !closed && delegate .isOpen ();
5889 }
59-
6090 // 4) After the shield is closed, only allow a tiny allowlist of safe queries
6191 if (closed && !isAllowedAfterClose (declaringClass , name , parameterCount )) {
6292 throw new ClosedChannelException ();
6393 }
64-
6594 // 5) Delegate to the underlying channel and unwrap target exceptions
6695 try {
6796 final Object result = method .invoke (delegate , args );
6897 return returnsThis (declaringClass , name , parameterCount ) ? proxy : result ;
69- } catch (InvocationTargetException e ) {
98+ } catch (final InvocationTargetException e ) {
7099 throw e .getCause ();
71100 }
72101 }
73102
74- /**
75- * Tests whether the given method is allowed to be called after the shield is closed.
76- *
77- * @param declaringClass The class declaring the method.
78- * @param name The method name.
79- * @param parameterCount The number of parameters.
80- * @return {@code true} if the method is allowed after {@code close()}, {@code false} otherwise.
81- */
82- private static boolean isAllowedAfterClose (Class <?> declaringClass , String name , int parameterCount ) {
83- // JDK explicitly allows NetworkChannel.supportedOptions() post-close
84- return parameterCount == 0 && name .equals ("supportedOptions" ) && NetworkChannel .class .equals (declaringClass );
85- }
86-
87- /**
88- * Tests whether the given method returns 'this' (the channel) as per JDK spec.
89- *
90- * @param declaringClass The class declaring the method.
91- * @param name The method name.
92- * @param parameterCount The number of parameters.
93- * @return {@code true} if the method returns 'this', {@code false} otherwise.
94- */
95- private static boolean returnsThis (Class <?> declaringClass , String name , int parameterCount ) {
96- if (SeekableByteChannel .class .equals (declaringClass )) {
97- // SeekableByteChannel.position(long) and truncate(long) return 'this'
98- return parameterCount == 1 && (name .equals ("position" ) || name .equals ("truncate" ));
99- }
100- if (NetworkChannel .class .equals (declaringClass )) {
101- // NetworkChannel.bind and NetworkChannel.setOption returns 'this'
102- return parameterCount == 1 && name .equals ("bind" ) || parameterCount == 2 && name .equals ("setOption" );
103- }
104- return false ;
105- }
106-
107- private Object invokeObjectMethod (final Object proxy , final Method method , final Object [] args )
108- throws ReflectiveOperationException {
103+ private Object invokeObjectMethod (final Object proxy , final Method method , final Object [] args ) {
109104 switch (method .getName ()) {
110- case "toString" :
111- return "CloseShield(" + delegate + ")" ;
112- case "hashCode" :
113- return Objects .hashCode (delegate );
114- case "equals" : {
115- final Object other = args [0 ];
116- if (other == null ) {
117- return false ;
118- }
119- if (proxy == other ) {
120- return true ;
121- }
122- if (Proxy .isProxyClass (other .getClass ())) {
123- final InvocationHandler h = Proxy .getInvocationHandler (other );
124- if (h instanceof CloseShieldChannelHandler ) {
125- return Objects .equals (((CloseShieldChannelHandler ) h ).delegate , this .delegate );
126- }
127- }
105+ case "toString" :
106+ return "CloseShield(" + delegate + ")" ;
107+ case "hashCode" :
108+ return Objects .hashCode (delegate );
109+ case "equals" : {
110+ final Object other = args [0 ];
111+ if (other == null ) {
128112 return false ;
129113 }
130- default :
131- // Not possible, all non-final Object methods are handled above
132- return null ;
114+ if (proxy == other ) {
115+ return true ;
116+ }
117+ if (Proxy .isProxyClass (other .getClass ())) {
118+ final InvocationHandler h = Proxy .getInvocationHandler (other );
119+ if (h instanceof CloseShieldChannelHandler ) {
120+ return Objects .equals (((CloseShieldChannelHandler ) h ).delegate , this .delegate );
121+ }
122+ }
123+ return false ;
124+ }
125+ default :
126+ // Not possible, all non-final Object methods are handled above
127+ return null ;
133128 }
134129 }
135130}
0 commit comments