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 LOG.debug("onMethodAnnotation({}, {}, {}, {})",metadata,pojo,method,annotation);
93
94 if (isAnnotation(annotation,OnOpen.class))
95 {
96 assertIsPublicNonStatic(method);
97 assertIsReturn(method,Void.TYPE);
98 assertNotDuplicate(metadata.onOpen,OnOpen.class,pojo,method);
99 OnOpenCallable onopen = new OnOpenCallable(pojo,method);
100 visitMethod(onopen,pojo,method,paramsOnOpen,OnOpen.class);
101 metadata.onOpen = onopen;
102 return;
103 }
104
105 if (isAnnotation(annotation,OnClose.class))
106 {
107 assertIsPublicNonStatic(method);
108 assertIsReturn(method,Void.TYPE);
109 assertNotDuplicate(metadata.onClose,OnClose.class,pojo,method);
110 OnCloseCallable onclose = new OnCloseCallable(pojo,method);
111 visitMethod(onclose,pojo,method,paramsOnClose,OnClose.class);
112 metadata.onClose = onclose;
113 return;
114 }
115
116 if (isAnnotation(annotation,OnError.class))
117 {
118 assertIsPublicNonStatic(method);
119 assertIsReturn(method,Void.TYPE);
120 assertNotDuplicate(metadata.onError,OnError.class,pojo,method);
121 OnErrorCallable onerror = new OnErrorCallable(pojo,method);
122 visitMethod(onerror,pojo,method,paramsOnError,OnError.class);
123 metadata.onError = onerror;
124 return;
125 }
126
127 if (isAnnotation(annotation,OnMessage.class))
128 {
129 assertIsPublicNonStatic(method);
130
131 OnMessageCallable onmessage = new OnMessageCallable(pojo,method);
132 visitMethod(onmessage,pojo,method,paramsOnMessage,OnMessage.class);
133
134 Param param = onmessage.getMessageObjectParam();
135 switch (param.role)
136 {
137 case MESSAGE_BINARY:
138 metadata.onBinary = new OnMessageBinaryCallable(onmessage);
139 break;
140 case MESSAGE_BINARY_STREAM:
141 metadata.onBinaryStream = new OnMessageBinaryStreamCallable(onmessage);
142 break;
143 case MESSAGE_TEXT:
144 metadata.onText = new OnMessageTextCallable(onmessage);
145 break;
146 case MESSAGE_TEXT_STREAM:
147 metadata.onTextStream = new OnMessageTextStreamCallable(onmessage);
148 break;
149 case MESSAGE_PONG:
150 metadata.onPong = new OnMessagePongCallable(onmessage);
151 break;
152 default:
153 StringBuilder err = new StringBuilder();
154 err.append("An unrecognized message type <");
155 err.append(param.type);
156 err.append(">: does not meet specified type categories of [TEXT, BINARY, DECODER, or PONG]");
157 throw new InvalidSignatureException(err.toString());
158 }
159 }
160 }
161
162 public AnnotatedEndpointMetadata<T, C> scan()
163 {
164 scanMethodAnnotations(metadata,metadata.getEndpointClass());
165 return metadata;
166 }
167
168 private void visitMethod(JsrCallable callable, Class<?> pojo, Method method, LinkedList<IJsrParamId> paramIds,
169 Class<? extends Annotation> methodAnnotationClass)
170 {
171
172 for (Param param : callable.getParams())
173 {
174 if (!visitParam(callable,param,paramIds))
175 {
176 StringBuilder err = new StringBuilder();
177 err.append("Encountered unknown parameter type <");
178 err.append(param.type.getName());
179 err.append("> on @");
180 err.append(methodAnnotationClass.getSimpleName());
181 err.append(" annotated method: ");
182 err.append(ReflectUtils.toString(pojo,method));
183
184 throw new InvalidSignatureException(err.toString());
185 }
186 }
187 }
188
189 private boolean visitParam(JsrCallable callable, Param param, List<IJsrParamId> paramIds)
190 {
191 for (IJsrParamId paramId : paramIds)
192 {
193 LOG.debug("{}.process()",paramId);
194 if (paramId.process(param,callable))
195 {
196
197 LOG.debug("Identified: {}",param);
198 return true;
199 }
200 }
201
202
203 return false;
204 }
205 }