1 /* 2 * Copyright 2012 The Netty Project 3 * 4 * The Netty Project licenses this file to you under the Apache License, 5 * version 2.0 (the "License"); you may not use this file except in compliance 6 * with the License. You may obtain a copy of the License at: 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package org.jboss.netty.channel; 17 18 import org.jboss.netty.bootstrap.Bootstrap; 19 import org.jboss.netty.channel.group.ChannelGroup; 20 21 import java.lang.annotation.Documented; 22 import java.lang.annotation.ElementType; 23 import java.lang.annotation.Inherited; 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.lang.annotation.Target; 27 28 /** 29 * Handles or intercepts a {@link ChannelEvent}, and sends a 30 * {@link ChannelEvent} to the next handler in a {@link ChannelPipeline}. 31 * 32 * <h3>Sub-types</h3> 33 * <p> 34 * {@link ChannelHandler} itself does not provide any method. To handle a 35 * {@link ChannelEvent} you need to implement its sub-interfaces. There are 36 * two sub-interfaces which handles a received event, one for upstream events 37 * and the other for downstream events: 38 * <ul> 39 * <li>{@link ChannelUpstreamHandler} handles and intercepts an upstream {@link ChannelEvent}.</li> 40 * <li>{@link ChannelDownstreamHandler} handles and intercepts a downstream {@link ChannelEvent}.</li> 41 * </ul> 42 * 43 * You will also find more detailed explanation from the documentation of 44 * each sub-interface on how an event is interpreted when it goes upstream and 45 * downstream respectively. 46 * 47 * <h3>The context object</h3> 48 * <p> 49 * A {@link ChannelHandler} is provided with a {@link ChannelHandlerContext} 50 * object. A {@link ChannelHandler} is supposed to interact with the 51 * {@link ChannelPipeline} it belongs to via a context object. Using the 52 * context object, the {@link ChannelHandler} can pass events upstream or 53 * downstream, modify the pipeline dynamically, or store the information 54 * (attachment) which is specific to the handler. 55 * 56 * <h3>State management</h3> 57 * 58 * A {@link ChannelHandler} often needs to store some stateful information. 59 * The simplest and recommended approach is to use member variables: 60 * <pre> 61 * public class DataServerHandler extends {@link SimpleChannelHandler} { 62 * 63 * <b>private boolean loggedIn;</b> 64 * 65 * {@code @Override} 66 * public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) { 67 * {@link Channel} ch = e.getChannel(); 68 * Object o = e.getMessage(); 69 * if (o instanceof LoginMessage) { 70 * authenticate((LoginMessage) o); 71 * <b>loggedIn = true;</b> 72 * } else (o instanceof GetDataMessage) { 73 * if (<b>loggedIn</b>) { 74 * ch.write(fetchSecret((GetDataMessage) o)); 75 * } else { 76 * fail(); 77 * } 78 * } 79 * } 80 * ... 81 * } 82 * </pre> 83 * Because the handler instance has a state variable which is dedicated to 84 * one connection, you have to create a new handler instance for each new 85 * channel to avoid a race condition where a unauthenticated client can get 86 * the confidential information: 87 * <pre> 88 * // Create a new handler instance per channel. 89 * // See {@link Bootstrap#setPipelineFactory(ChannelPipelineFactory)}. 90 * public class DataServerPipelineFactory implements {@link ChannelPipelineFactory} { 91 * public {@link ChannelPipeline} getPipeline() { 92 * return {@link Channels}.pipeline(<b>new DataServerHandler()</b>); 93 * } 94 * } 95 * </pre> 96 * 97 * <h4>Using an attachment</h4> 98 * 99 * Although it's recommended to use member variables to store the state of a 100 * handler, for some reason you might not want to create many handler instances. 101 * In such a case, you can use an <em>attachment</em> which is provided by 102 * {@link ChannelHandlerContext}: 103 * <pre> 104 * {@code @Sharable} 105 * public class DataServerHandler extends {@link SimpleChannelHandler} { 106 * 107 * {@code @Override} 108 * public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) { 109 * {@link Channel} ch = e.getChannel(); 110 * Object o = e.getMessage(); 111 * if (o instanceof LoginMessage) { 112 * authenticate((LoginMessage) o); 113 * <b>ctx.setAttachment(true)</b>; 114 * } else (o instanceof GetDataMessage) { 115 * if (<b>Boolean.TRUE.equals(ctx.getAttachment())</b>) { 116 * ch.write(fetchSecret((GetDataMessage) o)); 117 * } else { 118 * fail(); 119 * } 120 * } 121 * } 122 * ... 123 * } 124 * </pre> 125 * Now that the state of the handler is stored as an attachment, you can add the 126 * same handler instance to different pipelines: 127 * <pre> 128 * public class DataServerPipelineFactory implements {@link ChannelPipelineFactory} { 129 * 130 * private static final DataServerHandler <b>SHARED</b> = new DataServerHandler(); 131 * 132 * public {@link ChannelPipeline} getPipeline() { 133 * return {@link Channels}.pipeline(<b>SHARED</b>); 134 * } 135 * } 136 * </pre> 137 * 138 * <h4>Using a {@link ChannelLocal}</h4> 139 * 140 * If you have a state variable which needs to be accessed either from other 141 * handlers or outside handlers, you can use {@link ChannelLocal}: 142 * <pre> 143 * public final class DataServerState { 144 * 145 * <b>public static final {@link ChannelLocal}<Boolean> loggedIn = new {@link ChannelLocal}<>() { 146 * protected Boolean initialValue(Channel channel) { 147 * return false; 148 * } 149 * }</b> 150 * ... 151 * } 152 * 153 * {@code @Sharable} 154 * public class DataServerHandler extends {@link SimpleChannelHandler} { 155 * 156 * {@code @Override} 157 * public void messageReceived({@link ChannelHandlerContext} ctx, {@link MessageEvent} e) { 158 * Channel ch = e.getChannel(); 159 * Object o = e.getMessage(); 160 * if (o instanceof LoginMessage) { 161 * authenticate((LoginMessage) o); 162 * <b>DataServerState.loggedIn.set(ch, true);</b> 163 * } else (o instanceof GetDataMessage) { 164 * if (<b>DataServerState.loggedIn.get(ch)</b>) { 165 * ctx.getChannel().write(fetchSecret((GetDataMessage) o)); 166 * } else { 167 * fail(); 168 * } 169 * } 170 * } 171 * ... 172 * } 173 * 174 * // Print the remote addresses of the authenticated clients: 175 * {@link ChannelGroup} allClientChannels = ...; 176 * for ({@link Channel} ch: allClientChannels) { 177 * if (<b>DataServerState.loggedIn.get(ch)</b>) { 178 * System.out.println(ch.getRemoteAddress()); 179 * } 180 * } 181 * </pre> 182 * 183 * <h4>The {@code @Sharable} annotation</h4> 184 * <p> 185 * In the examples above which used an attachment or a {@link ChannelLocal}, 186 * you might have noticed the {@code @Sharable} annotation. 187 * <p> 188 * If a {@link ChannelHandler} is annotated with the {@code @Sharable} 189 * annotation, it means you can create an instance of the handler just once and 190 * add it to one or more {@link ChannelPipeline}s multiple times without 191 * a race condition. 192 * <p> 193 * If this annotation is not specified, you have to create a new handler 194 * instance every time you add it to a pipeline because it has unshared state 195 * such as member variables. 196 * <p> 197 * This annotation is provided for documentation purpose, just like 198 * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>. 199 * 200 * <h3>Additional resources worth reading</h3> 201 * <p> 202 * Please refer to the {@link ChannelEvent} and {@link ChannelPipeline} to find 203 * out what a upstream event and a downstream event are, what fundamental 204 * differences they have, and how they flow in a pipeline. 205 * 206 * @apiviz.landmark 207 * @apiviz.exclude ^org\.jboss\.netty\.handler\..*$ 208 */ 209 public interface ChannelHandler { 210 211 /** 212 * Indicates that the same instance of the annotated {@link ChannelHandler} 213 * can be added to one or more {@link ChannelPipeline}s multiple times 214 * without a race condition. 215 * <p> 216 * If this annotation is not specified, you have to create a new handler 217 * instance every time you add it to a pipeline because it has unshared 218 * state such as member variables. 219 * <p> 220 * This annotation is provided for documentation purpose, just like 221 * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>. 222 */ 223 @Inherited 224 @Documented 225 @Target(ElementType.TYPE) 226 @Retention(RetentionPolicy.RUNTIME) 227 @interface Sharable { 228 // no value 229 } 230 }