View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.websocket.common.extensions;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.ListIterator;
25  import java.util.concurrent.Future;
26  
27  import javax.net.websocket.SendResult;
28  
29  import org.eclipse.jetty.util.annotation.ManagedAttribute;
30  import org.eclipse.jetty.util.annotation.ManagedObject;
31  import org.eclipse.jetty.util.component.ContainerLifeCycle;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  import org.eclipse.jetty.websocket.api.WebSocketException;
35  import org.eclipse.jetty.websocket.api.extensions.Extension;
36  import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
37  import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
38  import org.eclipse.jetty.websocket.api.extensions.Frame;
39  import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
40  import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
41  import org.eclipse.jetty.websocket.common.Generator;
42  import org.eclipse.jetty.websocket.common.Parser;
43  
44  /**
45   * Represents the stack of Extensions.
46   */
47  @ManagedObject("Extension Stack")
48  public class ExtensionStack extends ContainerLifeCycle implements IncomingFrames, OutgoingFrames
49  {
50      private static final Logger LOG = Log.getLogger(ExtensionStack.class);
51      private final ExtensionFactory factory;
52      private List<Extension> extensions;
53      private IncomingFrames nextIncoming;
54      private OutgoingFrames nextOutgoing;
55  
56      public ExtensionStack(ExtensionFactory factory)
57      {
58          this.factory = factory;
59      }
60  
61      public void configure(Generator generator)
62      {
63          generator.configureFromExtensions(extensions);
64      }
65  
66      public void configure(Parser parser)
67      {
68          parser.configureFromExtensions(extensions);
69      }
70  
71      @Override
72      protected void doStart() throws Exception
73      {
74          super.doStart();
75          LOG.debug("doStart");
76  
77          // Wire up Extensions
78          if ((extensions != null) && (extensions.size() > 0))
79          {
80              ListIterator<Extension> eiter = extensions.listIterator();
81  
82              // Connect outgoings
83              while (eiter.hasNext())
84              {
85                  Extension ext = eiter.next();
86                  ext.setNextOutgoingFrames(nextOutgoing);
87                  nextOutgoing = ext;
88              }
89  
90              // Connect incomings
91              while (eiter.hasPrevious())
92              {
93                  Extension ext = eiter.previous();
94                  ext.setNextIncomingFrames(nextIncoming);
95                  nextIncoming = ext;
96              }
97          }
98      }
99  
100     @Override
101     public void dump(Appendable out, String indent) throws IOException
102     {
103         super.dump(out,indent);
104 
105         IncomingFrames websocket = getLastIncoming();
106         OutgoingFrames network = getLastOutgoing();
107 
108         out.append(indent).append(" +- Stack\n");
109         out.append(indent).append("    +- Network  : ").append(network.toString()).append('\n');
110         for (Extension ext : extensions)
111         {
112             out.append(indent).append("    +- Extension: ").append(ext.toString()).append('\n');
113         }
114         out.append(indent).append("    +- Websocket: ").append(websocket.toString()).append('\n');
115     }
116 
117     @ManagedAttribute(name = "Extension List", readonly = true)
118     public List<Extension> getExtensions()
119     {
120         return extensions;
121     }
122 
123     private IncomingFrames getLastIncoming()
124     {
125         IncomingFrames last = nextIncoming;
126         boolean done = false;
127         while (!done)
128         {
129             if (last instanceof AbstractExtension)
130             {
131                 last = ((AbstractExtension)last).getNextIncoming();
132             }
133             else
134             {
135                 done = true;
136             }
137         }
138         return last;
139     }
140 
141     private OutgoingFrames getLastOutgoing()
142     {
143         OutgoingFrames last = nextOutgoing;
144         boolean done = false;
145         while (!done)
146         {
147             if (last instanceof AbstractExtension)
148             {
149                 last = ((AbstractExtension)last).getNextOutgoing();
150             }
151             else
152             {
153                 done = true;
154             }
155         }
156         return last;
157     }
158 
159     /**
160      * Get the list of negotiated extensions, each entry being a full "name; params" extension configuration
161      * 
162      * @return list of negotiated extensions
163      */
164     public List<String> getNegotiatedExtensions()
165     {
166         List<String> ret = new ArrayList<>();
167         if (extensions == null)
168         {
169             return ret;
170         }
171 
172         for (Extension ext : extensions)
173         {
174             ret.add(ext.getConfig().getParameterizedName());
175         }
176         return ret;
177     }
178 
179     @ManagedAttribute(name = "Next Incoming Frames Handler", readonly = true)
180     public IncomingFrames getNextIncoming()
181     {
182         return nextIncoming;
183     }
184 
185     @ManagedAttribute(name = "Next Outgoing Frames Handler", readonly = true)
186     public OutgoingFrames getNextOutgoing()
187     {
188         return nextOutgoing;
189     }
190 
191     @Override
192     public void incomingError(WebSocketException e)
193     {
194         nextIncoming.incomingError(e);
195     }
196 
197     @Override
198     public void incomingFrame(Frame frame)
199     {
200         nextIncoming.incomingFrame(frame);
201     }
202 
203     /**
204      * Perform the extension negotiation.
205      * <p>
206      * For the list of negotiated extensions, use {@link #getNegotiatedExtensions()}
207      * 
208      * @param configs
209      *            the configurations being requested
210      */
211     public void negotiate(List<ExtensionConfig> configs)
212     {
213         LOG.debug("Extension Configs={}",configs);
214         this.extensions = new ArrayList<>();
215         for (ExtensionConfig config : configs)
216         {
217             Extension ext = factory.newInstance(config);
218             extensions.add(ext);
219             LOG.debug("Adding Extension: {}",ext);
220         }
221 
222         addBean(extensions);
223     }
224 
225     @Override
226     public Future<SendResult> outgoingFrame(Frame frame) throws IOException
227     {
228         return nextOutgoing.outgoingFrame(frame);
229     }
230 
231     public void setNextIncoming(IncomingFrames nextIncoming)
232     {
233         this.nextIncoming = nextIncoming;
234     }
235 
236     public void setNextOutgoing(OutgoingFrames nextOutgoing)
237     {
238         this.nextOutgoing = nextOutgoing;
239     }
240 
241     @Override
242     public String toString()
243     {
244         return String.format("ExtensionStack[extensions=%s]",extensions);
245     }
246 }