xref: /unit/src/java/nginx/unit/websocket/pojo/PojoMethodMapping.java (revision 1157:7ae152bda303)
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 package nginx.unit.websocket.pojo;
18 
19 import java.io.InputStream;
20 import java.io.Reader;
21 import java.lang.annotation.Annotation;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Modifier;
24 import java.nio.ByteBuffer;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 
33 import javax.websocket.CloseReason;
34 import javax.websocket.DecodeException;
35 import javax.websocket.Decoder;
36 import javax.websocket.DeploymentException;
37 import javax.websocket.EndpointConfig;
38 import javax.websocket.MessageHandler;
39 import javax.websocket.OnClose;
40 import javax.websocket.OnError;
41 import javax.websocket.OnMessage;
42 import javax.websocket.OnOpen;
43 import javax.websocket.PongMessage;
44 import javax.websocket.Session;
45 import javax.websocket.server.PathParam;
46 
47 import org.apache.tomcat.util.res.StringManager;
48 import nginx.unit.websocket.DecoderEntry;
49 import nginx.unit.websocket.Util;
50 import nginx.unit.websocket.Util.DecoderMatch;
51 
52 /**
53  * For a POJO class annotated with
54  * {@link javax.websocket.server.ServerEndpoint}, an instance of this class
55  * creates and caches the method handler, method information and parameter
56  * information for the onXXX calls.
57  */
58 public class PojoMethodMapping {
59 
60     private static final StringManager sm =
61             StringManager.getManager(PojoMethodMapping.class);
62 
63     private final Method onOpen;
64     private final Method onClose;
65     private final Method onError;
66     private final PojoPathParam[] onOpenParams;
67     private final PojoPathParam[] onCloseParams;
68     private final PojoPathParam[] onErrorParams;
69     private final List<MessageHandlerInfo> onMessage = new ArrayList<>();
70     private final String wsPath;
71 
72 
PojoMethodMapping(Class<?> clazzPojo, List<Class<? extends Decoder>> decoderClazzes, String wsPath)73     public PojoMethodMapping(Class<?> clazzPojo,
74             List<Class<? extends Decoder>> decoderClazzes, String wsPath)
75                     throws DeploymentException {
76 
77         this.wsPath = wsPath;
78 
79         List<DecoderEntry> decoders = Util.getDecoders(decoderClazzes);
80         Method open = null;
81         Method close = null;
82         Method error = null;
83         Method[] clazzPojoMethods = null;
84         Class<?> currentClazz = clazzPojo;
85         while (!currentClazz.equals(Object.class)) {
86             Method[] currentClazzMethods = currentClazz.getDeclaredMethods();
87             if (currentClazz == clazzPojo) {
88                 clazzPojoMethods = currentClazzMethods;
89             }
90             for (Method method : currentClazzMethods) {
91                 if (method.getAnnotation(OnOpen.class) != null) {
92                     checkPublic(method);
93                     if (open == null) {
94                         open = method;
95                     } else {
96                         if (currentClazz == clazzPojo ||
97                                 !isMethodOverride(open, method)) {
98                             // Duplicate annotation
99                             throw new DeploymentException(sm.getString(
100                                     "pojoMethodMapping.duplicateAnnotation",
101                                     OnOpen.class, currentClazz));
102                         }
103                     }
104                 } else if (method.getAnnotation(OnClose.class) != null) {
105                     checkPublic(method);
106                     if (close == null) {
107                         close = method;
108                     } else {
109                         if (currentClazz == clazzPojo ||
110                                 !isMethodOverride(close, method)) {
111                             // Duplicate annotation
112                             throw new DeploymentException(sm.getString(
113                                     "pojoMethodMapping.duplicateAnnotation",
114                                     OnClose.class, currentClazz));
115                         }
116                     }
117                 } else if (method.getAnnotation(OnError.class) != null) {
118                     checkPublic(method);
119                     if (error == null) {
120                         error = method;
121                     } else {
122                         if (currentClazz == clazzPojo ||
123                                 !isMethodOverride(error, method)) {
124                             // Duplicate annotation
125                             throw new DeploymentException(sm.getString(
126                                     "pojoMethodMapping.duplicateAnnotation",
127                                     OnError.class, currentClazz));
128                         }
129                     }
130                 } else if (method.getAnnotation(OnMessage.class) != null) {
131                     checkPublic(method);
132                     MessageHandlerInfo messageHandler = new MessageHandlerInfo(method, decoders);
133                     boolean found = false;
134                     for (MessageHandlerInfo otherMessageHandler : onMessage) {
135                         if (messageHandler.targetsSameWebSocketMessageType(otherMessageHandler)) {
136                             found = true;
137                             if (currentClazz == clazzPojo ||
138                                 !isMethodOverride(messageHandler.m, otherMessageHandler.m)) {
139                                 // Duplicate annotation
140                                 throw new DeploymentException(sm.getString(
141                                         "pojoMethodMapping.duplicateAnnotation",
142                                         OnMessage.class, currentClazz));
143                             }
144                         }
145                     }
146                     if (!found) {
147                         onMessage.add(messageHandler);
148                     }
149                 } else {
150                     // Method not annotated
151                 }
152             }
153             currentClazz = currentClazz.getSuperclass();
154         }
155         // If the methods are not on clazzPojo and they are overridden
156         // by a non annotated method in clazzPojo, they should be ignored
157         if (open != null && open.getDeclaringClass() != clazzPojo) {
158             if (isOverridenWithoutAnnotation(clazzPojoMethods, open, OnOpen.class)) {
159                 open = null;
160             }
161         }
162         if (close != null && close.getDeclaringClass() != clazzPojo) {
163             if (isOverridenWithoutAnnotation(clazzPojoMethods, close, OnClose.class)) {
164                 close = null;
165             }
166         }
167         if (error != null && error.getDeclaringClass() != clazzPojo) {
168             if (isOverridenWithoutAnnotation(clazzPojoMethods, error, OnError.class)) {
169                 error = null;
170             }
171         }
172         List<MessageHandlerInfo> overriddenOnMessage = new ArrayList<>();
173         for (MessageHandlerInfo messageHandler : onMessage) {
174             if (messageHandler.m.getDeclaringClass() != clazzPojo
175                     && isOverridenWithoutAnnotation(clazzPojoMethods, messageHandler.m, OnMessage.class)) {
176                 overriddenOnMessage.add(messageHandler);
177             }
178         }
179         for (MessageHandlerInfo messageHandler : overriddenOnMessage) {
180             onMessage.remove(messageHandler);
181         }
182         this.onOpen = open;
183         this.onClose = close;
184         this.onError = error;
185         onOpenParams = getPathParams(onOpen, MethodType.ON_OPEN);
186         onCloseParams = getPathParams(onClose, MethodType.ON_CLOSE);
187         onErrorParams = getPathParams(onError, MethodType.ON_ERROR);
188     }
189 
190 
checkPublic(Method m)191     private void checkPublic(Method m) throws DeploymentException {
192         if (!Modifier.isPublic(m.getModifiers())) {
193             throw new DeploymentException(sm.getString(
194                     "pojoMethodMapping.methodNotPublic", m.getName()));
195         }
196     }
197 
198 
isMethodOverride(Method method1, Method method2)199     private boolean isMethodOverride(Method method1, Method method2) {
200         return method1.getName().equals(method2.getName())
201                 && method1.getReturnType().equals(method2.getReturnType())
202                 && Arrays.equals(method1.getParameterTypes(), method2.getParameterTypes());
203     }
204 
205 
isOverridenWithoutAnnotation(Method[] methods, Method superclazzMethod, Class<? extends Annotation> annotation)206     private boolean isOverridenWithoutAnnotation(Method[] methods,
207             Method superclazzMethod, Class<? extends Annotation> annotation) {
208         for (Method method : methods) {
209             if (isMethodOverride(method, superclazzMethod)
210                     && (method.getAnnotation(annotation) == null)) {
211                 return true;
212             }
213         }
214         return false;
215     }
216 
217 
getWsPath()218     public String getWsPath() {
219         return wsPath;
220     }
221 
222 
getOnOpen()223     public Method getOnOpen() {
224         return onOpen;
225     }
226 
227 
getOnOpenArgs(Map<String,String> pathParameters, Session session, EndpointConfig config)228     public Object[] getOnOpenArgs(Map<String,String> pathParameters,
229             Session session, EndpointConfig config) throws DecodeException {
230         return buildArgs(onOpenParams, pathParameters, session, config, null,
231                 null);
232     }
233 
234 
getOnClose()235     public Method getOnClose() {
236         return onClose;
237     }
238 
239 
getOnCloseArgs(Map<String,String> pathParameters, Session session, CloseReason closeReason)240     public Object[] getOnCloseArgs(Map<String,String> pathParameters,
241             Session session, CloseReason closeReason) throws DecodeException {
242         return buildArgs(onCloseParams, pathParameters, session, null, null,
243                 closeReason);
244     }
245 
246 
getOnError()247     public Method getOnError() {
248         return onError;
249     }
250 
251 
getOnErrorArgs(Map<String,String> pathParameters, Session session, Throwable throwable)252     public Object[] getOnErrorArgs(Map<String,String> pathParameters,
253             Session session, Throwable throwable) throws DecodeException {
254         return buildArgs(onErrorParams, pathParameters, session, null,
255                 throwable, null);
256     }
257 
258 
hasMessageHandlers()259     public boolean hasMessageHandlers() {
260         return !onMessage.isEmpty();
261     }
262 
263 
getMessageHandlers(Object pojo, Map<String,String> pathParameters, Session session, EndpointConfig config)264     public Set<MessageHandler> getMessageHandlers(Object pojo,
265             Map<String,String> pathParameters, Session session,
266             EndpointConfig config) {
267         Set<MessageHandler> result = new HashSet<>();
268         for (MessageHandlerInfo messageMethod : onMessage) {
269             result.addAll(messageMethod.getMessageHandlers(pojo, pathParameters,
270                     session, config));
271         }
272         return result;
273     }
274 
275 
getPathParams(Method m, MethodType methodType)276     private static PojoPathParam[] getPathParams(Method m,
277             MethodType methodType) throws DeploymentException {
278         if (m == null) {
279             return new PojoPathParam[0];
280         }
281         boolean foundThrowable = false;
282         Class<?>[] types = m.getParameterTypes();
283         Annotation[][] paramsAnnotations = m.getParameterAnnotations();
284         PojoPathParam[] result = new PojoPathParam[types.length];
285         for (int i = 0; i < types.length; i++) {
286             Class<?> type = types[i];
287             if (type.equals(Session.class)) {
288                 result[i] = new PojoPathParam(type, null);
289             } else if (methodType == MethodType.ON_OPEN &&
290                     type.equals(EndpointConfig.class)) {
291                 result[i] = new PojoPathParam(type, null);
292             } else if (methodType == MethodType.ON_ERROR
293                     && type.equals(Throwable.class)) {
294                 foundThrowable = true;
295                 result[i] = new PojoPathParam(type, null);
296             } else if (methodType == MethodType.ON_CLOSE &&
297                     type.equals(CloseReason.class)) {
298                 result[i] = new PojoPathParam(type, null);
299             } else {
300                 Annotation[] paramAnnotations = paramsAnnotations[i];
301                 for (Annotation paramAnnotation : paramAnnotations) {
302                     if (paramAnnotation.annotationType().equals(
303                             PathParam.class)) {
304                         // Check that the type is valid. "0" coerces to every
305                         // valid type
306                         try {
307                             Util.coerceToType(type, "0");
308                         } catch (IllegalArgumentException iae) {
309                             throw new DeploymentException(sm.getString(
310                                     "pojoMethodMapping.invalidPathParamType"),
311                                     iae);
312                         }
313                         result[i] = new PojoPathParam(type,
314                                 ((PathParam) paramAnnotation).value());
315                         break;
316                     }
317                 }
318                 // Parameters without annotations are not permitted
319                 if (result[i] == null) {
320                     throw new DeploymentException(sm.getString(
321                             "pojoMethodMapping.paramWithoutAnnotation",
322                             type, m.getName(), m.getClass().getName()));
323                 }
324             }
325         }
326         if (methodType == MethodType.ON_ERROR && !foundThrowable) {
327             throw new DeploymentException(sm.getString(
328                     "pojoMethodMapping.onErrorNoThrowable",
329                     m.getName(), m.getDeclaringClass().getName()));
330         }
331         return result;
332     }
333 
334 
buildArgs(PojoPathParam[] pathParams, Map<String,String> pathParameters, Session session, EndpointConfig config, Throwable throwable, CloseReason closeReason)335     private static Object[] buildArgs(PojoPathParam[] pathParams,
336             Map<String,String> pathParameters, Session session,
337             EndpointConfig config, Throwable throwable, CloseReason closeReason)
338             throws DecodeException {
339         Object[] result = new Object[pathParams.length];
340         for (int i = 0; i < pathParams.length; i++) {
341             Class<?> type = pathParams[i].getType();
342             if (type.equals(Session.class)) {
343                 result[i] = session;
344             } else if (type.equals(EndpointConfig.class)) {
345                 result[i] = config;
346             } else if (type.equals(Throwable.class)) {
347                 result[i] = throwable;
348             } else if (type.equals(CloseReason.class)) {
349                 result[i] = closeReason;
350             } else {
351                 String name = pathParams[i].getName();
352                 String value = pathParameters.get(name);
353                 try {
354                     result[i] = Util.coerceToType(type, value);
355                 } catch (Exception e) {
356                     throw new DecodeException(value, sm.getString(
357                             "pojoMethodMapping.decodePathParamFail",
358                             value, type), e);
359                 }
360             }
361         }
362         return result;
363     }
364 
365 
366     private static class MessageHandlerInfo {
367 
368         private final Method m;
369         private int indexString = -1;
370         private int indexByteArray = -1;
371         private int indexByteBuffer = -1;
372         private int indexPong = -1;
373         private int indexBoolean = -1;
374         private int indexSession = -1;
375         private int indexInputStream = -1;
376         private int indexReader = -1;
377         private int indexPrimitive = -1;
378         private Class<?> primitiveType = null;
379         private Map<Integer,PojoPathParam> indexPathParams = new HashMap<>();
380         private int indexPayload = -1;
381         private DecoderMatch decoderMatch = null;
382         private long maxMessageSize = -1;
383 
MessageHandlerInfo(Method m, List<DecoderEntry> decoderEntries)384         public MessageHandlerInfo(Method m, List<DecoderEntry> decoderEntries) {
385             this.m = m;
386 
387             Class<?>[] types = m.getParameterTypes();
388             Annotation[][] paramsAnnotations = m.getParameterAnnotations();
389 
390             for (int i = 0; i < types.length; i++) {
391                 boolean paramFound = false;
392                 Annotation[] paramAnnotations = paramsAnnotations[i];
393                 for (Annotation paramAnnotation : paramAnnotations) {
394                     if (paramAnnotation.annotationType().equals(
395                             PathParam.class)) {
396                         indexPathParams.put(
397                                 Integer.valueOf(i), new PojoPathParam(types[i],
398                                         ((PathParam) paramAnnotation).value()));
399                         paramFound = true;
400                         break;
401                     }
402                 }
403                 if (paramFound) {
404                     continue;
405                 }
406                 if (String.class.isAssignableFrom(types[i])) {
407                     if (indexString == -1) {
408                         indexString = i;
409                     } else {
410                         throw new IllegalArgumentException(sm.getString(
411                                 "pojoMethodMapping.duplicateMessageParam",
412                                 m.getName(), m.getDeclaringClass().getName()));
413                     }
414                 } else if (Reader.class.isAssignableFrom(types[i])) {
415                     if (indexReader == -1) {
416                         indexReader = i;
417                     } else {
418                         throw new IllegalArgumentException(sm.getString(
419                                 "pojoMethodMapping.duplicateMessageParam",
420                                 m.getName(), m.getDeclaringClass().getName()));
421                     }
422                 } else if (boolean.class == types[i]) {
423                     if (indexBoolean == -1) {
424                         indexBoolean = i;
425                     } else {
426                         throw new IllegalArgumentException(sm.getString(
427                                 "pojoMethodMapping.duplicateLastParam",
428                                 m.getName(), m.getDeclaringClass().getName()));
429                     }
430                 } else if (ByteBuffer.class.isAssignableFrom(types[i])) {
431                     if (indexByteBuffer == -1) {
432                         indexByteBuffer = i;
433                     } else {
434                         throw new IllegalArgumentException(sm.getString(
435                                 "pojoMethodMapping.duplicateMessageParam",
436                                 m.getName(), m.getDeclaringClass().getName()));
437                     }
438                 } else if (byte[].class == types[i]) {
439                     if (indexByteArray == -1) {
440                         indexByteArray = i;
441                     } else {
442                         throw new IllegalArgumentException(sm.getString(
443                                 "pojoMethodMapping.duplicateMessageParam",
444                                 m.getName(), m.getDeclaringClass().getName()));
445                     }
446                 } else if (InputStream.class.isAssignableFrom(types[i])) {
447                     if (indexInputStream == -1) {
448                         indexInputStream = i;
449                     } else {
450                         throw new IllegalArgumentException(sm.getString(
451                                 "pojoMethodMapping.duplicateMessageParam",
452                                 m.getName(), m.getDeclaringClass().getName()));
453                     }
454                 } else if (Util.isPrimitive(types[i])) {
455                     if (indexPrimitive == -1) {
456                         indexPrimitive = i;
457                         primitiveType = types[i];
458                     } else {
459                         throw new IllegalArgumentException(sm.getString(
460                                 "pojoMethodMapping.duplicateMessageParam",
461                                 m.getName(), m.getDeclaringClass().getName()));
462                     }
463                 } else if (Session.class.isAssignableFrom(types[i])) {
464                     if (indexSession == -1) {
465                         indexSession = i;
466                     } else {
467                         throw new IllegalArgumentException(sm.getString(
468                                 "pojoMethodMapping.duplicateSessionParam",
469                                 m.getName(), m.getDeclaringClass().getName()));
470                     }
471                 } else if (PongMessage.class.isAssignableFrom(types[i])) {
472                     if (indexPong == -1) {
473                         indexPong = i;
474                     } else {
475                         throw new IllegalArgumentException(sm.getString(
476                                 "pojoMethodMapping.duplicatePongMessageParam",
477                                 m.getName(), m.getDeclaringClass().getName()));
478                     }
479                 } else {
480                     if (decoderMatch != null && decoderMatch.hasMatches()) {
481                         throw new IllegalArgumentException(sm.getString(
482                                 "pojoMethodMapping.duplicateMessageParam",
483                                 m.getName(), m.getDeclaringClass().getName()));
484                     }
485                     decoderMatch = new DecoderMatch(types[i], decoderEntries);
486 
487                     if (decoderMatch.hasMatches()) {
488                         indexPayload = i;
489                     }
490                 }
491             }
492 
493             // Additional checks required
494             if (indexString != -1) {
495                 if (indexPayload != -1) {
496                     throw new IllegalArgumentException(sm.getString(
497                             "pojoMethodMapping.duplicateMessageParam",
498                             m.getName(), m.getDeclaringClass().getName()));
499                 } else {
500                     indexPayload = indexString;
501                 }
502             }
503             if (indexReader != -1) {
504                 if (indexPayload != -1) {
505                     throw new IllegalArgumentException(sm.getString(
506                             "pojoMethodMapping.duplicateMessageParam",
507                             m.getName(), m.getDeclaringClass().getName()));
508                 } else {
509                     indexPayload = indexReader;
510                 }
511             }
512             if (indexByteArray != -1) {
513                 if (indexPayload != -1) {
514                     throw new IllegalArgumentException(sm.getString(
515                             "pojoMethodMapping.duplicateMessageParam",
516                             m.getName(), m.getDeclaringClass().getName()));
517                 } else {
518                     indexPayload = indexByteArray;
519                 }
520             }
521             if (indexByteBuffer != -1) {
522                 if (indexPayload != -1) {
523                     throw new IllegalArgumentException(sm.getString(
524                             "pojoMethodMapping.duplicateMessageParam",
525                             m.getName(), m.getDeclaringClass().getName()));
526                 } else {
527                     indexPayload = indexByteBuffer;
528                 }
529             }
530             if (indexInputStream != -1) {
531                 if (indexPayload != -1) {
532                     throw new IllegalArgumentException(sm.getString(
533                             "pojoMethodMapping.duplicateMessageParam",
534                             m.getName(), m.getDeclaringClass().getName()));
535                 } else {
536                     indexPayload = indexInputStream;
537                 }
538             }
539             if (indexPrimitive != -1) {
540                 if (indexPayload != -1) {
541                     throw new IllegalArgumentException(sm.getString(
542                             "pojoMethodMapping.duplicateMessageParam",
543                             m.getName(), m.getDeclaringClass().getName()));
544                 } else {
545                     indexPayload = indexPrimitive;
546                 }
547             }
548             if (indexPong != -1) {
549                 if (indexPayload != -1) {
550                     throw new IllegalArgumentException(sm.getString(
551                             "pojoMethodMapping.pongWithPayload",
552                             m.getName(), m.getDeclaringClass().getName()));
553                 } else {
554                     indexPayload = indexPong;
555                 }
556             }
557             if (indexPayload == -1 && indexPrimitive == -1 &&
558                     indexBoolean != -1) {
559                 // The boolean we found is a payload, not a last flag
560                 indexPayload = indexBoolean;
561                 indexPrimitive = indexBoolean;
562                 primitiveType = Boolean.TYPE;
563                 indexBoolean = -1;
564             }
565             if (indexPayload == -1) {
566                 throw new IllegalArgumentException(sm.getString(
567                         "pojoMethodMapping.noPayload",
568                         m.getName(), m.getDeclaringClass().getName()));
569             }
570             if (indexPong != -1 && indexBoolean != -1) {
571                 throw new IllegalArgumentException(sm.getString(
572                         "pojoMethodMapping.partialPong",
573                         m.getName(), m.getDeclaringClass().getName()));
574             }
575             if(indexReader != -1 && indexBoolean != -1) {
576                 throw new IllegalArgumentException(sm.getString(
577                         "pojoMethodMapping.partialReader",
578                         m.getName(), m.getDeclaringClass().getName()));
579             }
580             if(indexInputStream != -1 && indexBoolean != -1) {
581                 throw new IllegalArgumentException(sm.getString(
582                         "pojoMethodMapping.partialInputStream",
583                         m.getName(), m.getDeclaringClass().getName()));
584             }
585             if (decoderMatch != null && decoderMatch.hasMatches() &&
586                     indexBoolean != -1) {
587                 throw new IllegalArgumentException(sm.getString(
588                         "pojoMethodMapping.partialObject",
589                         m.getName(), m.getDeclaringClass().getName()));
590             }
591 
592             maxMessageSize = m.getAnnotation(OnMessage.class).maxMessageSize();
593         }
594 
595 
targetsSameWebSocketMessageType(MessageHandlerInfo otherHandler)596         public boolean targetsSameWebSocketMessageType(MessageHandlerInfo otherHandler) {
597             if (otherHandler == null) {
598                 return false;
599             }
600             if (indexByteArray >= 0 && otherHandler.indexByteArray >= 0) {
601                 return true;
602             }
603             if (indexByteBuffer >= 0 && otherHandler.indexByteBuffer >= 0) {
604                 return true;
605             }
606             if (indexInputStream >= 0 && otherHandler.indexInputStream >= 0) {
607                 return true;
608             }
609             if (indexPong >= 0 && otherHandler.indexPong >= 0) {
610                 return true;
611             }
612             if (indexPrimitive >= 0 && otherHandler.indexPrimitive >= 0
613                     && primitiveType == otherHandler.primitiveType) {
614                 return true;
615             }
616             if (indexReader >= 0 && otherHandler.indexReader >= 0) {
617                 return true;
618             }
619             if (indexString >= 0 && otherHandler.indexString >= 0) {
620                 return true;
621             }
622             if (decoderMatch != null && otherHandler.decoderMatch != null
623                     && decoderMatch.getTarget().equals(otherHandler.decoderMatch.getTarget())) {
624                 return true;
625             }
626             return false;
627         }
628 
629 
getMessageHandlers(Object pojo, Map<String,String> pathParameters, Session session, EndpointConfig config)630         public Set<MessageHandler> getMessageHandlers(Object pojo,
631                 Map<String,String> pathParameters, Session session,
632                 EndpointConfig config) {
633             Object[] params = new Object[m.getParameterTypes().length];
634 
635             for (Map.Entry<Integer,PojoPathParam> entry :
636                     indexPathParams.entrySet()) {
637                 PojoPathParam pathParam = entry.getValue();
638                 String valueString = pathParameters.get(pathParam.getName());
639                 Object value = null;
640                 try {
641                     value = Util.coerceToType(pathParam.getType(), valueString);
642                 } catch (Exception e) {
643                     DecodeException de =  new DecodeException(valueString,
644                             sm.getString(
645                                     "pojoMethodMapping.decodePathParamFail",
646                                     valueString, pathParam.getType()), e);
647                     params = new Object[] { de };
648                     break;
649                 }
650                 params[entry.getKey().intValue()] = value;
651             }
652 
653             Set<MessageHandler> results = new HashSet<>(2);
654             if (indexBoolean == -1) {
655                 // Basic
656                 if (indexString != -1 || indexPrimitive != -1) {
657                     MessageHandler mh = new PojoMessageHandlerWholeText(pojo, m,
658                             session, config, null, params, indexPayload, false,
659                             indexSession, maxMessageSize);
660                     results.add(mh);
661                 } else if (indexReader != -1) {
662                     MessageHandler mh = new PojoMessageHandlerWholeText(pojo, m,
663                             session, config, null, params, indexReader, true,
664                             indexSession, maxMessageSize);
665                     results.add(mh);
666                 } else if (indexByteArray != -1) {
667                     MessageHandler mh = new PojoMessageHandlerWholeBinary(pojo,
668                             m, session, config, null, params, indexByteArray,
669                             true, indexSession, false, maxMessageSize);
670                     results.add(mh);
671                 } else if (indexByteBuffer != -1) {
672                     MessageHandler mh = new PojoMessageHandlerWholeBinary(pojo,
673                             m, session, config, null, params, indexByteBuffer,
674                             false, indexSession, false, maxMessageSize);
675                     results.add(mh);
676                 } else if (indexInputStream != -1) {
677                     MessageHandler mh = new PojoMessageHandlerWholeBinary(pojo,
678                             m, session, config, null, params, indexInputStream,
679                             true, indexSession, true, maxMessageSize);
680                     results.add(mh);
681                 } else if (decoderMatch != null && decoderMatch.hasMatches()) {
682                     if (decoderMatch.getBinaryDecoders().size() > 0) {
683                         MessageHandler mh = new PojoMessageHandlerWholeBinary(
684                                 pojo, m, session, config,
685                                 decoderMatch.getBinaryDecoders(), params,
686                                 indexPayload, true, indexSession, true,
687                                 maxMessageSize);
688                         results.add(mh);
689                     }
690                     if (decoderMatch.getTextDecoders().size() > 0) {
691                         MessageHandler mh = new PojoMessageHandlerWholeText(
692                                 pojo, m, session, config,
693                                 decoderMatch.getTextDecoders(), params,
694                                 indexPayload, true, indexSession, maxMessageSize);
695                         results.add(mh);
696                     }
697                 } else {
698                     MessageHandler mh = new PojoMessageHandlerWholePong(pojo, m,
699                             session, params, indexPong, false, indexSession);
700                     results.add(mh);
701                 }
702             } else {
703                 // ASync
704                 if (indexString != -1) {
705                     MessageHandler mh = new PojoMessageHandlerPartialText(pojo,
706                             m, session, params, indexString, false,
707                             indexBoolean, indexSession, maxMessageSize);
708                     results.add(mh);
709                 } else if (indexByteArray != -1) {
710                     MessageHandler mh = new PojoMessageHandlerPartialBinary(
711                             pojo, m, session, params, indexByteArray, true,
712                             indexBoolean, indexSession, maxMessageSize);
713                     results.add(mh);
714                 } else {
715                     MessageHandler mh = new PojoMessageHandlerPartialBinary(
716                             pojo, m, session, params, indexByteBuffer, false,
717                             indexBoolean, indexSession, maxMessageSize);
718                     results.add(mh);
719                 }
720             }
721             return results;
722         }
723     }
724 
725 
726     private enum MethodType {
727         ON_OPEN,
728         ON_CLOSE,
729         ON_ERROR
730     }
731 }
732