View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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  
26  import org.eclipse.jetty.util.annotation.ManagedAttribute;
27  import org.eclipse.jetty.util.annotation.ManagedObject;
28  import org.eclipse.jetty.util.component.ContainerLifeCycle;
29  import org.eclipse.jetty.util.log.Log;
30  import org.eclipse.jetty.util.log.Logger;
31  import org.eclipse.jetty.websocket.api.WebSocketException;
32  import org.eclipse.jetty.websocket.api.WriteCallback;
33  import org.eclipse.jetty.websocket.api.extensions.Extension;
34  import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
35  import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
36  import org.eclipse.jetty.websocket.api.extensions.Frame;
37  import org.eclipse.jetty.websocket.api.extensions.IncomingFrames;
38  import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
39  import org.eclipse.jetty.websocket.common.Generator;
40  import org.eclipse.jetty.websocket.common.Parser;
41  
42  /**
43   * Represents the stack of Extensions.
44   */
45  @ManagedObject("Extension Stack")
46  public class ExtensionStack extends ContainerLifeCycle implements IncomingFrames, OutgoingFrames
47  {
48      private static final Logger LOG = Log.getLogger(ExtensionStack.class);
49      private final ExtensionFactory factory;
50      private List<Extension> extensions;
51      private IncomingFrames nextIncoming;
52      private OutgoingFrames nextOutgoing;
53  
54      public ExtensionStack(ExtensionFactory factory)
55      {
56          this.factory = factory;
57      }
58  
59      public void configure(Generator generator)
60      {
61          generator.configureFromExtensions(extensions);
62      }
63  
64      public void configure(Parser parser)
65      {
66          parser.configureFromExtensions(extensions);
67      }
68  
69      @Override
70      protected void doStart() throws Exception
71      {
72          super.doStart();
73          LOG.debug("doStart");
74  
75          // Wire up Extensions
76          if ((extensions != null) && (extensions.size() > 0))
77          {
78              ListIterator<Extension> eiter = extensions.listIterator();
79  
80              // Connect outgoings
81              while (eiter.hasNext())
82              {
83                  Extension ext = eiter.next();
84                  ext.setNextOutgoingFrames(nextOutgoing);
85                  nextOutgoing = ext;
86              }
87  
88              // Connect incomings
89              while (eiter.hasPrevious())
90              {
91                  Extension ext = eiter.previous();
92                  ext.setNextIncomingFrames(nextIncoming);
93                  nextIncoming = ext;
94              }
95          }
96      }
97  
98      @Override
99      public void dump(Appendable out, String indent) throws IOException
100     {
101         super.dump(out,indent);
102 
103         IncomingFrames websocket = getLastIncoming();
104         OutgoingFrames network = getLastOutgoing();
105 
106         out.append(indent).append(" +- Stack\n");
107         out.append(indent).append("    +- Network  : ").append(network.toString()).append('\n');
108         for (Extension ext : extensions)
109         {
110             out.append(indent).append("    +- Extension: ").append(ext.toString()).append('\n');
111         }
112         out.append(indent).append("    +- Websocket: ").append(websocket.toString()).append('\n');
113     }
114 
115     @ManagedAttribute(name = "Extension List", readonly = true)
116     public List<Extension> getExtensions()
117     {
118         return extensions;
119     }
120 
121     private IncomingFrames getLastIncoming()
122     {
123         IncomingFrames last = nextIncoming;
124         boolean done = false;
125         while (!done)
126         {
127             if (last instanceof AbstractExtension)
128             {
129                 last = ((AbstractExtension)last).getNextIncoming();
130             }
131             else
132             {
133                 done = true;
134             }
135         }
136         return last;
137     }
138 
139     private OutgoingFrames getLastOutgoing()
140     {
141         OutgoingFrames last = nextOutgoing;
142         boolean done = false;
143         while (!done)
144         {
145             if (last instanceof AbstractExtension)
146             {
147                 last = ((AbstractExtension)last).getNextOutgoing();
148             }
149             else
150             {
151                 done = true;
152             }
153         }
154         return last;
155     }
156 
157     /**
158      * Get the list of negotiated extensions, each entry being a full "name; params" extension configuration
159      * 
160      * @return list of negotiated extensions
161      */
162     public List<ExtensionConfig> getNegotiatedExtensions()
163     {
164         List<ExtensionConfig> ret = new ArrayList<>();
165         if (extensions == null)
166         {
167             return ret;
168         }
169 
170         for (Extension ext : extensions)
171         {
172             ret.add(ext.getConfig());
173         }
174         return ret;
175     }
176 
177     @ManagedAttribute(name = "Next Incoming Frames Handler", readonly = true)
178     public IncomingFrames getNextIncoming()
179     {
180         return nextIncoming;
181     }
182 
183     @ManagedAttribute(name = "Next Outgoing Frames Handler", readonly = true)
184     public OutgoingFrames getNextOutgoing()
185     {
186         return nextOutgoing;
187     }
188 
189     public boolean hasNegotiatedExtensions()
190     {
191         return (this.extensions != null) && (this.extensions.size() > 0);
192     }
193 
194     @Override
195     public void incomingError(WebSocketException e)
196     {
197         nextIncoming.incomingError(e);
198     }
199 
200     @Override
201     public void incomingFrame(Frame frame)
202     {
203         nextIncoming.incomingFrame(frame);
204     }
205 
206     /**
207      * Perform the extension negotiation.
208      * <p>
209      * For the list of negotiated extensions, use {@link #getNegotiatedExtensions()}
210      * 
211      * @param configs
212      *            the configurations being requested
213      */
214     public void negotiate(List<ExtensionConfig> configs)
215     {
216         LOG.debug("Extension Configs={}",configs);
217         this.extensions = new ArrayList<>();
218         for (ExtensionConfig config : configs)
219         {
220             Extension ext = factory.newInstance(config);
221             if (ext == null)
222             {
223                 // Extension not present on this side
224                 continue;
225             }
226             extensions.add(ext);
227             LOG.debug("Adding Extension: {}",ext);
228         }
229 
230         addBean(extensions);
231     }
232 
233     @Override
234     public void outgoingFrame(Frame frame, WriteCallback callback)
235     {
236         nextOutgoing.outgoingFrame(frame,callback);
237     }
238 
239     public void setNextIncoming(IncomingFrames nextIncoming)
240     {
241         this.nextIncoming = nextIncoming;
242     }
243 
244     public void setNextOutgoing(OutgoingFrames nextOutgoing)
245     {
246         this.nextOutgoing = nextOutgoing;
247     }
248 
249     @Override
250     public String toString()
251     {
252         StringBuilder s = new StringBuilder();
253         s.append("ExtensionStack[");
254         s.append("extensions=");
255         if (extensions == null)
256         {
257             s.append("<null>");
258         }
259         else
260         {
261             s.append('[');
262             boolean delim = false;
263             for (Extension ext : extensions)
264             {
265                 if (delim)
266                 {
267                     s.append(',');
268                 }
269                 if (ext == null)
270                 {
271                     s.append("<null>");
272                 }
273                 else
274                 {
275                     s.append(ext.getName());
276                 }
277                 delim = true;
278             }
279             s.append(']');
280         }
281         s.append(",incoming=").append((this.nextIncoming == null)?"<null>":this.nextIncoming.getClass().getName());
282         s.append(",outgoing=").append((this.nextOutgoing == null)?"<null>":this.nextOutgoing.getClass().getName());
283         s.append("]");
284         return s.toString();
285     }
286 }
287