View Javadoc

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