View Javadoc

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