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> 040 * The returned proxy implements all {@link Channel} sub-interfaces that are both supported by this implementation and actually implemented by the given 041 * delegate. 042 * </p> 043 * <p> 044 * The following interfaces are supported: 045 * </p> 046 * <ul> 047 * <li>{@link AsynchronousChannel}</li> 048 * <li>{@link ByteChannel}</li> 049 * <li>{@link Channel}</li> 050 * <li>{@link GatheringByteChannel}</li> 051 * <li>{@link InterruptibleChannel}</li> 052 * <li>{@link NetworkChannel}</li> 053 * <li>{@link ReadableByteChannel}</li> 054 * <li>{@link ScatteringByteChannel}</li> 055 * <li>{@link SeekableByteChannel}</li> 056 * <li>{@link WritableByteChannel}</li> 057 * </ul> 058 * 059 * @see Channel 060 * @see Closeable 061 * @since 2.21.0 062 */ 063public final class CloseShieldChannel { 064 065 private static final Class<?>[] EMPTY = {}; 066 067 private static Set<Class<?>> collectChannelInterfaces(final Class<?> type, final Set<Class<?>> out) { 068 Class<?> currentType = type; 069 // Visit interfaces 070 while (currentType != null) { 071 for (final Class<?> iface : currentType.getInterfaces()) { 072 if (CloseShieldChannelHandler.isSupported(iface) && out.add(iface)) { 073 collectChannelInterfaces(iface, out); 074 } 075 } 076 currentType = currentType.getSuperclass(); 077 } 078 return out; 079 } 080 081 /** 082 * Wraps a channel to shield it from being closed. 083 * 084 * @param channel The underlying channel to shield, not {@code null}. 085 * @param <T> A supported channel type. 086 * @return A proxy that shields {@code close()} and enforces closed semantics on other calls. 087 * @throws ClassCastException if {@code T} is not a supported channel type. 088 * @throws NullPointerException if {@code channel} is {@code null}. 089 */ 090 @SuppressWarnings({ "unchecked", "resource" }) // caller closes 091 public static <T extends Channel> T wrap(final T channel) { 092 Objects.requireNonNull(channel, "channel"); 093 // Fast path: already our shield 094 if (Proxy.isProxyClass(channel.getClass()) && Proxy.getInvocationHandler(channel) instanceof CloseShieldChannelHandler) { 095 return channel; 096 } 097 // Collect only Channel sub-interfaces. 098 final Set<Class<?>> set = collectChannelInterfaces(channel.getClass(), new LinkedHashSet<>()); 099 // fallback to root surface 100 return (T) Proxy.newProxyInstance(channel.getClass().getClassLoader(), // use delegate's loader 101 set.isEmpty() ? new Class<?>[] { Channel.class } : set.toArray(EMPTY), new CloseShieldChannelHandler(channel)); 102 } 103 104 private CloseShieldChannel() { 105 // no instance 106 } 107}