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.annotations;
20  
21  import java.lang.annotation.Annotation;
22  import java.lang.reflect.Method;
23  import java.util.LinkedList;
24  import java.util.List;
25  import javax.websocket.EndpointConfig;
26  import javax.websocket.OnClose;
27  import javax.websocket.OnError;
28  import javax.websocket.OnMessage;
29  import javax.websocket.OnOpen;
30  
31  import org.eclipse.jetty.util.log.Log;
32  import org.eclipse.jetty.util.log.Logger;
33  import org.eclipse.jetty.websocket.common.events.annotated.AbstractMethodAnnotationScanner;
34  import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
35  import org.eclipse.jetty.websocket.common.util.ReflectUtils;
36  
37  public class AnnotatedEndpointScanner<T extends Annotation, C extends EndpointConfig> extends AbstractMethodAnnotationScanner<AnnotatedEndpointMetadata<T, C>>
38  {
39      private static final Logger LOG = Log.getLogger(AnnotatedEndpointScanner.class);
40  
41      private final LinkedList<IJsrParamId> paramsOnOpen;
42      private final LinkedList<IJsrParamId> paramsOnClose;
43      private final LinkedList<IJsrParamId> paramsOnError;
44      private final LinkedList<IJsrParamId> paramsOnMessage;
45      private final AnnotatedEndpointMetadata<T, C> metadata;
46  
47      public AnnotatedEndpointScanner(AnnotatedEndpointMetadata<T, C> metadata)
48      {
49          this.metadata = metadata;
50  
51          paramsOnOpen = new LinkedList<>();
52          paramsOnClose = new LinkedList<>();
53          paramsOnError = new LinkedList<>();
54          paramsOnMessage = new LinkedList<>();
55  
56          metadata.customizeParamsOnOpen(paramsOnOpen);
57          paramsOnOpen.add(JsrParamIdOnOpen.INSTANCE);
58  
59          metadata.customizeParamsOnClose(paramsOnClose);
60          paramsOnClose.add(JsrParamIdOnClose.INSTANCE);
61  
62          metadata.customizeParamsOnError(paramsOnError);
63          paramsOnError.add(JsrParamIdOnError.INSTANCE);
64  
65          metadata.customizeParamsOnMessage(paramsOnMessage);
66          paramsOnMessage.add(JsrParamIdText.INSTANCE);
67          paramsOnMessage.add(JsrParamIdBinary.INSTANCE);
68          paramsOnMessage.add(JsrParamIdPong.INSTANCE);
69      }
70  
71      private void assertNotDuplicate(JsrCallable callable, Class<? extends Annotation> methodAnnotationClass, Class<?> pojo, Method method)
72      {
73          if (callable != null)
74          {
75              // Duplicate annotation detected
76              StringBuilder err = new StringBuilder();
77              err.append("Encountered duplicate method annotations @");
78              err.append(methodAnnotationClass.getSimpleName());
79              err.append(" on ");
80              err.append(ReflectUtils.toString(pojo,callable.getMethod()));
81              err.append(" and ");
82              err.append(ReflectUtils.toString(pojo,method));
83  
84              throw new InvalidSignatureException(err.toString());
85          }
86      }
87  
88      @Override
89      public void onMethodAnnotation(AnnotatedEndpointMetadata<T, C> metadata, Class<?> pojo, Method method, Annotation annotation)
90      {
91          if (LOG.isDebugEnabled())
92          {
93              LOG.debug("onMethodAnnotation({}, {}, {}, {})",metadata,pojo,method,annotation);
94          }
95  
96          if (isAnnotation(annotation,OnOpen.class))
97          {
98              assertIsPublicNonStatic(method);
99              assertIsReturn(method,Void.TYPE);
100             assertNotDuplicate(metadata.onOpen,OnOpen.class,pojo,method);
101             OnOpenCallable onopen = new OnOpenCallable(pojo,method);
102             visitMethod(onopen,pojo,method,paramsOnOpen,OnOpen.class);
103             metadata.onOpen = onopen;
104             return;
105         }
106 
107         if (isAnnotation(annotation,OnClose.class))
108         {
109             assertIsPublicNonStatic(method);
110             assertIsReturn(method,Void.TYPE);
111             assertNotDuplicate(metadata.onClose,OnClose.class,pojo,method);
112             OnCloseCallable onclose = new OnCloseCallable(pojo,method);
113             visitMethod(onclose,pojo,method,paramsOnClose,OnClose.class);
114             metadata.onClose = onclose;
115             return;
116         }
117 
118         if (isAnnotation(annotation,OnError.class))
119         {
120             assertIsPublicNonStatic(method);
121             assertIsReturn(method,Void.TYPE);
122             assertNotDuplicate(metadata.onError,OnError.class,pojo,method);
123             OnErrorCallable onerror = new OnErrorCallable(pojo,method);
124             visitMethod(onerror,pojo,method,paramsOnError,OnError.class);
125             metadata.onError = onerror;
126             return;
127         }
128 
129         if (isAnnotation(annotation,OnMessage.class))
130         {
131             assertIsPublicNonStatic(method);
132             // assertIsReturn(method,Void.TYPE); // no validation, it can be any return type
133             OnMessageCallable onmessage = new OnMessageCallable(pojo,method);
134             visitMethod(onmessage,pojo,method,paramsOnMessage,OnMessage.class);
135 
136             Param param = onmessage.getMessageObjectParam();
137             switch (param.role)
138             {
139                 case MESSAGE_BINARY:
140                     metadata.onBinary = new OnMessageBinaryCallable(onmessage);
141                     break;
142                 case MESSAGE_BINARY_STREAM:
143                     metadata.onBinaryStream = new OnMessageBinaryStreamCallable(onmessage);
144                     break;
145                 case MESSAGE_TEXT:
146                     metadata.onText = new OnMessageTextCallable(onmessage);
147                     break;
148                 case MESSAGE_TEXT_STREAM:
149                     metadata.onTextStream = new OnMessageTextStreamCallable(onmessage);
150                     break;
151                 case MESSAGE_PONG:
152                     metadata.onPong = new OnMessagePongCallable(onmessage);
153                     break;
154                 default:
155                     StringBuilder err = new StringBuilder();
156                     err.append("An unrecognized message type <");
157                     err.append(param.type);
158                     err.append(">: does not meet specified type categories of [TEXT, BINARY, DECODER, or PONG]");
159                     throw new InvalidSignatureException(err.toString());
160             }
161         }
162     }
163 
164     public AnnotatedEndpointMetadata<T, C> scan()
165     {
166         scanMethodAnnotations(metadata,metadata.getEndpointClass());
167         return metadata;
168     }
169 
170     private void visitMethod(JsrCallable callable, Class<?> pojo, Method method, LinkedList<IJsrParamId> paramIds,
171             Class<? extends Annotation> methodAnnotationClass)
172     {
173         // Identify all of the parameters
174         for (Param param : callable.getParams())
175         {
176             if (!visitParam(callable,param,paramIds))
177             {
178                 StringBuilder err = new StringBuilder();
179                 err.append("Encountered unknown parameter type <");
180                 err.append(param.type.getName());
181                 err.append("> on @");
182                 err.append(methodAnnotationClass.getSimpleName());
183                 err.append(" annotated method: ");
184                 err.append(ReflectUtils.toString(pojo,method));
185 
186                 throw new InvalidSignatureException(err.toString());
187             }
188         }
189     }
190 
191     private boolean visitParam(JsrCallable callable, Param param, List<IJsrParamId> paramIds)
192     {
193         for (IJsrParamId paramId : paramIds)
194         {
195             if (LOG.isDebugEnabled())
196             {
197                 LOG.debug("{}.process()",paramId);
198             }
199             if (paramId.process(param,callable))
200             {
201                 // Successfully identified
202                 if (LOG.isDebugEnabled())
203                 {
204                     LOG.debug("Identified: {}",param);
205                 }
206                 return true;
207             }
208         }
209 
210         // Failed identification as a known parameter
211         return false;
212     }
213 }