org.nosceon.titanite.Router.java Source code

Java tutorial

Introduction

Here is the source code for org.nosceon.titanite.Router.java

Source

/*
 * Copyright 2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.nosceon.titanite;

import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import org.nosceon.titanite.body.EmptyBodyParser;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
import java.util.function.Function;

import static java.util.Collections.emptyMap;
import static java.util.stream.Collectors.toList;

/**
 * @author Johan Siebens
 */
final class Router {

    private static final RoutingResult METHOD_NOT_ALLOWED = new RoutingResult(emptyMap(), EmptyBodyParser::new,
            (r) -> Response.methodNotAllowed().toFuture());

    private static final RoutingResult NOT_FOUND = new RoutingResult(emptyMap(), EmptyBodyParser::new,
            (r) -> Response.notFound().toFuture());

    private final List<Route> routes = new LinkedList<>();

    Router(List<Route> routings) {
        this.routes.addAll(routings);
    }

    RoutingResult find(HttpMethod httpMethod, String path) {
        Method method = map(httpMethod);

        List<Route> candidates = this.routes.stream().filter(r -> r.pattern().matches(path)).collect(toList());

        if (candidates.isEmpty()) {
            return NOT_FOUND;
        } else {
            return candidates.stream().filter(route -> route.hasMethod(method)).findFirst().map(route -> {
                ParameterizedPattern.Matcher matcher = route.pattern().matcher(path);
                return Method.OPTIONS.equals(method)
                        ? new RoutingResult(matcher.parameters(), route.bodyParser(),
                                new CompositeHandler(allowedMethodsFilter(candidates), route.handler()))
                        : new RoutingResult(matcher.parameters(), route.bodyParser(), route.handler());
            }).orElseGet(() -> {
                if (Method.OPTIONS.equals(method)) {
                    return new RoutingResult(emptyMap(), EmptyBodyParser::new, new CompositeHandler(
                            allowedMethodsFilter(candidates), req -> Response.ok().toFuture()));
                } else if (Method.HEAD.equals(method)) {
                    return find(HttpMethod.GET, path);
                } else {
                    return METHOD_NOT_ALLOWED;
                }
            });
        }

    }

    private BiFunction<Request, Function<Request, CompletionStage<Response>>, CompletionStage<Response>> allowedMethodsFilter(
            List<Route> candidates) {
        return (req, h) -> h.apply(req)
                .thenApply(resp -> resp.header(HttpHeaders.Names.ALLOW, allowedMethods(candidates)));
    }

    private String allowedMethods(List<Route> candidates) {
        return candidates.stream().map(Route::method).distinct().map(Method::name).sorted().reduce("",
                (s, s2) -> s.length() == 0 ? s2 : s + ", " + s2);
    }

    private Method map(HttpMethod method) {
        try {
            return Method.valueOf(method.name());
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

}