1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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
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
202 if (LOG.isDebugEnabled())
203 {
204 LOG.debug("Identified: {}",param);
205 }
206 return true;
207 }
208 }
209
210
211 return false;
212 }
213 }