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
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
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
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
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
203 if (LOG.isDebugEnabled())
204 {
205 LOG.debug("Identified: {}",param);
206 }
207 return true;
208 }
209 }
210
211
212 return false;
213 }
214 }