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.jsr356.metadata;
20  
21  import java.util.ArrayList;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.ConcurrentHashMap;
26  
27  import javax.websocket.Decoder;
28  
29  import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
30  
31  /**
32   * An durable collection of {@link CoderMetadata}.
33   * <p>
34   * This is a write-only collection, and cannot be modified once initialized.
35   * 
36   * @param <T>
37   *            The type of coder ({@link Decoder} or {@link Encoder}
38   * @param <M>
39   *            The metadata for the coder
40   */
41  public abstract class CoderMetadataSet<T, M extends CoderMetadata<T>> implements Iterable<M>
42  {
43      /**
44       * Collection of metadatas
45       */
46      private final List<M> metadatas;
47      /**
48       * Collection of declared Coder classes
49       */
50      private final List<Class<? extends T>> coders;
51      /**
52       * Mapping of supported Type to metadata list index
53       */
54      private final Map<Class<?>, Integer> typeMap;
55      /**
56       * Mapping of Coder class to list of supported metadata
57       */
58      private final Map<Class<? extends T>, List<Integer>> implMap;
59  
60      protected CoderMetadataSet()
61      {
62          metadatas = new ArrayList<>();
63          coders = new ArrayList<>();
64          typeMap = new ConcurrentHashMap<>();
65          implMap = new ConcurrentHashMap<>();
66      }
67  
68      public void add(Class<? extends T> coder)
69      {
70          List<M> metadatas = discover(coder);
71          trackMetadata(metadatas);
72      }
73  
74      public List<M> addAll(Class<? extends T>[] coders)
75      {
76          List<M> metadatas = new ArrayList<>();
77  
78          for (Class<? extends T> coder : coders)
79          {
80              metadatas.addAll(discover(coder));
81          }
82  
83          trackMetadata(metadatas);
84          return metadatas;
85      }
86  
87      public List<M> addAll(List<Class<? extends T>> coders)
88      {
89          List<M> metadatas = new ArrayList<>();
90  
91          for (Class<? extends T> coder : coders)
92          {
93              metadatas.addAll(discover(coder));
94          }
95  
96          trackMetadata(metadatas);
97          return metadatas;
98      }
99  
100     /**
101      * Coder Specific discovery of Metadata for a specific coder.
102      * 
103      * @param coder
104      *            the coder to discover metadata in.
105      * @return the list of metadata discovered
106      * @throws InvalidWebSocketException
107      *             if unable to discover some metadata. Sucha as: a duplicate {@link CoderMetadata#getObjectType()} encountered, , or if unable to find the
108      *             concrete generic class reference for the coder, or if the provided coder is not valid per spec.
109      */
110     protected abstract List<M> discover(Class<? extends T> coder);
111 
112     public Class<? extends T> getCoder(Class<?> type)
113     {
114         M metadata = getMetadataByType(type);
115         if (metadata == null)
116         {
117             return null;
118         }
119         return metadata.getCoderClass();
120     }
121 
122     public List<Class<? extends T>> getList()
123     {
124         return coders;
125     }
126 
127     public List<M> getMetadataByImplementation(Class<? extends T> clazz)
128     {
129         List<Integer> indexes = implMap.get(clazz);
130         if (indexes == null)
131         {
132             return null;
133         }
134         List<M> ret = new ArrayList<>();
135         for (Integer idx : indexes)
136         {
137             ret.add(metadatas.get(idx));
138         }
139         return ret;
140     }
141 
142     public M getMetadataByType(Class<?> type)
143     {
144         Integer idx = typeMap.get(type);
145         if (idx == null)
146         {
147             // Quick lookup failed, try slower lookup via isAssignable instead
148             idx = getMetadataByAssignableType(type);
149             if (idx != null)
150             {
151                 // add new entry map
152                 typeMap.put(type,idx);
153             }
154         }
155 
156         // If idx is STILL null, we've got no match
157         if (idx == null)
158         {
159             return null;
160         }
161         return metadatas.get(idx);
162     }
163 
164     private Integer getMetadataByAssignableType(Class<?> type)
165     {
166         for (Map.Entry<Class<?>, Integer> entry : typeMap.entrySet())
167         {
168             if (entry.getKey().isAssignableFrom(type))
169             {
170                 return entry.getValue();
171             }
172         }
173 
174         return null;
175     }
176 
177     @Override
178     public Iterator<M> iterator()
179     {
180         return metadatas.iterator();
181     }
182 
183     @Override
184     public String toString()
185     {
186         StringBuilder builder = new StringBuilder();
187         builder.append(this.getClass().getSimpleName());
188         builder.append("[metadatas=");
189         builder.append(metadatas.size());
190         builder.append(",coders=");
191         builder.append(coders.size());
192         builder.append("]");
193         return builder.toString();
194     }
195 
196     protected void trackMetadata(List<M> metadatas)
197     {
198         for (M metadata : metadatas)
199         {
200             trackMetadata(metadata);
201         }
202     }
203 
204     protected void trackMetadata(M metadata)
205     {
206         synchronized (metadatas)
207         {
208             // Validate
209             boolean duplicate = false;
210 
211             // Is this metadata already declared?
212             if (metadatas.contains(metadata))
213             {
214                 duplicate = true;
215             }
216 
217             // Is this type already declared?
218             Class<?> type = metadata.getObjectType();
219             if (typeMap.containsKey(type))
220             {
221                 duplicate = true;
222             }
223 
224             if (duplicate)
225             {
226                 StringBuilder err = new StringBuilder();
227                 err.append("Duplicate decoder for type: ");
228                 err.append(type);
229                 err.append(" (class ").append(metadata.getCoderClass().getName());
230 
231                 // Get prior one
232                 M dup = getMetadataByType(type);
233                 err.append(" duplicates ");
234                 err.append(dup.getCoderClass().getName());
235                 err.append(")");
236                 throw new IllegalStateException(err.toString());
237             }
238 
239             // Track
240             Class<? extends T> coderClass = metadata.getCoderClass();
241             int newidx = metadatas.size();
242             metadatas.add(metadata);
243             coders.add(coderClass);
244             typeMap.put(type,newidx);
245 
246             List<Integer> indexes = implMap.get(coderClass);
247             if (indexes == null)
248             {
249                 indexes = new ArrayList<>();
250             }
251             if (indexes.contains(newidx))
252             {
253                 // possible duplicate, TODO: how?
254             }
255             indexes.add(newidx);
256             implMap.put(coderClass,indexes);
257         }
258     }
259 }