blob: 6977a3e6b1c5f66d24add8214430e062ea9171ec [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.jsr356.annotations;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.common.events.annotated.AbstractMethodAnnotationScanner;
import org.eclipse.jetty.websocket.common.events.annotated.InvalidSignatureException;
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
public class AnnotatedEndpointScanner<T extends Annotation, C extends EndpointConfig> extends AbstractMethodAnnotationScanner<AnnotatedEndpointMetadata<T, C>>
{
private static final Logger LOG = Log.getLogger(AnnotatedEndpointScanner.class);
private final LinkedList<IJsrParamId> paramsOnOpen;
private final LinkedList<IJsrParamId> paramsOnClose;
private final LinkedList<IJsrParamId> paramsOnError;
private final LinkedList<IJsrParamId> paramsOnMessage;
private final AnnotatedEndpointMetadata<T, C> metadata;
public AnnotatedEndpointScanner(AnnotatedEndpointMetadata<T, C> metadata)
{
this.metadata = metadata;
paramsOnOpen = new LinkedList<>();
paramsOnClose = new LinkedList<>();
paramsOnError = new LinkedList<>();
paramsOnMessage = new LinkedList<>();
metadata.customizeParamsOnOpen(paramsOnOpen);
paramsOnOpen.add(JsrParamIdOnOpen.INSTANCE);
metadata.customizeParamsOnClose(paramsOnClose);
paramsOnClose.add(JsrParamIdOnClose.INSTANCE);
metadata.customizeParamsOnError(paramsOnError);
paramsOnError.add(JsrParamIdOnError.INSTANCE);
metadata.customizeParamsOnMessage(paramsOnMessage);
paramsOnMessage.add(JsrParamIdText.INSTANCE);
paramsOnMessage.add(JsrParamIdBinary.INSTANCE);
paramsOnMessage.add(JsrParamIdPong.INSTANCE);
}
private void assertNotDuplicate(JsrCallable callable, Class<? extends Annotation> methodAnnotationClass, Class<?> pojo, Method method)
{
if (callable != null)
{
// Duplicate annotation detected
StringBuilder err = new StringBuilder();
err.append("Encountered duplicate method annotations @");
err.append(methodAnnotationClass.getSimpleName());
err.append(" on ");
err.append(ReflectUtils.toString(pojo,callable.getMethod()));
err.append(" and ");
err.append(ReflectUtils.toString(pojo,method));
throw new InvalidSignatureException(err.toString());
}
}
@Override
public void onMethodAnnotation(AnnotatedEndpointMetadata<T, C> metadata, Class<?> pojo, Method method, Annotation annotation)
{
if (LOG.isDebugEnabled())
{
LOG.debug("onMethodAnnotation({}, {}, {}, {})",metadata,pojo,method,annotation);
}
if (isAnnotation(annotation,OnOpen.class))
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
assertNotDuplicate(metadata.onOpen,OnOpen.class,pojo,method);
OnOpenCallable onopen = new OnOpenCallable(pojo,method);
visitMethod(onopen,pojo,method,paramsOnOpen,OnOpen.class);
metadata.onOpen = onopen;
return;
}
if (isAnnotation(annotation,OnClose.class))
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
assertNotDuplicate(metadata.onClose,OnClose.class,pojo,method);
OnCloseCallable onclose = new OnCloseCallable(pojo,method);
visitMethod(onclose,pojo,method,paramsOnClose,OnClose.class);
metadata.onClose = onclose;
return;
}
if (isAnnotation(annotation,OnError.class))
{
assertIsPublicNonStatic(method);
assertIsReturn(method,Void.TYPE);
assertNotDuplicate(metadata.onError,OnError.class,pojo,method);
OnErrorCallable onerror = new OnErrorCallable(pojo,method);
visitMethod(onerror,pojo,method,paramsOnError,OnError.class);
metadata.onError = onerror;
return;
}
if (isAnnotation(annotation,OnMessage.class))
{
assertIsPublicNonStatic(method);
// assertIsReturn(method,Void.TYPE); // no validation, it can be any return type
OnMessageCallable onmessage = new OnMessageCallable(pojo,method);
visitMethod(onmessage,pojo,method,paramsOnMessage,OnMessage.class);
Param param = onmessage.getMessageObjectParam();
switch (param.role)
{
case MESSAGE_BINARY:
metadata.onBinary = new OnMessageBinaryCallable(onmessage);
break;
case MESSAGE_BINARY_STREAM:
metadata.onBinaryStream = new OnMessageBinaryStreamCallable(onmessage);
break;
case MESSAGE_TEXT:
metadata.onText = new OnMessageTextCallable(onmessage);
break;
case MESSAGE_TEXT_STREAM:
metadata.onTextStream = new OnMessageTextStreamCallable(onmessage);
break;
case MESSAGE_PONG:
metadata.onPong = new OnMessagePongCallable(onmessage);
break;
default:
StringBuilder err = new StringBuilder();
err.append("An unrecognized message type <");
err.append(param.type);
err.append(">: does not meet specified type categories of [TEXT, BINARY, DECODER, or PONG]");
throw new InvalidSignatureException(err.toString());
}
}
}
public AnnotatedEndpointMetadata<T, C> scan()
{
scanMethodAnnotations(metadata,metadata.getEndpointClass());
return metadata;
}
private void visitMethod(JsrCallable callable, Class<?> pojo, Method method, LinkedList<IJsrParamId> paramIds,
Class<? extends Annotation> methodAnnotationClass)
{
// Identify all of the parameters
for (Param param : callable.getParams())
{
if (!visitParam(callable,param,paramIds))
{
StringBuilder err = new StringBuilder();
err.append("Encountered unknown parameter type <");
err.append(param.type.getName());
err.append("> on @");
err.append(methodAnnotationClass.getSimpleName());
err.append(" annotated method: ");
err.append(ReflectUtils.toString(pojo,method));
throw new InvalidSignatureException(err.toString());
}
}
}
private boolean visitParam(JsrCallable callable, Param param, List<IJsrParamId> paramIds)
{
for (IJsrParamId paramId : paramIds)
{
if (LOG.isDebugEnabled())
{
LOG.debug("{}.process()",paramId);
}
if (paramId.process(param,callable))
{
// Successfully identified
if (LOG.isDebugEnabled())
{
LOG.debug("Identified: {}",param);
}
return true;
}
}
// Failed identification as a known parameter
return false;
}
}