001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.io.channels; 019 020import java.io.Closeable; 021import java.lang.reflect.Proxy; 022import java.nio.channels.AsynchronousChannel; 023import java.nio.channels.ByteChannel; 024import java.nio.channels.Channel; 025import java.nio.channels.GatheringByteChannel; 026import java.nio.channels.InterruptibleChannel; 027import java.nio.channels.NetworkChannel; 028import java.nio.channels.ReadableByteChannel; 029import java.nio.channels.ScatteringByteChannel; 030import java.nio.channels.SeekableByteChannel; 031import java.nio.channels.WritableByteChannel; 032import java.util.LinkedHashSet; 033import java.util.Objects; 034import java.util.Set; 035 036/** 037 * Creates a close-shielding proxy for a {@link Channel}. 038 * 039 * <p>The returned proxy implements all {@link Channel} sub-interfaces that are both supported by this implementation and actually implemented by the given 040 * delegate.</p> 041 * 042 * <p>The following interfaces are supported:</p> 043 * 044 * <ul> 045 * <li>{@link AsynchronousChannel}</li> 046 * <li>{@link ByteChannel}</li> 047 * <li>{@link Channel}</li> 048 * <li>{@link GatheringByteChannel}</li> 049 * <li>{@link InterruptibleChannel}</li> 050 * <li>{@link NetworkChannel}</li> 051 * <li>{@link ReadableByteChannel}</li> 052 * <li>{@link ScatteringByteChannel}</li> 053 * <li>{@link SeekableByteChannel}</li> 054 * <li>{@link WritableByteChannel}</li> 055 * </ul> 056 * 057 * @see Channel 058 * @see Closeable 059 * @since 2.21.0 060 */ 061public final class CloseShieldChannel { 062 063 private static final Class<?>[] EMPTY = {}; 064 065 private static Set<Class<?>> collectChannelInterfaces(final Class<?> type, final Set<Class<?>> out) { 066 Class<?> currentType = type; 067 // Visit interfaces 068 while (currentType != null) { 069 for (final Class<?> iface : currentType.getInterfaces()) { 070 if (CloseShieldChannelHandler.isSupported(iface) && out.add(iface)) { 071 collectChannelInterfaces(iface, out); 072 } 073 } 074 currentType = currentType.getSuperclass(); 075 } 076 return out; 077 } 078 079 /** 080 * Wraps a channel to shield it from being closed. 081 * 082 * @param channel The underlying channel to shield, not {@code null}. 083 * @param <T> A supported channel type. 084 * @return A proxy that shields {@code close()} and enforces closed semantics on other calls. 085 * @throws ClassCastException if {@code T} is not a supported channel type. 086 * @throws NullPointerException if {@code channel} is {@code null}. 087 */ 088 @SuppressWarnings({ "unchecked", "resource" }) // caller closes 089 public static <T extends Channel> T wrap(final T channel) { 090 Objects.requireNonNull(channel, "channel"); 091 // Fast path: already our shield 092 if (Proxy.isProxyClass(channel.getClass()) && Proxy.getInvocationHandler(channel) instanceof CloseShieldChannelHandler) { 093 return channel; 094 } 095 // Collect only Channel sub-interfaces. 096 final Set<Class<?>> set = collectChannelInterfaces(channel.getClass(), new LinkedHashSet<>()); 097 // fallback to root surface 098 return (T) Proxy.newProxyInstance(channel.getClass().getClassLoader(), // use delegate's loader 099 set.isEmpty() ? new Class<?>[] { Channel.class } : set.toArray(EMPTY), new CloseShieldChannelHandler(channel)); 100 } 101 102 private CloseShieldChannel() { 103 // no instance 104 } 105}