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