/*
* Copyright 2013 Simon Taddiken
*
* This file is part of Polly HTTP API.
*
* Polly HTTP API is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Polly HTTP API is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with Polly HTTP API. If not, see http://www.gnu.org/licenses/.
*/
package de.skuzzle.polly.http.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.skuzzle.polly.http.annotations.Get;
import de.skuzzle.polly.http.annotations.Param;
import de.skuzzle.polly.http.annotations.Post;
import de.skuzzle.polly.http.annotations.OnRegister;
import de.skuzzle.polly.http.api.Controller;
import de.skuzzle.polly.http.api.HttpEvent.RequestMode;
import de.skuzzle.polly.http.api.AddHandlerListener;
import de.skuzzle.polly.http.api.HttpServletServer;
import de.skuzzle.polly.http.api.ParameterHandler;
import de.skuzzle.polly.http.api.ServerFactory;
import de.skuzzle.polly.http.api.answers.HttpAnswer;
class HttpServletServerImpl extends HttpServerImpl implements HttpServletServer {
private final List<ParameterHandler> paramHandler;
private final List<AddHandlerListener> addHandlerListener;
private final Map<String, String> handledUrls;
public HttpServletServerImpl(ServerFactory factory) {
super(factory);
this.paramHandler = new ArrayList<ParameterHandler>();
this.addHandlerListener = new ArrayList<>();
this.handledUrls = new HashMap<>();
this.addParameterHandler(NativeHandlers.BOOLEAN);
this.addParameterHandler(NativeHandlers.INTEGER);
this.addParameterHandler(NativeHandlers.STRING);
this.addParameterHandler(NativeHandlers.STRING_LIST);
this.addParameterHandler(NativeHandlers.INT_LIST);
}
@Override
public void addParameterHandler(ParameterHandler handler) {
this.paramHandler.add(handler);
}
@Override
public void addController(Controller carrier) {
this.findRequestHandlers(carrier, carrier.getClass());
}
@Override
public void addAddHandlerListener(AddHandlerListener listener) {
this.addHandlerListener.add(listener);
}
@Override
public void removeAddHandlerListener(AddHandlerListener listener) {
this.addHandlerListener.remove(listener);
}
@Override
public Map<String, String> getHandledUrls() {
return this.handledUrls;
}
private void findRequestHandlers(Controller carrier, Class<?> cls) {
for (final Method mtd : cls.getMethods()) {
final RequestMode mode;
String url;
String name;
boolean matchExcatly;
if (mtd.isAnnotationPresent(Get.class)) {
mode = RequestMode.GET;
url = mtd.getAnnotation(Get.class).value();
name = mtd.getAnnotation(Get.class).name();
matchExcatly = mtd.getAnnotation(Get.class).matchExactly();
} else if (mtd.isAnnotationPresent(Post.class)) {
mode = RequestMode.POST;
url = mtd.getAnnotation(Post.class).value();
name = mtd.getAnnotation(Post.class).name();
matchExcatly = mtd.getAnnotation(Post.class).matchExactly();
} else {
continue;
}
if (name.equals("")) {
name = mtd.getName();
}
// validate return type
if (!HttpAnswer.class.isAssignableFrom(mtd.getReturnType())) {
throw new IllegalArgumentException("handler has illegal return type");
}
// validate parameters
for (int i = 0; i < mtd.getParameterTypes().length; ++i) {
final Annotation[] an = mtd.getParameterAnnotations()[i];
Param param = null;
for (Annotation a : an) {
if (a instanceof Param) {
param = (Param) a;
break;
}
}
if (param == null) {
throw new IllegalArgumentException(
"parameter is not annotated with @Param");
}
final Class<?> type = mtd.getParameterTypes()[i];
final Class<?> typeVar = param.typeHint();
if (this.findHandler(type, typeVar) == null) {
throw new IllegalArgumentException("No handler for parameter "
+ param.value());
}
}
if (!url.startsWith("/")) {
url = "/" + url;
}
boolean isStatic = Modifier.isStatic(mtd.getModifiers());
final ReflectionHttpHandler rhh = new ReflectionHttpHandler(
mode, url, isStatic ? null : carrier, mtd, this, matchExcatly);
this.addHttpEventHandler(url, rhh);
carrier.putHandledURL(this.handledUrls, name, url);
if (mtd.isAnnotationPresent(OnRegister.class)) {
final OnRegister te = mtd.getAnnotation(OnRegister.class);
this.fireHandlerAdded(carrier, url, name, te.value());
}
}
}
ParameterHandler findHandler(Class<?> type, Class<?> typeVar) {
for (final ParameterHandler ph : this.paramHandler) {
if (ph.canHandle(type, typeVar)) {
return ph;
}
}
return null;
}
private void fireHandlerAdded(Controller c, String url, String name, String[] values) {
for (final AddHandlerListener l : this.addHandlerListener) {
l.handlerAdded(c, url, name, values);
}
}
}