1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.io.channels;
19
20 import java.lang.reflect.InvocationHandler;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24 import java.nio.channels.AsynchronousChannel;
25 import java.nio.channels.ByteChannel;
26 import java.nio.channels.Channel;
27 import java.nio.channels.ClosedChannelException;
28 import java.nio.channels.GatheringByteChannel;
29 import java.nio.channels.InterruptibleChannel;
30 import java.nio.channels.NetworkChannel;
31 import java.nio.channels.ReadableByteChannel;
32 import java.nio.channels.ScatteringByteChannel;
33 import java.nio.channels.SeekableByteChannel;
34 import java.nio.channels.WritableByteChannel;
35 import java.util.Collections;
36 import java.util.HashSet;
37 import java.util.Objects;
38 import java.util.Set;
39
40
41
42
43 final class CloseShieldChannelHandler implements InvocationHandler {
44
45 private static final Set<Class<? extends Channel>> SUPPORTED_INTERFACES;
46
47 static {
48 final Set<Class<? extends Channel>> interfaces = new HashSet<>();
49 interfaces.add(AsynchronousChannel.class);
50 interfaces.add(ByteChannel.class);
51 interfaces.add(Channel.class);
52 interfaces.add(GatheringByteChannel.class);
53 interfaces.add(InterruptibleChannel.class);
54 interfaces.add(NetworkChannel.class);
55 interfaces.add(ReadableByteChannel.class);
56 interfaces.add(ScatteringByteChannel.class);
57 interfaces.add(SeekableByteChannel.class);
58 interfaces.add(WritableByteChannel.class);
59 SUPPORTED_INTERFACES = Collections.unmodifiableSet(interfaces);
60 }
61
62
63
64
65
66
67
68
69
70 private static boolean isAllowedAfterClose(final Class<?> declaringClass, final String name, final int parameterCount) {
71
72 return parameterCount == 0 && name.equals("supportedOptions") && NetworkChannel.class.equals(declaringClass);
73 }
74
75 static boolean isSupported(final Class<?> interfaceClass) {
76 return SUPPORTED_INTERFACES.contains(interfaceClass);
77 }
78
79
80
81
82
83
84
85
86
87 private static boolean returnsThis(final Class<?> declaringClass, final String name, final int parameterCount) {
88 if (SeekableByteChannel.class.equals(declaringClass)) {
89
90 return parameterCount == 1 && (name.equals("position") || name.equals("truncate"));
91 }
92 if (NetworkChannel.class.equals(declaringClass)) {
93
94 return parameterCount == 1 && name.equals("bind") || parameterCount == 2 && name.equals("setOption");
95 }
96 return false;
97 }
98
99 private final Channel delegate;
100 private volatile boolean closed;
101
102 CloseShieldChannelHandler(final Channel delegate) {
103 this.delegate = Objects.requireNonNull(delegate, "delegate");
104 }
105
106 @Override
107 public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
108 final Class<?> declaringClass = method.getDeclaringClass();
109 final String name = method.getName();
110 final int parameterCount = method.getParameterCount();
111
112 if (declaringClass == Object.class) {
113 return invokeObjectMethod(proxy, method, args);
114 }
115
116 if (parameterCount == 0 && name.equals("close")) {
117 closed = true;
118 return null;
119 }
120
121 if (parameterCount == 0 && name.equals("isOpen")) {
122 return !closed && delegate.isOpen();
123 }
124
125 if (closed && !isAllowedAfterClose(declaringClass, name, parameterCount)) {
126 throw new ClosedChannelException();
127 }
128
129 try {
130 final Object result = method.invoke(delegate, args);
131 return returnsThis(declaringClass, name, parameterCount) ? proxy : result;
132 } catch (final InvocationTargetException e) {
133 throw e.getCause();
134 }
135 }
136
137 private Object invokeObjectMethod(final Object proxy, final Method method, final Object[] args) {
138 switch (method.getName()) {
139 case "toString":
140 return "CloseShieldChannel(" + delegate + ")";
141 case "hashCode":
142 return Objects.hashCode(delegate);
143 case "equals": {
144 final Object other = args[0];
145 if (other == null) {
146 return false;
147 }
148 if (proxy == other) {
149 return true;
150 }
151 if (Proxy.isProxyClass(other.getClass())) {
152 final InvocationHandler h = Proxy.getInvocationHandler(other);
153 if (h instanceof CloseShieldChannelHandler) {
154 return Objects.equals(((CloseShieldChannelHandler) h).delegate, this.delegate);
155 }
156 }
157 return false;
158 }
159 default:
160
161 return null;
162 }
163 }
164 }