View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.io.channels;
19  
20  import java.io.Closeable;
21  import java.lang.reflect.Proxy;
22  import java.nio.channels.AsynchronousChannel;
23  import java.nio.channels.ByteChannel;
24  import java.nio.channels.Channel;
25  import java.nio.channels.GatheringByteChannel;
26  import java.nio.channels.InterruptibleChannel;
27  import java.nio.channels.NetworkChannel;
28  import java.nio.channels.ReadableByteChannel;
29  import java.nio.channels.ScatteringByteChannel;
30  import java.nio.channels.SeekableByteChannel;
31  import java.nio.channels.WritableByteChannel;
32  import java.util.LinkedHashSet;
33  import java.util.Objects;
34  import java.util.Set;
35  
36  /**
37   * Creates a close-shielding proxy for a {@link Channel}.
38   *
39   * <p>The returned proxy implements all {@link Channel} sub-interfaces that are both supported by this implementation and actually implemented by the given
40   * delegate.</p>
41   *
42   * <p>The following interfaces are supported:</p>
43   *
44   * <ul>
45   * <li>{@link AsynchronousChannel}</li>
46   * <li>{@link ByteChannel}</li>
47   * <li>{@link Channel}</li>
48   * <li>{@link GatheringByteChannel}</li>
49   * <li>{@link InterruptibleChannel}</li>
50   * <li>{@link NetworkChannel}</li>
51   * <li>{@link ReadableByteChannel}</li>
52   * <li>{@link ScatteringByteChannel}</li>
53   * <li>{@link SeekableByteChannel}</li>
54   * <li>{@link WritableByteChannel}</li>
55   * </ul>
56   *
57   * @see Channel
58   * @see Closeable
59   * @since 2.21.0
60   */
61  public final class CloseShieldChannel {
62  
63      private static final Class<?>[] EMPTY = {};
64  
65      private static Set<Class<?>> collectChannelInterfaces(final Class<?> type, final Set<Class<?>> out) {
66          Class<?> currentType = type;
67          // Visit interfaces
68          while (currentType != null) {
69              for (final Class<?> iface : currentType.getInterfaces()) {
70                  if (CloseShieldChannelHandler.isSupported(iface) && out.add(iface)) {
71                      collectChannelInterfaces(iface, out);
72                  }
73              }
74              currentType = currentType.getSuperclass();
75          }
76          return out;
77      }
78  
79      /**
80       * Wraps a channel to shield it from being closed.
81       *
82       * @param channel The underlying channel to shield, not {@code null}.
83       * @param <T>     A supported channel type.
84       * @return A proxy that shields {@code close()} and enforces closed semantics on other calls.
85       * @throws ClassCastException if {@code T} is not a supported channel type.
86       * @throws NullPointerException if {@code channel} is {@code null}.
87       */
88      @SuppressWarnings({ "unchecked", "resource" }) // caller closes
89      public static <T extends Channel> T wrap(final T channel) {
90          Objects.requireNonNull(channel, "channel");
91          // Fast path: already our shield
92          if (Proxy.isProxyClass(channel.getClass()) && Proxy.getInvocationHandler(channel) instanceof CloseShieldChannelHandler) {
93              return channel;
94          }
95          // Collect only Channel sub-interfaces.
96          final Set<Class<?>> set = collectChannelInterfaces(channel.getClass(), new LinkedHashSet<>());
97          // fallback to root surface
98          return (T) Proxy.newProxyInstance(channel.getClass().getClassLoader(), // use delegate's loader
99                  set.isEmpty() ? new Class<?>[] { Channel.class } : set.toArray(EMPTY), new CloseShieldChannelHandler(channel));
100     }
101 
102     private CloseShieldChannel() {
103         // no instance
104     }
105 }