/*
* Copyright 2016 LINE Corporation
*
* LINE Corporation licenses this file to you 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 com.linecorp.armeria.server.http.dynamic;
import static java.util.Objects.requireNonNull;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.google.common.collect.Sets;
import com.linecorp.armeria.common.http.HttpMethod;
import com.linecorp.armeria.common.http.PathParamExtractor;
/**
* Builds a new {@link DynamicHttpService}.
*/
public final class DynamicHttpServiceBuilder {
private final Map<Class<?>, ResponseConverter> converters = new HashMap<>();
private final List<DynamicHttpFunctionEntry> entries = new ArrayList<>();
/**
* Adds a mapping from {@link Class} to {@link ResponseConverter}.
*/
public DynamicHttpServiceBuilder addConverter(Class<?> type, ResponseConverter converter) {
this.converters.put(type, converter);
return this;
}
/**
* Adds a new dynamic-mapped method, invoked with given {@link HttpMethod} and (dynamically-bound) path,
* runs synchronously.
*/
public DynamicHttpServiceBuilder addMapping(HttpMethod method, String path, DynamicHttpFunction function) {
return addMapping(EnumSet.of(method), path, function);
}
/**
* Adds a new dynamic-mapped method, invoked with given {@link HttpMethod} and (dynamically-bound) path,
* runs synchronously.
*/
public DynamicHttpServiceBuilder addMapping(HttpMethod method, String path, DynamicHttpFunction function,
ResponseConverter converter) {
return addMapping(EnumSet.of(method), path, function, converter);
}
/**
* Adds a new dynamic-mapped method, invoked with given {@link HttpMethod}s and (dynamically-bound) path,
* runs synchronously.
*/
public DynamicHttpServiceBuilder addMapping(Iterable<HttpMethod> methods, String path,
DynamicHttpFunction function) {
DynamicHttpFunction f = DynamicHttpFunctions.of(function, converters);
DynamicHttpFunctionEntry entry = new DynamicHttpFunctionEntry(
Sets.immutableEnumSet(methods), new PathParamExtractor(path), f);
validate(entry);
entries.add(entry);
return this;
}
/**
* Adds a new dynamic-mapped method, invoked with given {@link HttpMethod}s and (dynamically-bound) path,
* runs synchronously.
*/
public DynamicHttpServiceBuilder addMapping(Iterable<HttpMethod> methods, String path,
DynamicHttpFunction function, ResponseConverter converter) {
DynamicHttpFunction composed = DynamicHttpFunctions.of(function, converter);
return addMapping(methods, path, composed);
}
/**
* Add the {@link Path} annotated methods in given object.
*
* @see Path
* @see PathParam
*/
public DynamicHttpServiceBuilder addMappings(Object strategy) {
for (DynamicHttpFunctionEntry entry : Methods.entries(strategy, converters)) {
validate(entry);
entries.add(entry);
}
return this;
}
/**
* Check whether the mapping pattern for {@code entry} already exists.
*
* @throws IllegalArgumentException if there is already a {@link DynamicHttpFunctionEntry} whose mapping
* pattern overlaps with {@code entry}.
*/
private void validate(DynamicHttpFunctionEntry entry) {
requireNonNull(entry, "entry");
Optional<DynamicHttpFunctionEntry> overlapped = entries.stream()
.filter(e -> e.overlaps(entry))
.findFirst();
if (overlapped.isPresent()) {
throw new IllegalArgumentException(
"Mapping conflicts: " + entry.toString() + ", " + overlapped.get().toString());
}
}
/**
* Creates a new {@link DynamicHttpService} with the configuration properties set so far.
*/
public DynamicHttpService build() {
return new DynamicHttpService(entries);
}
}