feat(triple): Dubbo triple&rest protocol convergense (#13607)

This commit is contained in:
Sean Yang 2024-02-01 14:00:16 +08:00 committed by GitHub
parent e60f24cb7f
commit 2dde7ac09e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
286 changed files with 23479 additions and 2643 deletions

View File

@ -120,3 +120,7 @@ dubbo-tracing
dubbo-xds
dubbo-plugin-proxy-bytebuddy
dubbo-plugin-loom
dubbo-rest-jaxrs
dubbo-rest-servlet
dubbo-rest-spring

View File

@ -16,6 +16,9 @@
*/
package org.apache.dubbo.common.extension;
import java.util.Collections;
import java.util.List;
/**
* Uniform accessor for extension
*/
@ -24,7 +27,7 @@ public interface ExtensionAccessor {
ExtensionDirector getExtensionDirector();
default <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
return this.getExtensionDirector().getExtensionLoader(type);
return getExtensionDirector().getExtensionLoader(type);
}
default <T> T getExtension(Class<T> type, String name) {
@ -41,4 +44,21 @@ public interface ExtensionAccessor {
ExtensionLoader<T> extensionLoader = getExtensionLoader(type);
return extensionLoader != null ? extensionLoader.getDefaultExtension() : null;
}
default <T> List<T> getActivateExtensions(Class<T> type) {
ExtensionLoader<T> extensionLoader = getExtensionLoader(type);
return extensionLoader != null ? extensionLoader.getActivateExtensions() : Collections.emptyList();
}
default <T> T getFirstActivateExtension(Class<T> type) {
ExtensionLoader<T> extensionLoader = getExtensionLoader(type);
if (extensionLoader == null) {
throw new IllegalArgumentException("ExtensionLoader for [" + type + "] is not found");
}
List<T> extensions = extensionLoader.getActivateExtensions();
if (extensions.isEmpty()) {
throw new IllegalArgumentException("No activate extensions for [" + type + "] found");
}
return extensions.get(0);
}
}

View File

@ -16,8 +16,13 @@
*/
package org.apache.dubbo.common.io;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Stream utils.
@ -229,4 +234,51 @@ public class StreamUtils {
is.skip(is.available());
}
}
public static void copy(InputStream in, OutputStream out) throws IOException {
if (in.getClass() == ByteArrayInputStream.class) {
copy((ByteArrayInputStream) in, out);
return;
}
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
public static void copy(ByteArrayInputStream in, OutputStream out) throws IOException {
int len = in.available();
byte[] buffer = new byte[len];
in.read(buffer, 0, len);
out.write(buffer, 0, len);
}
public static byte[] readBytes(InputStream in) throws IOException {
if (in.getClass() == ByteArrayInputStream.class) {
return readBytes((ByteArrayInputStream) in);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
return out.toByteArray();
}
public static byte[] readBytes(ByteArrayInputStream in) throws IOException {
int len = in.available();
byte[] bytes = new byte[len];
in.read(bytes, 0, len);
return bytes;
}
public static String toString(InputStream in) throws IOException {
return new String(readBytes(in), StandardCharsets.UTF_8);
}
public static String toString(InputStream in, Charset charset) throws IOException {
return new String(readBytes(in), charset);
}
}

View File

@ -40,6 +40,10 @@ public interface JsonUtil {
Map<String, ?> getObject(Map<String, ?> obj, String key);
Object convertObject(Object obj, Type type);
Object convertObject(Object obj, Class<?> clazz);
Double getNumberAsDouble(Map<String, ?> obj, String key);
Integer getNumberAsInteger(Map<String, ?> obj, String key);

View File

@ -36,4 +36,14 @@ public class FastJson2Impl extends AbstractJsonUtilImpl {
public String toJson(Object obj) {
return com.alibaba.fastjson2.JSON.toJSONString(obj, JSONWriter.Feature.WriteEnumsUsingName);
}
@Override
public Object convertObject(Object obj, Type type) {
return com.alibaba.fastjson2.util.TypeUtils.cast(obj, type);
}
@Override
public Object convertObject(Object obj, Class<?> clazz) {
return com.alibaba.fastjson2.util.TypeUtils.cast(obj, clazz);
}
}

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.common.json.impl;
import java.lang.reflect.Type;
import java.util.List;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
public class FastJsonImpl extends AbstractJsonUtilImpl {
@ -37,4 +38,14 @@ public class FastJsonImpl extends AbstractJsonUtilImpl {
public String toJson(Object obj) {
return com.alibaba.fastjson.JSON.toJSONString(obj, SerializerFeature.DisableCircularReferenceDetect);
}
@Override
public Object convertObject(Object obj, Type type) {
return com.alibaba.fastjson.util.TypeUtils.cast(obj, type, ParserConfig.getGlobalInstance());
}
@Override
public Object convertObject(Object obj, Class<?> clazz) {
return com.alibaba.fastjson.util.TypeUtils.cast(obj, clazz, ParserConfig.getGlobalInstance());
}
}

View File

@ -42,6 +42,18 @@ public class GsonImpl extends AbstractJsonUtilImpl {
return getGson().toJson(obj);
}
@Override
public Object convertObject(Object obj, Type type) {
Gson gson = getGson();
return gson.fromJson(gson.toJsonTree(obj), type);
}
@Override
public Object convertObject(Object obj, Class<?> clazz) {
Gson gson = getGson();
return gson.fromJson(gson.toJsonTree(obj), clazz);
}
private Gson getGson() {
if (gsonCache == null || !(gsonCache instanceof Gson)) {
synchronized (this) {

View File

@ -59,6 +59,17 @@ public class JacksonImpl extends AbstractJsonUtilImpl {
}
}
@Override
public Object convertObject(Object obj, Type type) {
JsonMapper mapper = getJackson();
return mapper.convertValue(obj, mapper.constructType(type));
}
@Override
public Object convertObject(Object obj, Class<?> clazz) {
return getJackson().convertValue(obj, clazz);
}
private JsonMapper getJackson() {
if (jacksonCache == null || !(jacksonCache instanceof JsonMapper)) {
synchronized (this) {

View File

@ -76,4 +76,8 @@ public final class ArrayUtils {
public static <T> T[] of(T... values) {
return values;
}
public static <T> T first(T[] data) {
return isEmpty(data) ? null : data[0];
}
}

View File

@ -28,6 +28,12 @@ public abstract class Assert {
}
}
public static void notNull(Object obj, String format, Object... args) {
if (obj == null) {
throw new IllegalArgumentException(String.format(format, args));
}
}
public static void notEmptyString(String str, String message) {
if (StringUtils.isEmpty(str)) {
throw new IllegalArgumentException(message);

View File

@ -327,6 +327,10 @@ public class ClassUtils {
return type != null && (type.isPrimitive() || isSimpleType(type));
}
public static boolean isPrimitiveWrapper(Class<?> type) {
return PRIMITIVE_WRAPPER_TYPE_MAP.containsKey(type);
}
/**
* The specified type is simple type or not
*

View File

@ -24,11 +24,14 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import static java.util.Collections.emptySet;
@ -404,13 +407,19 @@ public class CollectionUtils {
return null;
}
if (values instanceof List) {
List<T> list = (List<T>) values;
return list.get(0);
return ((List<T>) values).get(0);
} else {
return values.iterator().next();
}
}
public static <T> T first(List<T> values) {
if (isEmpty(values)) {
return null;
}
return values.get(0);
}
public static <T> Set<T> toTreeSet(Set<T> set) {
if (isEmpty(set)) {
return set;
@ -420,4 +429,78 @@ public class CollectionUtils {
}
return set;
}
public static <T> Set<T> newHashSet(int expectedSize) {
return new HashSet<>(capacity(expectedSize));
}
public static <T> Set<T> newLinkedHashSet(int expectedSize) {
return new LinkedHashSet<>(capacity(expectedSize));
}
public static <T, K> Map<K, T> newHashMap(int expectedSize) {
return new HashMap<>(capacity(expectedSize));
}
public static <T, K> Map<K, T> newLinkedHashMap(int expectedSize) {
return new LinkedHashMap<>(capacity(expectedSize));
}
public static <T, K> Map<K, T> newConcurrentHashMap(int expectedSize) {
if (JRE.JAVA_8.isCurrentVersion()) {
return new SafeConcurrentHashMap<>(capacity(expectedSize));
}
return new ConcurrentHashMap<>(capacity(expectedSize));
}
public static <T, K> Map<K, T> newConcurrentHashMap() {
if (JRE.JAVA_8.isCurrentVersion()) {
return new SafeConcurrentHashMap<>();
}
return new ConcurrentHashMap<>();
}
public static int capacity(int expectedSize) {
if (expectedSize < 3) {
if (expectedSize < 0) {
throw new IllegalArgumentException("expectedSize cannot be negative but was: " + expectedSize);
}
return expectedSize + 1;
}
if (expectedSize < 1 << (Integer.SIZE - 2)) {
return (int) (expectedSize / 0.75F + 1.0F);
}
return Integer.MAX_VALUE;
}
public static class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
private static final long serialVersionUID = 1L;
public SafeConcurrentHashMap() {}
public SafeConcurrentHashMap(int initialCapacity) {
super(initialCapacity);
}
public SafeConcurrentHashMap(Map<? extends K, ? extends V> m) {
super(m);
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
V value = get(key);
if (value != null) {
return value;
}
value = mappingFunction.apply(key);
if (value == null) {
return null;
}
V exists = putIfAbsent(key, value);
if (exists != null) {
return exists;
}
return value;
}
}
}

View File

@ -0,0 +1,259 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.common.utils;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.CopyOnWriteArrayList;
public final class DateUtils {
public static final ZoneId GMT = ZoneId.of("GMT");
public static final ZoneId UTC = ZoneId.of("UTC");
public static final String DATE = "yyyy-MM-dd";
public static final String DATE_MIN = "yyyy-MM-dd HH:mm";
public static final String DATE_TIME = "yyyy-MM-dd HH:mm:ss";
public static final String JDK_TIME = "EEE MMM dd HH:mm:ss zzz yyyy";
public static final String ASC_TIME = "EEE MMM d HH:mm:ss yyyy";
public static final String RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz";
public static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern(DATE);
public static final DateTimeFormatter DATE_MIN_FORMAT = DateTimeFormatter.ofPattern(DATE_MIN);
public static final DateTimeFormatter DATE_TIME_FORMAT = DateTimeFormatter.ofPattern(DATE_TIME);
public static final DateTimeFormatter JDK_TIME_FORMAT = DateTimeFormatter.ofPattern(JDK_TIME, Locale.US);
public static final DateTimeFormatter ASC_TIME_FORMAT = DateTimeFormatter.ofPattern(ASC_TIME, Locale.US);
public static final DateTimeFormatter RFC1036_FORMAT = DateTimeFormatter.ofPattern(RFC1036, Locale.US);
private static final Map<String, DateTimeFormatter> CACHE = new LRUCache<>(64);
private static final List<DateTimeFormatter> CUSTOM_FORMATTERS = new CopyOnWriteArrayList<>();
private DateUtils() {}
public static void registerFormatter(String pattern) {
CUSTOM_FORMATTERS.add(DateTimeFormatter.ofPattern(pattern));
}
public static void registerFormatter(DateTimeFormatter formatter) {
CUSTOM_FORMATTERS.add(formatter);
}
public static Date parse(String str, String pattern) {
if (DATE_TIME.equals(pattern)) {
return parse(str, DATE_TIME_FORMAT);
}
DateTimeFormatter formatter = getFormatter(pattern);
return parse(str, formatter);
}
public static Date parse(String str, DateTimeFormatter formatter) {
return toDate(formatter.parse(str));
}
public static String format(Date date) {
return format(date, DATE_TIME_FORMAT);
}
public static String format(Date date, String pattern) {
if (DATE_TIME.equals(pattern)) {
return format(date, DATE_TIME_FORMAT);
}
DateTimeFormatter formatter = getFormatter(pattern);
return format(date, formatter);
}
public static String format(Date date, DateTimeFormatter formatter) {
return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()));
}
public static String format(Date date, DateTimeFormatter formatter, ZoneId zone) {
return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), zone));
}
public static String formatGMT(Date date, DateTimeFormatter formatter) {
return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), GMT));
}
public static String formatUTC(Date date, DateTimeFormatter formatter) {
return formatter.format(ZonedDateTime.ofInstant(date.toInstant(), UTC));
}
public static String formatHeader(Date date) {
return DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.ofInstant(date.toInstant(), GMT));
}
private static DateTimeFormatter getFormatter(String pattern) {
return CACHE.computeIfAbsent(pattern, DateTimeFormatter::ofPattern);
}
public static Date parse(Object value) {
if (value == null) {
return null;
}
if (value instanceof Date) {
return (Date) value;
}
if (value instanceof Calendar) {
return ((Calendar) value).getTime();
}
if (value.getClass() == Instant.class) {
return Date.from((Instant) value);
}
if (value instanceof TemporalAccessor) {
return Date.from(Instant.from((TemporalAccessor) value));
}
if (value instanceof Number) {
return new Date(((Number) value).longValue());
}
if (value instanceof CharSequence) {
return parse(value.toString());
}
throw new IllegalArgumentException("Can not cast to Date, value : '" + value + "'");
}
public static Date parse(String value) {
if (value == null) {
return null;
}
String str = value.trim();
int len = str.length();
if (len == 0) {
return null;
}
boolean isIso = true;
boolean isNumeric = true;
boolean hasDate = false;
boolean hasTime = false;
for (int i = 0; i < len; i++) {
char c = str.charAt(i);
switch (c) {
case ' ':
isIso = false;
break;
case '-':
hasDate = true;
break;
case 'T':
case ':':
hasTime = true;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
continue;
default:
}
if (isNumeric) {
isNumeric = false;
}
}
DateTimeFormatter formatter = null;
if (isIso) {
if (hasDate) {
formatter = hasTime ? DateTimeFormatter.ISO_DATE_TIME : DateTimeFormatter.ISO_DATE;
} else if (hasTime) {
formatter = DateTimeFormatter.ISO_TIME;
}
}
if (isNumeric) {
long num = Long.parseLong(str);
if (num > 21000101 || num < 19700101) {
return new Date(num);
}
formatter = DateTimeFormatter.BASIC_ISO_DATE;
}
switch (len) {
case 10:
formatter = DATE_FORMAT;
break;
case 16:
formatter = DATE_MIN_FORMAT;
break;
case 19:
formatter = DATE_TIME_FORMAT;
break;
case 23:
case 24:
formatter = ASC_TIME_FORMAT;
break;
case 27:
formatter = RFC1036_FORMAT;
break;
case 28:
formatter = JDK_TIME_FORMAT;
break;
case 29:
formatter = DateTimeFormatter.RFC_1123_DATE_TIME;
break;
default:
}
if (formatter != null) {
try {
return toDate(formatter.parse(str));
} catch (Exception ignored) {
}
}
for (DateTimeFormatter dtf : CUSTOM_FORMATTERS) {
try {
return parse(str, dtf);
} catch (Exception ignored) {
}
}
throw new IllegalArgumentException("Can not cast to Date, value : '" + value + "'");
}
public static Date toDate(TemporalAccessor temporal) {
if (temporal instanceof Instant) {
return Date.from((Instant) temporal);
}
long timestamp;
if (temporal.isSupported(ChronoField.EPOCH_DAY)) {
timestamp = temporal.getLong(ChronoField.EPOCH_DAY) * 86400000;
} else {
timestamp = LocalDate.now().toEpochDay() * 86400000;
}
if (temporal.isSupported(ChronoField.MILLI_OF_DAY)) {
timestamp += temporal.getLong(ChronoField.MILLI_OF_DAY);
}
if (temporal.isSupported(ChronoField.OFFSET_SECONDS)) {
timestamp -= temporal.getLong(ChronoField.OFFSET_SECONDS) * 1000;
} else {
timestamp -= TimeZone.getDefault().getRawOffset();
}
return new Date(timestamp);
}
}

View File

@ -53,6 +53,7 @@ public class JsonUtils {
case "jackson":
instance = new JacksonImpl();
break;
default:
}
if (instance != null && instance.isSupport()) {
jsonUtil = instance;
@ -123,6 +124,14 @@ public class JsonUtils {
return getJson().getObject(obj, key);
}
public static Object convertObject(Object obj, Type targetType) {
return getJson().convertObject(obj, targetType);
}
public static Object convertObject(Object obj, Class<?> targetType) {
return getJson().convertObject(obj, targetType);
}
public static Double getNumberAsDouble(Map<String, ?> obj, String key) {
return getJson().getNumberAsDouble(obj, key);
}

View File

@ -19,13 +19,14 @@ package org.apache.dubbo.common.utils;
import java.util.LinkedHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
/**
* LRU-2
* </p>
* <p>
* When the data accessed for the first time, add it to history list. If the size of history list reaches max capacity, eliminate the earliest data (first in first out).
* When the data already exists in the history list, and be accessed for the second time, then it will be put into cache.
*
* </p>
* TODO, consider replacing with ConcurrentHashMap to improve performance under concurrency
*/
public class LRU2Cache<K, V> extends LinkedHashMap<K, V> {
@ -33,8 +34,8 @@ public class LRU2Cache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = -5167631809472116969L;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private static final int DEFAULT_MAX_CAPACITY = 1000;
private final Lock lock = new ReentrantLock();
private volatile int maxCapacity;
@ -94,6 +95,16 @@ public class LRU2Cache<K, V> extends LinkedHashMap<K, V> {
}
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> fn) {
V value = get(key);
if (value == null) {
value = fn.apply(key);
put(key, value);
}
return value;
}
@Override
public V remove(Object key) {
lock.lock();

View File

@ -19,6 +19,7 @@ package org.apache.dubbo.common.utils;
import java.util.LinkedHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
/**
* A 'least recently used' cache based on LinkedHashMap.
@ -31,8 +32,8 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private static final long serialVersionUID = -5167631809472116969L;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private static final int DEFAULT_MAX_CAPACITY = 1000;
private final Lock lock = new ReentrantLock();
private volatile int maxCapacity;
@ -110,6 +111,20 @@ public class LRUCache<K, V> extends LinkedHashMap<K, V> {
}
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> fn) {
V value = get(key);
if (value == null) {
lock.lock();
try {
return super.computeIfAbsent(key, fn);
} finally {
lock.unlock();
}
}
return value;
}
public void lock() {
lock.lock();
}

View File

@ -0,0 +1,138 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.common.utils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
public final class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable {
private static final long serialVersionUID = 1L;
@SuppressWarnings("rawtypes")
private static final Pair NULL = new Pair<>(null, null);
public final L left;
public final R right;
public static <L, R> Pair<L, R> of(L left, R right) {
return left == null && right == null ? nullPair() : new Pair<>(left, right);
}
@SuppressWarnings("unchecked")
public static <L, R> Pair<L, R> nullPair() {
return NULL;
}
@SafeVarargs
public static <L, R> Map<L, R> toMap(Pair<L, R>... pairs) {
if (pairs == null) {
return Collections.emptyMap();
}
return toMap(Arrays.asList(pairs));
}
public static <L, R> Map<L, R> toMap(Collection<Pair<L, R>> pairs) {
if (pairs == null) {
return Collections.emptyMap();
}
Map<L, R> map = CollectionUtils.newLinkedHashMap(pairs.size());
for (Pair<L, R> pair : pairs) {
map.put(pair.getLeft(), pair.getRight());
}
return map;
}
public static <L, R> List<Pair<L, R>> toPairs(Map<L, R> map) {
if (map == null) {
return Collections.emptyList();
}
List<Pair<L, R>> pairs = new ArrayList<>(map.size());
for (Map.Entry<L, R> entry : map.entrySet()) {
pairs.add(of(entry.getKey(), entry.getValue()));
}
return pairs;
}
public Pair(L left, R right) {
this.left = left;
this.right = right;
}
public L getLeft() {
return left;
}
public R getRight() {
return right;
}
public boolean isNull() {
return this == NULL || left == null && right == null;
}
@Override
public L getKey() {
return left;
}
@Override
public R getValue() {
return right;
}
@Override
public R setValue(R value) {
throw new UnsupportedOperationException();
}
@Override
@SuppressWarnings("unchecked")
public int compareTo(Pair<L, R> other) {
return left.equals(other.left)
? ((Comparable<R>) right).compareTo(other.right)
: ((Comparable<L>) left).compareTo(other.left);
}
@Override
public int hashCode() {
return Objects.hashCode(left) ^ Objects.hashCode(right);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof Map.Entry) {
Map.Entry<?, ?> that = (Map.Entry<?, ?>) other;
return Objects.equals(left, that.getKey()) && Objects.equals(right, that.getValue());
}
return false;
}
@Override
public String toString() {
return "(" + left + ", " + right + ')';
}
}

View File

@ -1256,10 +1256,6 @@ public final class StringUtils {
/**
* Test str whether starts with the prefix ignore case.
*
* @param str
* @param prefix
* @return
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
if (str == null || prefix == null || str.length() < prefix.length()) {
@ -1268,4 +1264,102 @@ public final class StringUtils {
// return str.substring(0, prefix.length()).equalsIgnoreCase(prefix);
return str.regionMatches(true, 0, prefix, 0, prefix.length());
}
public static String defaultIf(String str, String defaultStr) {
return isEmpty(str) ? defaultStr : str;
}
/**
* Returns a substring from 'str' between 'start' and 'end', or to the end if 'end' is -1
*/
public static String substring(String str, int start, int end) {
if (str == null) {
return null;
}
return end == INDEX_NOT_FOUND ? str.substring(start) : str.substring(start, end);
}
/**
* Extracts a substring from the given string that precedes the first occurrence of the specified character separator.
* If the character is not found, the entire string is returned.
*/
public static String substringBefore(String str, int separator) {
if (isEmpty(str)) {
return str;
}
int index = str.indexOf(separator);
return index == INDEX_NOT_FOUND ? str : str.substring(0, index);
}
/**
* Extracts a substring from the given string that precedes the first occurrence of the specified string separator.
* If the separator is not found or is null, the entire string is returned.
*/
public static String substringBefore(String str, String separator) {
if (isEmpty(str) || separator == null) {
return str;
}
if (separator.isEmpty()) {
return EMPTY_STRING;
}
int index = str.indexOf(separator);
return index == INDEX_NOT_FOUND ? str : str.substring(0, index);
}
/**
* Tokenize the given String into a String array.
* Trims tokens and omits empty tokens.
*/
public static String[] tokenize(String str, char... separators) {
if (isEmpty(str)) {
return EMPTY_STRING_ARRAY;
}
return tokenizeToList(str, separators).toArray(EMPTY_STRING_ARRAY);
}
public static List<String> tokenizeToList(String str, char... separators) {
if (isEmpty(str)) {
return Collections.emptyList();
}
if (separators == null || separators.length == 0) {
separators = new char[] {','};
}
List<String> tokens = new ArrayList<>();
int start = -1, end = 0;
int i = 0;
out:
for (int len = str.length(), sLen = separators.length; i < len; i++) {
char c = str.charAt(i);
for (int j = 0; j < sLen; j++) {
if (c == separators[j]) {
if (start > -1) {
tokens.add(str.substring(start, end + 1));
start = -1;
}
continue out;
}
}
switch (c) {
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
if (start == -1) {
start = i;
}
end = i;
break;
}
}
if (start > -1) {
String part = str.substring(start, end + 1);
if (tokens.isEmpty()) {
return Collections.singletonList(part);
}
tokens.add(part);
}
return tokens;
}
}

View File

@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.config;
import java.io.Serializable;
/**
* Configuration for triple rest protocol.
*/
public class RestConfig implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Maximum allowed size for request bodies.
* Limits the size of request to prevent excessively large request.
* <p>The default value is 8MiB.
*/
private Integer maxBodySize;
/**
* Maximum allowed size for response bodies.
* Limits the size of responses to prevent excessively large response.
* <p>The default value is 8MiB.
*/
private Integer maxResponseBodySize;
/**
* Whether path matching should be match paths with a trailing slash.
* If enabled, a method mapped to "/users" also matches to "/users/".
* <p>The default value is {@code true}.
*/
private Boolean trailingSlashMatch;
/**
* Whether path matching should be case-sensitive.
* If enabled, a method mapped to "/users" won't match to "/Users/".
* <p>The default value is {@code false}.
*/
private Boolean caseSensitiveMatch;
/**
* Whether path matching uses suffix pattern matching (".*").
* If enabled, a method mapped to "/users" also matches to "/users.*".
* <p>This also enables suffix content negotiation, with the media-type
* inferred from the URL suffix, e.g., ".json" for "application/json".
* <p>The default value is {@code true}.
*/
private Boolean suffixPatternMatch;
/**
* The parameter name that can be used to specify the response format.
* <p>The default value is 'format'.
*/
private String formatParameterName;
public Integer getMaxBodySize() {
return maxBodySize;
}
public void setMaxBodySize(Integer maxBodySize) {
this.maxBodySize = maxBodySize;
}
public Integer getMaxResponseBodySize() {
return maxResponseBodySize;
}
public void setMaxResponseBodySize(Integer maxResponseBodySize) {
this.maxResponseBodySize = maxResponseBodySize;
}
public Boolean getTrailingSlashMatch() {
return trailingSlashMatch;
}
public void setTrailingSlashMatch(Boolean trailingSlashMatch) {
this.trailingSlashMatch = trailingSlashMatch;
}
public Boolean getCaseSensitiveMatch() {
return caseSensitiveMatch;
}
public void setCaseSensitiveMatch(Boolean caseSensitiveMatch) {
this.caseSensitiveMatch = caseSensitiveMatch;
}
public Boolean getSuffixPatternMatch() {
return suffixPatternMatch;
}
public void setSuffixPatternMatch(Boolean suffixPatternMatch) {
this.suffixPatternMatch = suffixPatternMatch;
}
public String getFormatParameterName() {
return formatParameterName;
}
public void setFormatParameterName(String formatParameterName) {
this.formatParameterName = formatParameterName;
}
}

View File

@ -55,6 +55,11 @@ public class TripleConfig implements Serializable {
*/
private String maxHeaderListSize;
/**
* Whether to pass through standard HTTP headers, default is false.
*/
private Boolean passThroughStandardHttpHeaders;
public String getHeaderTableSize() {
return headerTableSize;
}
@ -102,4 +107,12 @@ public class TripleConfig implements Serializable {
public void setMaxHeaderListSize(String maxHeaderListSize) {
this.maxHeaderListSize = maxHeaderListSize;
}
public Boolean getPassThroughStandardHttpHeaders() {
return passThroughStandardHttpHeaders;
}
public void setPassThroughStandardHttpHeaders(Boolean passThroughStandardHttpHeaders) {
this.passThroughStandardHttpHeaders = passThroughStandardHttpHeaders;
}
}

View File

@ -1218,6 +1218,7 @@ public class DefaultApplicationDeployer extends AbstractDeployer<ApplicationMode
// cannot change to pending from other state
// setPending();
break;
default:
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -284,6 +284,28 @@
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-jaxrs</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-servlet</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-spring</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- registry -->
<dependency>
<groupId>org.apache.dubbo</groupId>
@ -448,6 +470,14 @@
</dependency>
<!-- Transitive dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
@ -544,6 +574,9 @@
<include>org.apache.dubbo:dubbo-rpc-injvm</include>
<include>org.apache.dubbo:dubbo-rpc-rest</include>
<include>org.apache.dubbo:dubbo-rpc-triple</include>
<include>org.apache.dubbo:dubbo-rest-jaxrs</include>
<include>org.apache.dubbo:dubbo-rest-servlet</include>
<include>org.apache.dubbo:dubbo-rest-spring</include>
<include>org.apache.dubbo:dubbo-serialization-api</include>
<include>org.apache.dubbo:dubbo-serialization-hessian2</include>
<include>org.apache.dubbo:dubbo-serialization-fastjson2</include>
@ -913,6 +946,27 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.compressor.DeCompressor</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentConverter</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/dubbo/internal/org.apache.dubbo.metrics.service.MetricsService</resource>
</transformer>
@ -980,7 +1034,7 @@
<filter>
<artifact>org.apache.dubbo:dubbo</artifact>
<excludes>
<!-- These following two line is optional, it can remove some warn log -->
<!-- These following two line are optional, it can remove some warning log -->
<exclude>com/**</exclude>
<exclude>org/**</exclude>
<!-- This one is required -->

View File

@ -334,6 +334,21 @@
<artifactId>dubbo-plugin-proxy-bytebuddy</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-jaxrs</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-servlet</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-spring</artifactId>
<version>${project.version}</version>
</dependency>
<!-- registry -->
<dependency>

View File

@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-plugin</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-rest-jaxrs</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-rest</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import java.lang.annotation.Annotation;
public abstract class AbstractJaxrsArgumentResolver extends AbstractAnnotationBaseArgumentResolver {
@Override
protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta<Annotation> ann) {
return new NamedValueMeta(ann.getValue(), Helper.isRequired(param), Helper.defaultValue(param));
}
}

View File

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationEnum;
import java.lang.annotation.Annotation;
public enum Annotations implements AnnotationEnum {
Path,
HttpMethod,
Produces,
Consumes,
PathParam,
MatrixParam,
QueryParam,
HeaderParam,
CookieParam,
FormParam,
BeanParam,
Body("org.jboss.resteasy.annotations.Body"),
Form("org.jboss.resteasy.annotations.Form"),
Context("javax.ws.rs.core.Context"),
Suspended("javax.ws.rs.container.Suspended"),
DefaultValue,
Encoded,
Nonnull("javax.annotation.Nonnull");
private final String className;
private Class<Annotation> type;
Annotations(String className) {
this.className = className;
}
Annotations() {
className = "javax.ws.rs." + name();
}
public String className() {
return className;
}
@Override
public Class<Annotation> type() {
if (type == null) {
type = loadType();
}
return type;
}
}

View File

@ -0,0 +1,280 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.Pair;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
final class BeanArgumentBinder {
private final ArgumentResolver argumentResolver;
private final Map<Pair<Class<?>, String>, BeanMeta> cache = CollectionUtils.newConcurrentHashMap();
BeanArgumentBinder(FrameworkModel frameworkModel) {
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
argumentResolver = beanFactory.getOrRegisterBean(CompositeArgumentResolver.class);
}
public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
try {
return resolveArgument(parameter, request, response);
} catch (Exception e) {
throw new RestException(Messages.ARGUMENT_BIND_ERROR, parameter.getName(), parameter.getType(), e);
}
}
private Object resolveArgument(ParameterMeta param, HttpRequest request, HttpResponse response) throws Exception {
AnnotationMeta<?> form = param.findAnnotation(Annotations.Form);
if (form != null || param.isHierarchyAnnotated(Annotations.BeanParam)) {
String prefix = form == null ? null : form.getString("prefix");
BeanMeta beanMeta = cache.computeIfAbsent(
Pair.of(param.getActualType(), prefix),
k -> new BeanMeta(param.getToolKit(), k.getValue(), k.getKey()));
ConstructorMeta constructor = beanMeta.constructor;
ParameterMeta[] parameters = constructor.parameters;
int len = parameters.length;
Object[] args = new Object[len];
for (int i = 0; i < len; i++) {
args[i] = resolveArgument(parameters[i], request, response);
}
Object bean = constructor.newInstance(args);
for (FieldMeta fieldMeta : beanMeta.fields) {
fieldMeta.set(bean, resolveArgument(fieldMeta, request, response));
}
for (SetMethodMeta methodMeta : beanMeta.methods) {
methodMeta.invoke(bean, resolveArgument(methodMeta, request, response));
}
return bean;
}
return argumentResolver.resolve(param, request, response);
}
private static class BeanMeta {
private final List<FieldMeta> fields = new ArrayList<>();
private final List<SetMethodMeta> methods = new ArrayList<>();
private final ConstructorMeta constructor;
BeanMeta(RestToolKit toolKit, String prefix, Class<?> type) {
constructor = resolveConstructor(toolKit, prefix, type);
resolveFieldAndMethod(toolKit, prefix, type);
}
private ConstructorMeta resolveConstructor(RestToolKit toolKit, String prefix, Class<?> type) {
Constructor<?>[] constructors = type.getConstructors();
Constructor<?> ct = null;
if (constructors.length == 1) {
ct = constructors[0];
} else {
try {
ct = type.getDeclaredConstructor();
} catch (NoSuchMethodException ignored) {
}
}
if (ct == null) {
throw new IllegalArgumentException("No available default constructor found in " + type);
}
return new ConstructorMeta(toolKit, prefix, ct);
}
private void resolveFieldAndMethod(RestToolKit toolKit, String prefix, Class<?> type) {
if (type == Object.class) {
return;
}
for (Field field : type.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
continue;
}
if (field.getAnnotations().length == 0) {
continue;
}
if (!field.isAccessible()) {
field.setAccessible(true);
}
fields.add(new FieldMeta(toolKit, prefix, field));
}
for (Method method : type.getDeclaredMethods()) {
if (method.getParameterCount() != 1) {
continue;
}
int modifiers = method.getModifiers();
if ((modifiers & (Modifier.PUBLIC | Modifier.ABSTRACT | Modifier.STATIC)) == Modifier.PUBLIC) {
Parameter parameter = method.getParameters()[0];
String name = method.getName();
if (name.length() > 3 && name.startsWith("set")) {
name = Character.toLowerCase(name.charAt(3)) + name.substring(4);
methods.add(new SetMethodMeta(toolKit, method, parameter, prefix, name));
}
}
}
resolveFieldAndMethod(toolKit, prefix, type.getSuperclass());
}
}
private static final class ConstructorMeta {
private final Constructor<?> constructor;
private final ConstructorParameterMeta[] parameters;
ConstructorMeta(RestToolKit toolKit, String prefix, Constructor<?> constructor) {
this.constructor = constructor;
parameters = initParameters(toolKit, prefix, constructor);
}
private ConstructorParameterMeta[] initParameters(RestToolKit toolKit, String prefix, Constructor<?> ct) {
Parameter[] cps = ct.getParameters();
int len = cps.length;
ConstructorParameterMeta[] parameters = new ConstructorParameterMeta[len];
for (int i = 0; i < len; i++) {
parameters[i] = new ConstructorParameterMeta(toolKit, cps[i], prefix);
}
return parameters;
}
Object newInstance(Object[] args) throws Exception {
return constructor.newInstance(args);
}
}
public static final class ConstructorParameterMeta extends ParameterMeta {
private final Parameter parameter;
ConstructorParameterMeta(RestToolKit toolKit, Parameter parameter, String prefix) {
super(toolKit, prefix, parameter.isNamePresent() ? parameter.getName() : null);
this.parameter = parameter;
}
@Override
protected AnnotatedElement getAnnotatedElement() {
return parameter;
}
@Override
public Class<?> getType() {
return parameter.getType();
}
@Override
public Type getGenericType() {
return parameter.getParameterizedType();
}
@Override
public String getDescription() {
return "ConstructorParameter{" + parameter + '}';
}
}
private static final class FieldMeta extends ParameterMeta {
private final Field field;
FieldMeta(RestToolKit toolKit, String prefix, Field field) {
super(toolKit, prefix, field.getName());
this.field = field;
}
@Override
public Class<?> getType() {
return field.getType();
}
@Override
public Type getGenericType() {
return field.getGenericType();
}
@Override
protected AnnotatedElement getAnnotatedElement() {
return field;
}
public void set(Object bean, Object value) throws Exception {
field.set(bean, value);
}
@Override
public String getDescription() {
return "FieldParameter{" + field + '}';
}
}
private static final class SetMethodMeta extends ParameterMeta {
private final Method method;
private final Parameter parameter;
SetMethodMeta(RestToolKit toolKit, Method method, Parameter parameter, String prefix, String name) {
super(toolKit, prefix, name);
this.method = method;
this.parameter = parameter;
}
@Override
public Class<?> getType() {
return parameter.getType();
}
@Override
public Type getGenericType() {
return parameter.getParameterizedType();
}
@Override
protected AnnotatedElement getAnnotatedElement() {
return parameter;
}
public void invoke(Object bean, Object value) throws Exception {
method.invoke(bean, value);
}
@Override
public String getDescription() {
return "SetMethodParameter{" + method + '}';
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import java.lang.annotation.Annotation;
@Activate(onClass = "javax.ws.rs.BeanParam")
public class BeanParamArgumentResolver implements AnnotationBaseArgumentResolver<Annotation> {
@Override
public Class<Annotation> accept() {
return Annotations.BeanParam.type();
}
@Override
public Object resolve(
ParameterMeta parameter,
AnnotationMeta<Annotation> annotation,
HttpRequest request,
HttpResponse response) {
return parameter.bind(request, response);
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.io.StreamUtils;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import java.io.IOException;
import java.lang.annotation.Annotation;
@Activate(onClass = "org.jboss.resteasy.annotations.Body")
public class BodyArgumentResolver implements AnnotationBaseArgumentResolver<Annotation> {
@Override
public Class<Annotation> accept() {
return Annotations.Body.type();
}
@Override
public Object resolve(
ParameterMeta parameter,
AnnotationMeta<Annotation> annotation,
HttpRequest request,
HttpResponse response) {
Class<?> type = parameter.getActualType();
if (type == byte[].class) {
try {
return StreamUtils.readBytes(request.inputStream());
} catch (IOException e) {
throw new RestException(e);
}
}
return RequestUtils.decodeBody(request, type);
}
}

View File

@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Activate(onClass = "javax.ws.rs.CookieParam")
public class CookieParamArgumentResolver extends AbstractJaxrsArgumentResolver {
@Override
public Class<Annotation> accept() {
return Annotations.CookieParam.type();
}
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.cookie(meta.name());
}
@Override
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Collection<HttpCookie> cookies = request.cookies();
if (cookies.isEmpty()) {
return Collections.emptyList();
}
String name = meta.name();
List<HttpCookie> result = new ArrayList<>(cookies.size());
for (HttpCookie cookie : cookies) {
if (name.equals(cookie.name())) {
result.add(cookie);
}
}
return result;
}
@Override
protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Collection<HttpCookie> cookies = request.cookies();
if (cookies.isEmpty()) {
return Collections.emptyMap();
}
Map<String, List<HttpCookie>> mapValue = CollectionUtils.newLinkedHashMap(cookies.size());
for (HttpCookie cookie : cookies) {
mapValue.computeIfAbsent(cookie.name(), k -> new ArrayList<>()).add(cookie);
}
return mapValue;
}
}

View File

@ -0,0 +1,89 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.io.StreamUtils;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
import java.io.IOException;
@Activate(order = Integer.MAX_VALUE - 10000, onClass = "javax.ws.rs.Path")
public class FallbackArgumentResolver extends AbstractArgumentResolver {
@Override
public boolean accept(ParameterMeta param) {
return param.getToolKit().getDialect() == RestConstants.DIALECT_JAXRS;
}
@Override
protected NamedValueMeta createNamedValueMeta(ParameterMeta param) {
return new NamedValueMeta(param.isAnnotated(Annotations.Nonnull), Helper.defaultValue(param));
}
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Object value = RequestUtils.decodeBody(request, meta.type());
if (value != null) {
return value;
}
if (meta.parameterMeta().isSimple()) {
return request.parameter(meta.name());
}
return null;
}
@Override
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Class<?> type = meta.type();
if (type == byte[].class) {
try {
return StreamUtils.readBytes(request.inputStream());
} catch (IOException e) {
throw new RestException(e);
}
}
Object value = RequestUtils.decodeBody(request, meta.type());
if (value != null) {
return value;
}
if (TypeUtils.isSimpleProperty(meta.nestedType(0))) {
return request.parameterValues(meta.name());
}
return null;
}
@Override
protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Object value = RequestUtils.decodeBody(request, meta.type());
if (value != null) {
return value;
}
if (TypeUtils.isSimpleProperty(meta.nestedType(1))) {
return RequestUtils.getParametersMap(request);
}
return null;
}
}

View File

@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import java.lang.annotation.Annotation;
@Activate(onClass = "org.jboss.resteasy.annotations.Form")
public class FormArgumentResolver implements AnnotationBaseArgumentResolver<Annotation> {
@Override
public Class<Annotation> accept() {
return Annotations.Form.type();
}
@Override
public Object resolve(
ParameterMeta parameter,
AnnotationMeta<Annotation> annotation,
HttpRequest request,
HttpResponse response) {
return parameter.bind(request, response);
}
}

View File

@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import java.lang.annotation.Annotation;
/**
* TODO: support nested values: (e.g., 'telephoneNumbers[0].countryCode' 'address[INVOICE].street')
*/
@Activate(onClass = "javax.ws.rs.FormParam")
public class FormParamArgumentResolver extends AbstractJaxrsArgumentResolver {
@Override
public Class<Annotation> accept() {
return Annotations.FormParam.type();
}
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return CollectionUtils.first(request.formParameterValues(getFullName(meta)));
}
@Override
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.formParameterValues(getFullName(meta));
}
private String getFullName(NamedValueMeta meta) {
String prefix = meta.parameterMeta().getPrefix();
return prefix == null ? meta.name() : prefix + '.' + meta.name();
}
}

View File

@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import java.lang.annotation.Annotation;
@Activate(onClass = "javax.ws.rs.HeaderParam")
public class HeaderParamArgumentResolver extends AbstractJaxrsArgumentResolver {
@Override
public Class<Annotation> accept() {
return Annotations.HeaderParam.type();
}
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.header(meta.name());
}
@Override
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.headerValues(meta.name());
}
@Override
protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.headers();
}
}

View File

@ -0,0 +1,85 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpResult;
import org.apache.dubbo.remoting.http12.HttpUtils;
import org.apache.dubbo.remoting.http12.message.DefaultHttpResult.Builder;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.stream.Collectors;
public final class Helper {
private Helper() {}
public static boolean isRequired(ParameterMeta parameter) {
return parameter.isAnnotated(Annotations.Nonnull);
}
public static String defaultValue(ParameterMeta annotation) {
AnnotationMeta<?> meta = annotation.getAnnotation(Annotations.DefaultValue);
return meta == null ? null : meta.getValue();
}
public static HttpResult<Object> toBody(Response response) {
Builder<Object> builder = HttpResult.builder().status(response.getStatus());
if (response.hasEntity()) {
builder.body(response.getEntity());
}
builder.headers(response.getStringHeaders());
return builder.build();
}
public static MediaType toMediaType(String mediaType) {
if (mediaType == null) {
return null;
}
int index = mediaType.indexOf('/');
if (index == -1) {
return null;
}
return new MediaType(mediaType.substring(0, index), mediaType.substring(index + 1));
}
public static String toString(MediaType mediaType) {
return mediaType.getType() + '/' + mediaType.getSubtype();
}
public static List<MediaType> toMediaTypes(String accept) {
return HttpUtils.parseAccept(accept).stream().map(Helper::toMediaType).collect(Collectors.toList());
}
public static NewCookie convert(HttpCookie cookie) {
return new NewCookie(
cookie.name(),
cookie.value(),
cookie.path(),
cookie.domain(),
null,
(int) cookie.maxAge(),
cookie.secure(),
cookie.httpOnly());
}
}

View File

@ -0,0 +1,163 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.io.InputStream;
import java.net.URI;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
import org.jboss.resteasy.spi.ResteasyAsynchronousContext;
import org.jboss.resteasy.spi.ResteasyUriInfo;
public final class JaxrsHttpRequestAdaptee implements org.jboss.resteasy.spi.HttpRequest {
private final HttpRequest request;
private HttpHeaders headers;
private ResteasyUriInfo uriInfo;
public JaxrsHttpRequestAdaptee(HttpRequest request) {
this.request = request;
}
@Override
public HttpHeaders getHttpHeaders() {
HttpHeaders headers = this.headers;
if (headers == null) {
headers = new ResteasyHttpHeaders(new MultivaluedMapWrapper<>(request.headers()));
this.headers = headers;
}
return headers;
}
@Override
public MultivaluedMap<String, String> getMutableHeaders() {
return headers.getRequestHeaders();
}
@Override
public InputStream getInputStream() {
return request.inputStream();
}
@Override
public void setInputStream(InputStream stream) {
request.setInputStream(stream);
}
@Override
public ResteasyUriInfo getUri() {
ResteasyUriInfo uriInfo = this.uriInfo;
if (uriInfo == null) {
uriInfo = new ResteasyUriInfo(request.rawPath(), request.query(), "/");
this.uriInfo = uriInfo;
}
return uriInfo;
}
@Override
public String getHttpMethod() {
return request.method();
}
@Override
public void setHttpMethod(String method) {
request.setMethod(method);
}
@Override
public void setRequestUri(URI requestUri) throws IllegalStateException {
String query = requestUri.getRawQuery();
request.setUri(requestUri.getRawPath() + (query == null ? "" : '?' + query));
}
@Override
public void setRequestUri(URI baseUri, URI requestUri) throws IllegalStateException {
String query = requestUri.getRawQuery();
request.setUri(baseUri.getRawPath() + requestUri.getRawPath() + (query == null ? "" : '?' + query));
}
@Override
public MultivaluedMap<String, String> getFormParameters() {
MultivaluedMap<String, String> result = new MultivaluedHashMap<>();
for (String name : request.formParameterNames()) {
List<String> values = request.formParameterValues(name);
if (values == null) {
continue;
}
for (String value : values) {
result.add(name, RequestUtils.encodeURL(value));
}
}
return result;
}
@Override
public MultivaluedMap<String, String> getDecodedFormParameters() {
return new MultivaluedMapWrapper<>(RequestUtils.getFormParametersMap(request));
}
@Override
public Object getAttribute(String attribute) {
return request.attribute(attribute);
}
@Override
public void setAttribute(String name, Object value) {
request.setAttribute(name, value);
}
@Override
public void removeAttribute(String name) {
request.removeAttribute(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(request.attributeNames());
}
@Override
public ResteasyAsynchronousContext getAsyncContext() {
throw new UnsupportedOperationException();
}
@Override
public boolean isInitial() {
return false;
}
@Override
public void forward(String path) {
throw new UnsupportedOperationException();
}
@Override
public boolean wasForwarded() {
return false;
}
}

View File

@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpResponse;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import java.io.OutputStream;
public final class JaxrsHttpResponseAdaptee implements org.jboss.resteasy.spi.HttpResponse {
private final HttpResponse response;
private MultivaluedMap<String, Object> headers;
public JaxrsHttpResponseAdaptee(HttpResponse response) {
this.response = response;
}
@Override
public int getStatus() {
return response.status();
}
@Override
public void setStatus(int status) {
response.setStatus(status);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public MultivaluedMap<String, Object> getOutputHeaders() {
MultivaluedMap<String, Object> headers = this.headers;
if (headers == null) {
headers = new MultivaluedMapWrapper(response.headers());
this.headers = headers;
}
return headers;
}
@Override
public OutputStream getOutputStream() {
return response.outputStream();
}
@Override
public void setOutputStream(OutputStream os) {
response.setOutputStream(os);
}
@Override
public void addNewCookie(NewCookie cookie) {
HttpCookie hCookie = new HttpCookie(cookie.getName(), cookie.getValue());
hCookie.setDomain(cookie.getDomain());
hCookie.setPath(cookie.getPath());
hCookie.setMaxAge(cookie.getMaxAge());
hCookie.setSecure(cookie.isSecure());
hCookie.setHttpOnly(cookie.isHttpOnly());
response.addCookie(hCookie);
}
@Override
public void sendError(int status) {
response.sendError(status);
}
@Override
public void sendError(int status, String message) {
response.sendError(status, message);
}
@Override
public boolean isCommitted() {
return response.isCommitted();
}
@Override
public void reset() {
response.reset();
}
@Override
public void flushBuffer() {}
}

View File

@ -0,0 +1,83 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.jboss.resteasy.specimpl.ResteasyHttpHeaders;
import org.jboss.resteasy.spi.ResteasyUriInfo;
@Activate(onClass = "javax.ws.rs.Path")
public class JaxrsMiscArgumentResolver implements ArgumentResolver {
private static final Set<Class<?>> SUPPORTED_TYPES = new HashSet<>();
static {
SUPPORTED_TYPES.add(Cookie.class);
SUPPORTED_TYPES.add(Form.class);
SUPPORTED_TYPES.add(HttpHeaders.class);
SUPPORTED_TYPES.add(MediaType.class);
SUPPORTED_TYPES.add(MultivaluedMap.class);
SUPPORTED_TYPES.add(UriInfo.class);
}
@Override
public boolean accept(ParameterMeta parameter) {
return SUPPORTED_TYPES.contains(parameter.getActualType());
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public Object resolve(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
Class<?> type = parameter.getActualType();
if (Cookie.class.isAssignableFrom(type)) {
return Helper.convert(request.cookie(parameter.getRequiredName()));
}
if (Form.class.isAssignableFrom(type)) {
return RequestUtils.getFormParametersMap(request);
}
if (HttpHeaders.class.isAssignableFrom(type)) {
return new ResteasyHttpHeaders(new MultivaluedMapWrapper<>(request.headers()));
}
if (MediaType.class.isAssignableFrom(type)) {
return Helper.toMediaType(request.mediaType());
}
if (MultivaluedMap.class.isAssignableFrom(type)) {
return new MultivaluedMapWrapper<>((Map) RequestUtils.getParametersMap(request));
}
if (UriInfo.class.isAssignableFrom(type)) {
return new ResteasyUriInfo(request.rawPath(), request.query(), "/");
}
return null;
}
}

View File

@ -0,0 +1,93 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping.Builder;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ServiceVersionCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationSupport;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ServiceMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
@Activate(onClass = "javax.ws.rs.Path")
public class JaxrsRequestMappingResolver implements RequestMappingResolver {
private final RestToolKit toolKit;
public JaxrsRequestMappingResolver(FrameworkModel frameworkModel) {
toolKit = new JaxrsRestToolKit(frameworkModel);
}
@Override
public RestToolKit getRestToolKit() {
return toolKit;
}
@Override
public RequestMapping resolve(ServiceMeta serviceMeta) {
AnnotationMeta<?> path = serviceMeta.findAnnotation(Annotations.Path);
if (path == null) {
return null;
}
return builder(serviceMeta, path, null)
.name(serviceMeta.getType().getSimpleName())
.contextPath(serviceMeta.getContextPath())
.build();
}
@Override
public RequestMapping resolve(MethodMeta methodMeta) {
AnnotationMeta<?> path = methodMeta.findAnnotation(Annotations.Path);
if (path == null) {
return null;
}
AnnotationMeta<?> httpMethod = methodMeta.findMergedAnnotation(Annotations.HttpMethod);
if (httpMethod == null) {
return null;
}
ServiceMeta serviceMeta = methodMeta.getServiceMeta();
return builder(methodMeta, path, httpMethod)
.name(methodMeta.getMethod().getName())
.contextPath(methodMeta.getServiceMeta().getContextPath())
.custom(new ServiceVersionCondition(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()))
.build();
}
private Builder builder(AnnotationSupport meta, AnnotationMeta<?> path, AnnotationMeta<?> httpMethod) {
Builder builder = RequestMapping.builder().path(path.getValue());
if (httpMethod == null) {
httpMethod = meta.findMergedAnnotation(Annotations.HttpMethod);
}
if (httpMethod != null) {
builder.method(httpMethod.getValue());
}
AnnotationMeta<?> produces = meta.findAnnotation(Annotations.Produces);
if (produces != null) {
builder.produce(produces.getValueArray());
}
AnnotationMeta<?> consumes = meta.findAnnotation(Annotations.Consumes);
if (consumes != null) {
builder.consume(consumes.getValueArray());
}
return builder;
}
}

View File

@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener;
import javax.ws.rs.core.Response;
@Activate(order = -10000, onClass = "javax.ws.rs.Path")
public class JaxrsResponseRestFilter implements RestFilter, Listener {
@Override
public void onResponse(Result result, HttpRequest request, HttpResponse response) {
if (result.hasException()) {
return;
}
Object value = result.getValue();
if (value instanceof Response) {
result.setValue(Helper.toBody((Response) value));
}
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.DefaultRestToolKit;
final class JaxrsRestToolKit extends DefaultRestToolKit {
private final BeanArgumentBinder binder;
public JaxrsRestToolKit(FrameworkModel frameworkModel) {
super(frameworkModel);
binder = new BeanArgumentBinder(frameworkModel);
}
@Override
public int getDialect() {
return RestConstants.DIALECT_JAXRS;
}
@Override
public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
return binder.bind(parameter, request, response);
}
}

View File

@ -0,0 +1,53 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
@Activate(onClass = "javax.ws.rs.MatrixParam")
public class MatrixParamArgumentResolver extends AbstractJaxrsArgumentResolver {
@Override
public Class<Annotation> accept() {
return Annotations.MatrixParam.type();
}
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return CollectionUtils.first(doResolveCollectionValue(meta, request));
}
@Override
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return doResolveCollectionValue(meta, request);
}
private static List<String> doResolveCollectionValue(NamedValueMeta meta, HttpRequest request) {
Map<String, String> variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
return RequestUtils.parseMatrixVariableValues(variableMap, meta.name());
}
}

View File

@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.convert.Converter;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
@SuppressWarnings("rawtypes")
public class MultivaluedMapCreationConverter implements Converter<Integer, MultivaluedMap> {
@Override
public MultivaluedMap<?, ?> convert(Integer source) {
return new MultivaluedHashMap<>(source);
}
}

View File

@ -0,0 +1,29 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import javax.ws.rs.core.AbstractMultivaluedMap;
import java.util.List;
import java.util.Map;
public final class MultivaluedMapWrapper<K, V> extends AbstractMultivaluedMap<K, V> {
public MultivaluedMapWrapper(Map<K, List<V>> store) {
super(store);
}
}

View File

@ -0,0 +1,69 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.RestParameterException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import java.lang.annotation.Annotation;
import java.util.Map;
@Activate(onClass = "javax.ws.rs.PathParam")
public class PathParamArgumentResolver implements AnnotationBaseArgumentResolver<Annotation> {
@Override
public Class<Annotation> accept() {
return Annotations.PathParam.type();
}
@Override
public Object resolve(
ParameterMeta parameter,
AnnotationMeta<Annotation> annotation,
HttpRequest request,
HttpResponse response) {
Map<String, String> variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
String name = annotation.getValue();
if (StringUtils.isEmpty(name)) {
name = parameter.getRequiredName();
}
if (variableMap == null) {
if (Helper.isRequired(parameter)) {
throw new RestParameterException(Messages.ARGUMENT_VALUE_MISSING, name, parameter.getType());
}
return null;
}
String value = variableMap.get(name);
if (value == null) {
return null;
}
int index = value.indexOf(';');
if (index != -1) {
value = value.substring(0, index);
}
return parameter.isAnnotated(Annotations.Encoded) ? value : RequestUtils.decodeURL(value);
}
}

View File

@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import java.lang.annotation.Annotation;
@Activate(onClass = "javax.ws.rs.QueryParam")
public class QueryParamArgumentResolver extends AbstractJaxrsArgumentResolver {
@Override
public Class<Annotation> accept() {
return Annotations.QueryParam.type();
}
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.parameter(meta.name());
}
@Override
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.parameterValues(meta.name());
}
@Override
protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return RequestUtils.getParametersMap(request);
}
}

View File

@ -0,0 +1,214 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpRequestAdaptee;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsHttpResponseAdaptee;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.core.Cookie;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
import java.io.InputStream;
import java.net.URI;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.jboss.resteasy.specimpl.RequestImpl;
import org.jboss.resteasy.spi.ResteasyUriInfo;
final class ContainerRequestContextImpl implements ContainerRequestContext {
private final HttpRequest request;
private final HttpResponse response;
private Request req;
private MultivaluedMap<String, String> headers;
private UriInfo uriInfo;
private boolean aborted;
public ContainerRequestContextImpl(HttpRequest request, HttpResponse response) {
this.request = request;
this.response = response;
}
@Override
public Object getProperty(String name) {
return request.attribute(name);
}
@Override
public Collection<String> getPropertyNames() {
return request.parameterNames();
}
@Override
public void setProperty(String name, Object object) {
request.setAttribute(name, object);
}
@Override
public void removeProperty(String name) {
request.removeAttribute(name);
}
@Override
public UriInfo getUriInfo() {
UriInfo uriInfo = this.uriInfo;
if (uriInfo == null) {
uriInfo = new ResteasyUriInfo(request.rawPath(), request.query(), "/");
this.uriInfo = uriInfo;
}
return uriInfo;
}
@Override
public void setRequestUri(URI requestUri) {
String query = requestUri.getRawQuery();
request.setUri(requestUri.getRawPath() + (query == null ? "" : '?' + query));
}
@Override
public void setRequestUri(URI baseUri, URI requestUri) {
String query = requestUri.getRawQuery();
request.setUri(baseUri.getRawPath() + requestUri.getRawPath() + (query == null ? "" : '?' + query));
}
@Override
public Request getRequest() {
Request req = this.req;
if (req == null) {
req = new RequestImpl(new JaxrsHttpRequestAdaptee(request), new JaxrsHttpResponseAdaptee(response));
this.req = req;
}
return req;
}
@Override
public String getMethod() {
return request.method();
}
@Override
public void setMethod(String method) {
request.setMethod(method);
}
@Override
public MultivaluedMap<String, String> getHeaders() {
MultivaluedMap<String, String> headers = this.headers;
if (headers == null) {
headers = new MultivaluedMapWrapper<>(request.headers());
this.headers = headers;
}
return headers;
}
@Override
public String getHeaderString(String name) {
return request.header(name);
}
@Override
public Date getDate() {
return null;
}
@Override
public Locale getLanguage() {
return request.locale();
}
@Override
public int getLength() {
return request.contentLength();
}
@Override
public MediaType getMediaType() {
return Helper.toMediaType(request.mediaType());
}
@Override
public List<MediaType> getAcceptableMediaTypes() {
return Helper.toMediaTypes(request.accept());
}
@Override
public List<Locale> getAcceptableLanguages() {
return request.locales();
}
@Override
public Map<String, Cookie> getCookies() {
Collection<HttpCookie> cookies = request.cookies();
Map<String, Cookie> result = new HashMap<>(cookies.size());
for (HttpCookie cookie : cookies) {
result.put(cookie.name(), Helper.convert(cookie));
}
return result;
}
@Override
@SuppressWarnings("resource")
public boolean hasEntity() {
return request.inputStream() != null;
}
@Override
public InputStream getEntityStream() {
return request.inputStream();
}
@Override
public void setEntityStream(InputStream input) {
request.setInputStream(input);
}
@Override
public SecurityContext getSecurityContext() {
return null;
}
@Override
public void setSecurityContext(SecurityContext context) {}
@Override
public void abortWith(Response response) {
this.response.setBody(Helper.toBody(response));
aborted = true;
}
public boolean isAborted() {
return aborted;
}
}

View File

@ -0,0 +1,57 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
import javax.ws.rs.container.ContainerRequestFilter;
@Activate(onClass = "javax.ws.rs.container.ContainerResponseFilter")
public class ContainerRequestFilterAdapter implements RestExtensionAdapter<ContainerRequestFilter> {
@Override
public boolean accept(Object extension) {
return extension instanceof ContainerRequestFilter;
}
@Override
public RestFilter adapt(ContainerRequestFilter extension) {
return new Filter(extension);
}
private static final class Filter extends AbstractRestFilter<ContainerRequestFilter> {
public Filter(ContainerRequestFilter extension) {
super(extension);
}
@Override
public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception {
ContainerRequestContextImpl context = new ContainerRequestContextImpl(request, response);
extension.filter(context);
if (context.isAborted()) {
return;
}
chain.doFilter(request, response);
}
}
}

View File

@ -0,0 +1,228 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.remoting.http12.HttpUtils;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.Link;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.Response.StatusType;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import io.netty.handler.codec.DateFormatter;
final class ContainerResponseContextImpl implements ContainerResponseContext {
private final HttpRequest request;
private final HttpResponse response;
private final Result result;
private MultivaluedMap<String, String> headers;
public ContainerResponseContextImpl(HttpRequest request, HttpResponse response, Result result) {
this.request = request;
this.response = response;
this.result = result;
}
@Override
public int getStatus() {
return response.status();
}
@Override
public void setStatus(int code) {
response.setStatus(code);
}
@Override
public StatusType getStatusInfo() {
return Status.fromStatusCode(response.status());
}
@Override
public void setStatusInfo(StatusType statusInfo) {
response.setStatus(statusInfo.getStatusCode());
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public MultivaluedMap<String, Object> getHeaders() {
return (MultivaluedMap) getStringHeaders();
}
@Override
public MultivaluedMap<String, String> getStringHeaders() {
MultivaluedMap<String, String> headers = this.headers;
if (headers == null) {
headers = new MultivaluedMapWrapper<>(response.headers());
this.headers = headers;
}
return headers;
}
@Override
public String getHeaderString(String name) {
return response.header(name);
}
@Override
public Set<String> getAllowedMethods() {
return Collections.singleton(request.method());
}
@Override
public Date getDate() {
return null;
}
@Override
public Locale getLanguage() {
return HttpUtils.parseLocale(response.locale());
}
@Override
public int getLength() {
return -1;
}
@Override
public MediaType getMediaType() {
return Helper.toMediaType(response.mediaType());
}
@Override
public Map<String, NewCookie> getCookies() {
Collection<HttpCookie> cookies = request.cookies();
Map<String, NewCookie> result = new HashMap<>(cookies.size());
for (HttpCookie cookie : cookies) {
result.put(cookie.name(), Helper.convert(cookie));
}
return result;
}
@Override
public EntityTag getEntityTag() {
return null;
}
@Override
public Date getLastModified() {
String value = response.header("last-modified");
return value == null ? null : DateFormatter.parseHttpDate(value);
}
@Override
public URI getLocation() {
String location = response.header("location");
return location == null ? null : URI.create(location);
}
@Override
public Set<Link> getLinks() {
return null;
}
@Override
public boolean hasLink(String relation) {
return false;
}
@Override
public Link getLink(String relation) {
return null;
}
@Override
public Link.Builder getLinkBuilder(String relation) {
return null;
}
@Override
public boolean hasEntity() {
return getEntity() != null;
}
@Override
public Object getEntity() {
return result.getValue();
}
@Override
public Class<?> getEntityClass() {
return getHandler().getMethod().getReturnType();
}
@Override
public Type getEntityType() {
return getHandler().getMethod().getGenericReturnType();
}
@Override
public void setEntity(Object entity) {
result.setValue(entity);
}
@Override
public void setEntity(Object entity, Annotation[] annotations, MediaType mediaType) {
result.setValue(entity);
response.setContentType(Helper.toString(mediaType));
}
@Override
public Annotation[] getEntityAnnotations() {
return new Annotation[0];
}
@Override
public OutputStream getEntityStream() {
return response.outputStream();
}
@Override
public void setEntityStream(OutputStream outputStream) {
response.setOutputStream(outputStream);
}
private HandlerMeta getHandler() {
return request.attribute(RestConstants.HANDLER_ATTRIBUTE);
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener;
import javax.ws.rs.container.ContainerResponseFilter;
@Activate(onClass = "javax.ws.rs.container.ContainerResponseFilter")
public class ContainerResponseFilterAdapter implements RestExtensionAdapter<ContainerResponseFilter> {
@Override
public boolean accept(Object extension) {
return extension instanceof ContainerResponseFilter;
}
@Override
public RestFilter adapt(ContainerResponseFilter extension) {
return new Filter(extension);
}
private static final class Filter extends AbstractRestFilter<ContainerResponseFilter> implements Listener {
public Filter(ContainerResponseFilter extension) {
super(extension);
}
@Override
public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception {
extension.filter(
new ContainerRequestContextImpl(request, response),
new ContainerResponseContextImpl(request, response, result));
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper;
import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
@Activate(onClass = "javax.ws.rs.ext.ExceptionMapper")
public final class ExceptionMapperAdapter implements RestExtensionAdapter<ExceptionMapper<Throwable>> {
@Override
public boolean accept(Object extension) {
return extension instanceof ExceptionMapper;
}
@Override
public RestFilter adapt(ExceptionMapper<Throwable> extension) {
return new Filter(extension);
}
private static final class Filter extends AbstractRestFilter<ExceptionMapper<Throwable>> implements Listener {
private final Class<?> exceptionType;
public Filter(ExceptionMapper<Throwable> extension) {
super(extension);
exceptionType = TypeUtils.getSuperGenericType(extension.getClass());
}
@Override
public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception {
if (result.hasException()) {
Throwable t = result.getException();
if (exceptionType.isInstance(t)) {
try (Response r = extension.toResponse(t)) {
response.setBody(Helper.toBody(r));
}
}
}
}
@Override
public void onError(Throwable t, HttpRequest request, HttpResponse response) {
if (exceptionType.isInstance(t)) {
try (Response r = extension.toResponse(t)) {
response.setBody(Helper.toBody(r));
}
}
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta;
import javax.ws.rs.ext.InterceptorContext;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
public abstract class InterceptorContextImpl implements InterceptorContext {
protected final HttpRequest request;
public InterceptorContextImpl(HttpRequest request) {
this.request = request;
}
@Override
public Object getProperty(String name) {
return request.attribute(name);
}
@Override
public Collection<String> getPropertyNames() {
return request.parameterNames();
}
@Override
public void setProperty(String name, Object object) {
request.setAttribute(name, object);
}
@Override
public void removeProperty(String name) {
request.removeAttribute(name);
}
@Override
public Annotation[] getAnnotations() {
return getHandler().getMethod().getRealAnnotations();
}
@Override
public void setAnnotations(Annotation[] annotations) {}
@Override
public Class<?> getType() {
return getHandler().getMethod().getReturnType();
}
@Override
public void setType(Class<?> type) {}
@Override
public Type getGenericType() {
return getHandler().getMethod().getGenericReturnType();
}
@Override
public void setGenericType(Type genericType) {}
private HandlerMeta getHandler() {
return request.attribute(RestConstants.HANDLER_ATTRIBUTE);
}
}

View File

@ -0,0 +1,52 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
import javax.ws.rs.ext.ReaderInterceptor;
@Activate(onClass = "javax.ws.rs.ext.ReaderInterceptor")
public final class ReadInterceptorAdapter implements RestExtensionAdapter<ReaderInterceptor> {
@Override
public boolean accept(Object extension) {
return extension instanceof ReaderInterceptor;
}
@Override
public RestFilter adapt(ReaderInterceptor extension) {
return new Filter(extension);
}
private static final class Filter extends AbstractRestFilter<ReaderInterceptor> {
public Filter(ReaderInterceptor extension) {
super(extension);
}
@Override
public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception {
extension.aroundReadFrom(new ReaderInterceptorContextImpl(request, response, chain));
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.FilterChain;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ReaderInterceptorContext;
import java.io.IOException;
import java.io.InputStream;
final class ReaderInterceptorContextImpl extends InterceptorContextImpl implements ReaderInterceptorContext {
private final HttpResponse response;
private final FilterChain chain;
private MultivaluedMap<String, String> headers;
public ReaderInterceptorContextImpl(HttpRequest request, HttpResponse response, FilterChain chain) {
super(request);
this.response = response;
this.chain = chain;
headers = new MultivaluedMapWrapper<>(request.headers());
}
@Override
public Object proceed() throws IOException, WebApplicationException {
try {
chain.doFilter(request, response);
} catch (RuntimeException | IOException e) {
throw e;
} catch (Exception e) {
throw new WebApplicationException(e);
}
return null;
}
@Override
public InputStream getInputStream() {
return request.inputStream();
}
@Override
public void setInputStream(InputStream is) {
request.setInputStream(is);
}
@Override
public MultivaluedMap<String, String> getHeaders() {
MultivaluedMap<String, String> headers = this.headers;
if (headers == null) {
headers = new MultivaluedMapWrapper<>(request.headers());
this.headers = headers;
}
return headers;
}
@Override
public MediaType getMediaType() {
return Helper.toMediaType(request.mediaType());
}
@Override
public void setMediaType(MediaType mediaType) {
request.setContentType(Helper.toString(mediaType));
}
}

View File

@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.AbstractRestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter.Listener;
import javax.ws.rs.ext.WriterInterceptor;
@Activate(onClass = "javax.ws.rs.ext.WriterInterceptor")
public final class WriterInterceptorAdapter implements RestExtensionAdapter<WriterInterceptor> {
@Override
public boolean accept(Object extension) {
return extension instanceof WriterInterceptor;
}
@Override
public RestFilter adapt(WriterInterceptor extension) {
return new Filter(extension);
}
private static final class Filter extends AbstractRestFilter<WriterInterceptor> implements Listener {
public Filter(WriterInterceptor extension) {
super(extension);
}
@Override
public void onResponse(Result result, HttpRequest request, HttpResponse response) throws Exception {
extension.aroundWriteTo(new WriterInterceptorContextImpl(request, response, result));
}
}
}

View File

@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.Helper;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapWrapper;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.WriterInterceptorContext;
import java.io.OutputStream;
final class WriterInterceptorContextImpl extends InterceptorContextImpl implements WriterInterceptorContext {
private final HttpResponse response;
private final Result result;
private MultivaluedMap<String, Object> headers;
public WriterInterceptorContextImpl(HttpRequest request, HttpResponse response, Result result) {
super(request);
this.response = response;
this.result = result;
}
@Override
public void proceed() throws WebApplicationException {}
@Override
public Object getEntity() {
return result.getValue();
}
@Override
public void setEntity(Object entity) {
result.setValue(entity);
}
@Override
public OutputStream getOutputStream() {
return response.outputStream();
}
@Override
public void setOutputStream(OutputStream os) {
response.setOutputStream(os);
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public MultivaluedMap<String, Object> getHeaders() {
MultivaluedMap<String, Object> headers = this.headers;
if (headers == null) {
headers = new MultivaluedMapWrapper(response.headers());
this.headers = headers;
}
return headers;
}
@Override
public MediaType getMediaType() {
return Helper.toMediaType(response.mediaType());
}
@Override
public void setMediaType(MediaType mediaType) {
response.setContentType(Helper.toString(mediaType));
}
}

View File

@ -0,0 +1 @@
integer-to-multi-valued-map=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MultivaluedMapCreationConverter

View File

@ -0,0 +1,11 @@
jaxrs-path-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.PathParamArgumentResolver
jaxrs-matrix-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.MatrixParamArgumentResolver
jaxrs-query-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.QueryParamArgumentResolver
jaxrs-header-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.HeaderParamArgumentResolver
jaxrs-cookie-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.CookieParamArgumentResolver
jaxrs-form-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.FormParamArgumentResolver
jaxrs-bean-param=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.BeanParamArgumentResolver
jaxrs-body=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.BodyArgumentResolver
jaxrs-form=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.FormArgumentResolver
jaxrs-misc=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsMiscArgumentResolver
jaxrs-fallback=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.FallbackArgumentResolver

View File

@ -0,0 +1 @@
jaxrs-response=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsResponseRestFilter

View File

@ -0,0 +1,5 @@
jaxrs-request-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ContainerRequestFilterAdapter
jaxrs-response-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ContainerResponseFilterAdapter
jaxrs-read-interceptor=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ReadInterceptorAdapter
jaxrs-writer-interceptor=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.WriterInterceptorAdapter
jaxrs-exception-mapper=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.filter.ExceptionMapperAdapter

View File

@ -0,0 +1 @@
jaxrs=org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.JaxrsRequestMappingResolver

View File

@ -0,0 +1,156 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible;
import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import org.jboss.resteasy.annotations.Form;
@Path("/demoService")
public interface DemoService {
@GET
@Path("/hello")
Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b);
@GET
@Path("/error")
@Consumes({MediaType.TEXT_PLAIN})
@Produces({MediaType.TEXT_PLAIN})
String error();
@POST
@Path("/say")
@Consumes({MediaType.TEXT_PLAIN})
String sayHello(String name);
@POST
@Path("number")
Long testFormBody(@FormParam("number") Long number);
boolean isCalled();
@GET
@Path("/primitive")
int primitiveInt(@QueryParam("a") int a, @QueryParam("b") int b);
@GET
@Path("/primitiveLong")
long primitiveLong(@QueryParam("a") long a, @QueryParam("b") Long b);
@GET
@Path("/primitiveByte")
long primitiveByte(@QueryParam("a") byte a, @QueryParam("b") Long b);
@POST
@Path("/primitiveShort")
long primitiveShort(@QueryParam("a") short a, @QueryParam("b") Long b, int c);
@GET
@Path("/request")
void request(DefaultFullHttpRequest defaultFullHttpRequest);
@GET
@Path("testMapParam")
@Produces({MediaType.TEXT_PLAIN})
@Consumes({MediaType.TEXT_PLAIN})
String testMapParam(@QueryParam("test") Map<String, String> params);
@GET
@Path("testMapHeader")
@Produces({MediaType.TEXT_PLAIN})
@Consumes({MediaType.TEXT_PLAIN})
String testMapHeader(@HeaderParam("test") Map<String, String> headers);
@POST
@Path("testMapForm")
@Produces({MediaType.APPLICATION_JSON})
@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
List<String> testMapForm(MultivaluedMap<String, String> params);
@POST
@Path("/header")
@Consumes({MediaType.TEXT_PLAIN})
String header(@HeaderParam("header") String header);
@POST
@Path("/headerInt")
@Consumes({MediaType.TEXT_PLAIN})
int headerInt(@HeaderParam("header") int header);
@POST
@Path("/noStringParam")
@Consumes({MediaType.TEXT_PLAIN})
String noStringParam(@QueryParam("param") String param);
@POST
@Path("/noStringHeader")
@Consumes({MediaType.TEXT_PLAIN})
String noStringHeader(@HeaderParam("header") String header);
@POST
@Path("/noIntHeader")
@Consumes({MediaType.TEXT_PLAIN})
int noIntHeader(int header);
@POST
@Path("/noIntParam")
@Consumes({MediaType.TEXT_PLAIN})
int noIntParam(int header);
@POST
@Path("/noBodyArg")
@Consumes({MediaType.APPLICATION_JSON})
User noBodyArg(User user);
@POST
@Path("/list")
List<User> list(List<User> users);
@POST
@Path("/set")
Set<User> set(Set<User> users);
@POST
@Path("/array")
User[] array(User[] users);
@POST
@Path("/stringMap")
Map<String, User> stringMap(Map<String, User> userMap);
@POST
@Path("/map")
Map<User, User> userMap(Map<User, User> userMap);
@POST
@Path("/formBody")
User formBody(@Form User user);
}

View File

@ -0,0 +1,187 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible;
import org.apache.dubbo.rpc.RpcContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
@Path("/demoService")
public class DemoServiceImpl implements DemoService {
private static Map<String, Object> context;
private boolean called;
@POST
@Path("/say")
@Consumes({MediaType.TEXT_PLAIN})
@Override
public String sayHello(String name) {
called = true;
return "Hello, " + name;
}
@Override
public Long testFormBody(Long number) {
return number;
}
public boolean isCalled() {
return called;
}
@Override
public int primitiveInt(int a, int b) {
return a + b;
}
@Override
public long primitiveLong(long a, Long b) {
return a + b;
}
@Override
public long primitiveByte(byte a, Long b) {
return a + b;
}
@Override
public long primitiveShort(short a, Long b, int c) {
return a + b;
}
@Override
public void request(DefaultFullHttpRequest defaultFullHttpRequest) {}
@Override
public String testMapParam(Map<String, String> params) {
return params.get("param");
}
@Override
public String testMapHeader(Map<String, String> headers) {
return headers.get("header");
}
@Override
public List<String> testMapForm(MultivaluedMap<String, String> params) {
return params.get("form");
}
@Override
public String header(String header) {
return header;
}
@Override
public int headerInt(int header) {
return header;
}
@Override
public String noStringParam(String param) {
return param;
}
@Override
public String noStringHeader(String header) {
return header;
}
@POST
@Path("/noIntHeader")
@Consumes({MediaType.TEXT_PLAIN})
@Override
public int noIntHeader(@HeaderParam("header") int header) {
return header;
}
@POST
@Path("/noIntParam")
@Consumes({MediaType.TEXT_PLAIN})
@Override
public int noIntParam(@QueryParam("header") int header) {
return header;
}
@Override
public User noBodyArg(User user) {
return user;
}
@GET
@Path("/hello")
@Override
public Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b) {
context = RpcContext.getServerAttachment().getObjectAttachments();
return a + b;
}
@GET
@Path("/error")
@Override
public String error() {
throw new RuntimeException("test error");
}
public static Map<String, Object> getAttachments() {
return context;
}
@Override
public List<User> list(List<User> users) {
return users;
}
@Override
public Set<User> set(Set<User> users) {
return users;
}
@Override
public User[] array(User[] users) {
return users;
}
@Override
public Map<String, User> stringMap(Map<String, User> userMap) {
return userMap;
}
@Override
public Map<User, User> userMap(Map<User, User> userMap) {
return userMap;
}
@Override
public User formBody(User user) {
user.setName("formBody");
return user;
}
}

View File

@ -0,0 +1,563 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ModuleServiceRepository;
import org.apache.dubbo.rpc.model.ProviderModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter.TestContainerRequestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter.TraceFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter.TraceRequestAndResponseFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.intercept.DynamicTraceInterceptor;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.AnotherUserRestService;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.AnotherUserRestServiceImpl;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.HttpMethodService;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.HttpMethodServiceImpl;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoForTestException;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoService;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.RestDemoServiceImpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.hamcrest.CoreMatchers;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import static org.apache.dubbo.remoting.Constants.SERVER_KEY;
import static org.apache.dubbo.rpc.protocol.tri.rest.RestConstants.EXTENSION_KEY;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
class JaxrsRestProtocolTest {
private final Protocol tProtocol =
ApplicationModel.defaultModel().getExtensionLoader(Protocol.class).getExtension("tri");
private final Protocol protocol =
ApplicationModel.defaultModel().getExtensionLoader(Protocol.class).getExtension("rest");
private final ProxyFactory proxy = ApplicationModel.defaultModel()
.getExtensionLoader(ProxyFactory.class)
.getAdaptiveExtension();
private final int availablePort = NetUtils.getAvailablePort();
private final URL exportUrl =
URL.valueOf("tri://127.0.0.1:" + availablePort + "/rest?interface=" + DemoService.class.getName());
private static final String SERVER = "netty4";
private final ModuleServiceRepository repository =
ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
@AfterEach
public void tearDown() {
tProtocol.destroy();
protocol.destroy();
FrameworkModel.destroyAll();
}
@Test
void testRestProtocol() {
URL url = URL.valueOf("tri://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface="
+ DemoService.class.getName());
DemoServiceImpl server = new DemoServiceImpl();
url = registerProvider(url, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url));
Invoker<DemoService> invoker = protocol.refer(DemoService.class, url);
Assertions.assertFalse(server.isCalled());
DemoService client = proxy.getProxy(invoker);
String result = client.sayHello("haha");
Assertions.assertTrue(server.isCalled());
Assertions.assertEquals("Hello, haha", result);
String header = client.header("header test");
Assertions.assertEquals("header test", header);
Assertions.assertEquals(1, client.headerInt(1));
invoker.destroy();
exporter.unexport();
}
@Test
void testAnotherUserRestProtocolByDifferentRestClient() {
testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.OK_HTTP);
testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT);
testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.URL_CONNECTION);
}
void testAnotherUserRestProtocol(String restClient) {
URL url = URL.valueOf("tri://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface="
+ AnotherUserRestService.class.getName() + "&" + org.apache.dubbo.remoting.Constants.CLIENT_KEY + "="
+ restClient);
AnotherUserRestServiceImpl server = new AnotherUserRestServiceImpl();
url = this.registerProvider(url, server, AnotherUserRestService.class);
Exporter<AnotherUserRestService> exporter =
tProtocol.export(proxy.getInvoker(server, AnotherUserRestService.class, url));
Invoker<AnotherUserRestService> invoker = protocol.refer(AnotherUserRestService.class, url);
AnotherUserRestService client = proxy.getProxy(invoker);
User result = client.getUser(123l);
Assertions.assertEquals(123l, result.getId());
result.setName("dubbo");
Assertions.assertEquals(123l, client.registerUser(result).getId());
Assertions.assertEquals("context", client.getContext());
byte[] bytes = {1, 2, 3, 4};
Assertions.assertTrue(Arrays.equals(bytes, client.bytes(bytes)));
Assertions.assertEquals(1l, client.number(1l));
HashMap<String, String> map = new HashMap<>();
map.put("headers", "h1");
Assertions.assertEquals("h1", client.headerMap(map));
Assertions.assertEquals(null, client.headerMap(null));
invoker.destroy();
exporter.unexport();
}
@Test
void testRestProtocolWithContextPath() {
DemoServiceImpl server = new DemoServiceImpl();
Assertions.assertFalse(server.isCalled());
int port = NetUtils.getAvailablePort();
URL url = URL.valueOf(
"tri://127.0.0.1:" + port + "/a/b/c?version=1.0.0&interface=" + DemoService.class.getName());
url = this.registerProvider(url, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url));
url = URL.valueOf(
"rest://127.0.0.1:" + port + "/a/b/c/?version=1.0.0&interface=" + DemoService.class.getName());
Invoker<DemoService> invoker = protocol.refer(DemoService.class, url);
DemoService client = proxy.getProxy(invoker);
String result = client.sayHello("haha");
Assertions.assertTrue(server.isCalled());
Assertions.assertEquals("Hello, haha", result);
invoker.destroy();
exporter.unexport();
}
@Test
void testExport() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
RpcContext.getClientAttachment().setAttachment("timeout", "20000");
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, url));
Integer echoString = demoService.hello(1, 2);
assertThat(echoString, is(3));
exporter.unexport();
}
@Test
void testNettyServer() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
Exporter<DemoService> exporter =
tProtocol.export(proxy.getInvoker(new DemoServiceImpl(), DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Integer echoString = demoService.hello(10, 10);
assertThat(echoString, is(20));
exporter.unexport();
}
@Test
void testInvoke() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, url));
RpcInvocation rpcInvocation = new RpcInvocation(
"hello", DemoService.class.getName(), "", new Class[] {Integer.class, Integer.class}, new Integer[] {
2, 3
});
rpcInvocation.setTargetServiceUniqueName(url.getServiceKey());
Result result = exporter.getInvoker().invoke(rpcInvocation);
assertThat(result.getValue(), CoreMatchers.<Object>is(5));
}
@Test
void testDefaultPort() {
assertThat(protocol.getDefaultPort(), is(80));
}
@Test
void testRestExceptionMapper() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL exceptionUrl = url.addParameter(EXTENSION_KEY, ResteasyExceptionMapper.class.getName());
tProtocol.export(proxy.getInvoker(server, DemoService.class, exceptionUrl));
DemoService referDemoService = this.proxy.getProxy(protocol.refer(DemoService.class, exceptionUrl));
Assertions.assertEquals("test-exception", referDemoService.error());
}
@Test
void testFormConsumerParser() {
DemoService server = new DemoServiceImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Long number = demoService.testFormBody(18l);
Assertions.assertEquals(18l, number);
exporter.unexport();
}
@Test
void test404() {
Assertions.assertThrows(RpcException.class, () -> {
DemoService server = new DemoServiceImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
URL referUrl = URL.valueOf(
"tri://127.0.0.1:" + availablePort + "/rest?interface=" + RestDemoForTestException.class.getName());
RestDemoForTestException restDemoForTestException =
this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl));
restDemoForTestException.test404();
exporter.unexport();
});
}
@Test
void test400() {
Assertions.assertThrows(RpcException.class, () -> {
DemoService server = new DemoServiceImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
URL referUrl = URL.valueOf(
"tri://127.0.0.1:" + availablePort + "/rest?interface=" + RestDemoForTestException.class.getName());
RestDemoForTestException restDemoForTestException =
this.proxy.getProxy(protocol.refer(RestDemoForTestException.class, referUrl));
restDemoForTestException.test400("abc", "edf");
exporter.unexport();
});
}
@Test
void testPrimitive() {
DemoService server = new DemoServiceImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Integer result = demoService.primitiveInt(1, 2);
Long resultLong = demoService.primitiveLong(1, 2l);
long resultByte = demoService.primitiveByte((byte) 1, 2l);
long resultShort = demoService.primitiveShort((short) 1, 2l, 1);
assertThat(result, is(3));
assertThat(resultShort, is(3l));
assertThat(resultLong, is(3l));
assertThat(resultByte, is(3l));
exporter.unexport();
}
@Test
void testMapParam() {
DemoService server = new DemoServiceImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Map<String, String> params = new HashMap<>();
params.put("param", "P1");
;
Map<String, String> headers = new HashMap<>();
headers.put("header", "H1");
Assertions.assertEquals("P1", demoService.testMapParam(params));
Assertions.assertEquals("H1", demoService.testMapHeader(headers));
MultivaluedMapImpl<String, String> forms = new MultivaluedMapImpl<>();
forms.put("form", Arrays.asList("F1"));
Assertions.assertEquals(Arrays.asList("F1"), demoService.testMapForm(forms));
exporter.unexport();
}
@Test
void testNoArgParam() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Assertions.assertEquals(null, demoService.noStringHeader(null));
Assertions.assertEquals(null, demoService.noStringParam(null));
/* Assertions.assertThrows(RpcException.class, () -> {
demoService.noIntHeader(1);
});
Assertions.assertThrows(RpcException.class, () -> {
demoService.noIntParam(1);
});*/
Assertions.assertEquals(null, demoService.noBodyArg(null));
exporter.unexport();
}
@Test
void testHttpMethods() {
testHttpMethod(org.apache.dubbo.remoting.Constants.OK_HTTP);
testHttpMethod(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT);
testHttpMethod(org.apache.dubbo.remoting.Constants.URL_CONNECTION);
}
void testHttpMethod(String restClient) {
HttpMethodService server = new HttpMethodServiceImpl();
URL url = URL.valueOf("tri://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface="
+ HttpMethodService.class.getName() + "&"
+ org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient);
url = this.registerProvider(url, server, HttpMethodService.class);
Exporter<HttpMethodService> exporter = tProtocol.export(proxy.getInvoker(server, HttpMethodService.class, url));
HttpMethodService demoService = this.proxy.getProxy(protocol.refer(HttpMethodService.class, url));
String expect = "hello";
Assertions.assertEquals(null, demoService.sayHelloHead());
Assertions.assertEquals(expect, demoService.sayHelloDelete("hello"));
Assertions.assertEquals(expect, demoService.sayHelloGet("hello"));
Assertions.assertEquals(expect, demoService.sayHelloOptions("hello"));
// Assertions.assertEquals(expect, demoService.sayHelloPatch("hello"));
Assertions.assertEquals(expect, demoService.sayHelloPost("hello"));
Assertions.assertEquals(expect, demoService.sayHelloPut("hello"));
exporter.unexport();
}
@Test
void test405() {
int availablePort = NetUtils.getAvailablePort();
URL url = URL.valueOf("tri://127.0.0.1:" + availablePort
+ "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.rest.RestDemoService&");
RestDemoServiceImpl server = new RestDemoServiceImpl();
url = this.registerProvider(url, server, RestDemoService.class);
Exporter<RestDemoService> exporter = tProtocol.export(proxy.getInvoker(server, RestDemoService.class, url));
URL consumer = URL.valueOf("rest://127.0.0.1:" + availablePort + "/?version=1.0.0&interface="
+ RestDemoForTestException.class.getName());
consumer = this.registerProvider(consumer, server, RestDemoForTestException.class);
Invoker<RestDemoForTestException> invoker = protocol.refer(RestDemoForTestException.class, consumer);
RestDemoForTestException client = proxy.getProxy(invoker);
Assertions.assertThrows(RpcException.class, () -> {
client.testMethodDisallowed("aaa");
});
invoker.destroy();
exporter.unexport();
}
@Test
void testContainerRequestFilter() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(EXTENSION_KEY, TestContainerRequestFilter.class.getName());
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Assertions.assertEquals("return-success", demoService.sayHello("hello"));
exporter.unexport();
}
@Test
void testIntercept() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(EXTENSION_KEY, DynamicTraceInterceptor.class.getName());
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Assertions.assertEquals("intercept", demoService.sayHello("hello"));
exporter.unexport();
}
@Test
void testResponseFilter() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(EXTENSION_KEY, TraceFilter.class.getName());
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Assertions.assertEquals("response-filter", demoService.sayHello("hello"));
exporter.unexport();
}
@Test
void testCollectionResult() {
DemoService server = new DemoServiceImpl();
URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Assertions.assertEquals(
User.getInstance(),
demoService.list(Arrays.asList(User.getInstance())).get(0));
HashSet<User> objects = new HashSet<>();
objects.add(User.getInstance());
Assertions.assertEquals(User.getInstance(), new ArrayList<>(demoService.set(objects)).get(0));
Assertions.assertEquals(User.getInstance(), demoService.array(objects.toArray(new User[0]))[0]);
Map<String, User> map = new HashMap<>();
map.put("map", User.getInstance());
Assertions.assertEquals(User.getInstance(), demoService.stringMap(map).get("map"));
// Map<User, User> maps = new HashMap<>();
// maps.put(User.getInstance(), User.getInstance());
// Assertions.assertEquals(User.getInstance(), demoService.userMap(maps).get(User.getInstance()));
exporter.unexport();
}
@Test
void testRequestAndResponseFilter() {
DemoService server = new DemoServiceImpl();
URL exportUrl = URL.valueOf("tri://127.0.0.1:" + availablePort + "/rest?interface="
+ DemoService.class.getName() + "&extension=" + TraceRequestAndResponseFilter.class.getName());
URL nettyUrl = this.registerProvider(exportUrl, server, DemoService.class);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
Assertions.assertEquals("header-result", demoService.sayHello("hello"));
exporter.unexport();
}
@Test
void testFormBody() {
DemoService server = new DemoServiceImpl();
URL url = this.registerProvider(exportUrl, server, DemoService.class);
URL nettyUrl = url.addParameter(SERVER_KEY, SERVER);
Exporter<DemoService> exporter = tProtocol.export(proxy.getInvoker(server, DemoService.class, nettyUrl));
DemoService demoService = this.proxy.getProxy(protocol.refer(DemoService.class, nettyUrl));
User user = demoService.formBody(User.getInstance());
Assertions.assertEquals("formBody", user.getName());
exporter.unexport();
}
private URL registerProvider(URL url, Object impl, Class<?> interfaceClass) {
ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
ProviderModel providerModel = new ProviderModel(url.getServiceKey(), impl, serviceDescriptor, null, null);
repository.registerProvider(providerModel);
return url.setServiceModel(providerModel);
}
}

View File

@ -0,0 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
public class ResteasyExceptionMapper implements ExceptionMapper<RuntimeException> {
@Override
public Response toResponse(RuntimeException exception) {
return Response.status(200).entity("test-exception").build();
}
}

View File

@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible;
import java.io.Serializable;
import java.util.Objects;
/**
* User Entity
*
* @since 2.7.6
*/
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public static User getInstance() {
User user = new User();
user.setAge(18);
user.setName("dubbo");
user.setId(404l);
return user;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(age, user.age);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
@Override
public String toString() {
return "User{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';
}
}

View File

@ -0,0 +1,34 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import java.io.IOException;
@Priority(Priorities.USER)
public class TestContainerRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.abortWith(Response.status(200).entity("return-success").build());
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import java.io.IOException;
@Priority(Priorities.USER)
public class TraceFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
System.out.println(
"Request filter invoked: " + requestContext.getUriInfo().getAbsolutePath());
}
@Override
public void filter(
ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
throws IOException {
containerResponseContext.setEntity("response-filter");
System.out.println("Response filter invoked.");
}
}

View File

@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.filter;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import java.io.IOException;
@Priority(Priorities.USER)
public class TraceRequestAndResponseFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
requestContext.getHeaders().add("test-response", "header-result");
}
@Override
public void filter(
ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext)
throws IOException {
String headerString = containerRequestContext.getHeaderString("test-response");
containerResponseContext.setEntity(headerString);
}
}

View File

@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.intercept;
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Priority(Priorities.USER)
public class DynamicTraceInterceptor implements ReaderInterceptor, WriterInterceptor {
public DynamicTraceInterceptor() {}
@Override
public Object aroundReadFrom(ReaderInterceptorContext readerInterceptorContext)
throws IOException, WebApplicationException {
System.out.println("Dynamic reader interceptor invoked");
return readerInterceptorContext.proceed();
}
@Override
public void aroundWriteTo(WriterInterceptorContext writerInterceptorContext)
throws IOException, WebApplicationException {
System.out.println("Dynamic writer interceptor invoked");
writerInterceptorContext.getOutputStream().write("intercept".getBytes(StandardCharsets.UTF_8));
writerInterceptorContext.proceed();
}
}

View File

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Map;
@Path("u")
@Consumes({MediaType.APPLICATION_JSON, MediaType.TEXT_XML})
public interface AnotherUserRestService {
@GET
@Path("{id : \\d+}")
@Produces({MediaType.APPLICATION_JSON})
User getUser(@PathParam("id") Long id);
@POST
@Path("register")
@Produces("text/xml; charset=UTF-8")
RegistrationResult registerUser(User user);
@GET
@Path("context")
@Produces({MediaType.TEXT_PLAIN})
String getContext();
@POST
@Path("bytes")
@Produces({MediaType.APPLICATION_JSON})
byte[] bytes(byte[] bytes);
@POST
@Path("number")
@Produces({MediaType.APPLICATION_JSON})
Long number(Long number);
@POST
@Path("headerMap")
@Produces({MediaType.TEXT_PLAIN})
String headerMap(@HeaderParam("headers") Map<String, String> headers);
}

View File

@ -0,0 +1,58 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User;
import java.util.Map;
public class AnotherUserRestServiceImpl implements AnotherUserRestService {
@Override
public User getUser(Long id) {
User user = new User();
user.setId(id);
return user;
}
@Override
public RegistrationResult registerUser(User user) {
return new RegistrationResult(user.getId());
}
@Override
public String getContext() {
return "context";
}
@Override
public byte[] bytes(byte[] bytes) {
return bytes;
}
@Override
public Long number(Long number) {
return number;
}
@Override
public String headerMap(Map<String, String> headers) {
return headers.get("headers");
}
}

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.PATCH;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
@Path("/demoService")
public interface HttpMethodService {
@POST
@Path("/sayPost")
@Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
String sayHelloPost(@QueryParam("name") String name);
@DELETE
@Path("/sayDelete")
@Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
String sayHelloDelete(@QueryParam("name") String name);
@HEAD
@Path("/sayHead")
@Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
String sayHelloHead();
@GET
@Path("/sayGet")
@Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
String sayHelloGet(@QueryParam("name") String name);
@PUT
@Path("/sayPut")
@Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
String sayHelloPut(@QueryParam("name") String name);
@PATCH
@Path("/sayPatch")
@Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
String sayHelloPatch(@QueryParam("name") String name);
@OPTIONS
@Path("/sayOptions")
@Consumes({javax.ws.rs.core.MediaType.TEXT_PLAIN})
String sayHelloOptions(@QueryParam("name") String name);
}

View File

@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
public class HttpMethodServiceImpl implements HttpMethodService {
@Override
public String sayHelloPost(String name) {
return name;
}
@Override
public String sayHelloDelete(String name) {
return name;
}
@Override
public String sayHelloHead() {
return "hello";
}
@Override
public String sayHelloGet(String name) {
return name;
}
@Override
public String sayHelloPut(String name) {
return name;
}
@Override
public String sayHelloPatch(String name) {
return name;
}
@Override
public String sayHelloOptions(String name) {
return name;
}
}

View File

@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
import java.util.Objects;
/**
* DTO to customize the returned message
*/
@XmlRootElement
public class RegistrationResult implements Serializable {
private Long id;
public RegistrationResult() {}
public RegistrationResult(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RegistrationResult that = (RegistrationResult) o;
return Objects.equals(id, that.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

View File

@ -0,0 +1,44 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
@Path("/demoService")
public interface RestDemoForTestException {
@POST
@Path("/noFound")
@Produces(MediaType.TEXT_PLAIN)
String test404();
@GET
@Consumes({MediaType.TEXT_PLAIN})
@Path("/hello")
Integer test400(@QueryParam("a") String a, @QueryParam("b") String b);
@POST
@Path("{uid}")
String testMethodDisallowed(@PathParam("uid") String uid);
}

View File

@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/demoService")
public interface RestDemoService {
@GET
@Path("/hello")
Integer hello(@QueryParam("a") Integer a, @QueryParam("b") Integer b);
@GET
@Path("/findUserById")
Response findUserById(@QueryParam("id") Integer id);
@GET
@Path("/error")
String error();
@POST
@Path("/say")
@Consumes({MediaType.TEXT_PLAIN})
String sayHello(String name);
@POST
@Path("number")
@Produces({MediaType.APPLICATION_FORM_URLENCODED})
@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
Long testFormBody(@FormParam("number") Long number);
boolean isCalled();
@DELETE
@Path("{uid}")
String deleteUserByUid(@PathParam("uid") String uid);
@DELETE
@Path("/deleteUserById/{uid}")
public Response deleteUserById(@PathParam("uid") String uid);
}

View File

@ -0,0 +1,80 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest;
import org.apache.dubbo.rpc.RpcContext;
import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;
import org.jboss.resteasy.specimpl.BuiltResponse;
public class RestDemoServiceImpl implements RestDemoService {
private static Map<String, Object> context;
private boolean called;
@Override
public String sayHello(String name) {
called = true;
return "Hello, " + name;
}
@Override
public Long testFormBody(Long number) {
return number;
}
public boolean isCalled() {
return called;
}
@Override
public String deleteUserByUid(String uid) {
return uid;
}
@Override
public Integer hello(Integer a, Integer b) {
context = RpcContext.getServerAttachment().getObjectAttachments();
return a + b;
}
@Override
public Response findUserById(Integer id) {
Map<String, Object> content = new HashMap<>();
content.put("username", "jack");
content.put("id", id);
return BuiltResponse.ok(content).build();
}
@Override
public String error() {
throw new RuntimeException();
}
@Override
public Response deleteUserById(String uid) {
return Response.status(300).entity("deleted").build();
}
public static Map<String, Object> getAttachments() {
return context;
}
}

View File

@ -22,6 +22,7 @@
</Console>
</Appenders>
<Loggers>
<Logger name="org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils" level="error"/>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-plugin</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-rest-servlet</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,67 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import java.util.Collections;
import java.util.Enumeration;
final class DummyFilterConfig implements FilterConfig {
private final String filterName;
private final FrameworkModel frameworkModel;
private final ServletContext servletContext;
public DummyFilterConfig(String filterName, FrameworkModel frameworkModel, ServletContext servletContext) {
this.filterName = filterName;
this.frameworkModel = frameworkModel;
this.servletContext = servletContext;
}
@Override
public String getFilterName() {
return filterName;
}
@Override
public ServletContext getServletContext() {
return servletContext;
}
@Override
public String getInitParameter(String name) {
String prefix = RestConstants.CONFIG_PREFIX + "filter-config.";
Configuration conf = ConfigurationUtils.getGlobalConfiguration(frameworkModel.defaultApplication());
String value = conf.getString(prefix + filterName + "." + name);
if (value == null) {
value = conf.getString(prefix + name);
}
return value;
}
@Override
public Enumeration<String> getInitParameterNames() {
return Collections.emptyEnumeration();
}
}

View File

@ -0,0 +1,318 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import javax.servlet.Filter;
import javax.servlet.FilterRegistration;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration;
import javax.servlet.SessionCookieConfig;
import javax.servlet.SessionTrackingMode;
import javax.servlet.descriptor.JspConfigDescriptor;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
final class DummyServletContext implements ServletContext {
private final FrameworkModel frameworkModel;
private final Map<String, Object> attributes = new HashMap<>();
private final Map<String, String> initParameters = new HashMap<>();
public DummyServletContext(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
}
@Override
public String getContextPath() {
return "/";
}
@Override
public ServletContext getContext(String uripath) {
return this;
}
@Override
public int getMajorVersion() {
return 3;
}
@Override
public int getMinorVersion() {
return 1;
}
@Override
public int getEffectiveMajorVersion() {
return 3;
}
@Override
public int getEffectiveMinorVersion() {
return 1;
}
@Override
public String getMimeType(String file) {
return null;
}
@Override
public Set<String> getResourcePaths(String path) {
return null;
}
@Override
public URL getResource(String path) {
return null;
}
@Override
public InputStream getResourceAsStream(String path) {
return null;
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
return null;
}
@Override
public RequestDispatcher getNamedDispatcher(String name) {
return null;
}
@Override
public Servlet getServlet(String name) {
throw new UnsupportedOperationException();
}
@Override
public Enumeration<Servlet> getServlets() {
throw new UnsupportedOperationException();
}
@Override
public Enumeration<String> getServletNames() {
throw new UnsupportedOperationException();
}
@Override
public void log(String msg) {
LoggerFactory.getLogger(DummyServletContext.class).info(msg);
}
@Override
public void log(Exception exception, String msg) {
LoggerFactory.getLogger(DummyServletContext.class).info(msg, exception);
}
@Override
public void log(String message, Throwable throwable) {
LoggerFactory.getLogger(DummyServletContext.class).info(message, throwable);
}
@Override
public String getRealPath(String path) {
throw new UnsupportedOperationException();
}
@Override
public String getServerInfo() {
return "Dubbo Rest Server/1.0";
}
@Override
public String getInitParameter(String name) {
String value = initParameters.get(name);
if (value != null) {
return value;
}
Configuration conf = ConfigurationUtils.getGlobalConfiguration(frameworkModel.defaultApplication());
return conf.getString(RestConstants.CONFIG_PREFIX + "servlet-context." + name);
}
@Override
public Enumeration<String> getInitParameterNames() {
return Collections.enumeration(initParameters.keySet());
}
@Override
public boolean setInitParameter(String name, String value) {
return initParameters.putIfAbsent(name, value) == null;
}
@Override
public Object getAttribute(String name) {
return attributes.get(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(attributes.keySet());
}
@Override
public void setAttribute(String name, Object object) {
attributes.put(name, object);
}
@Override
public void removeAttribute(String name) {
attributes.remove(name);
}
@Override
public String getServletContextName() {
return "";
}
@Override
public ServletRegistration.Dynamic addServlet(String servletName, String className) {
throw new UnsupportedOperationException();
}
@Override
public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
throw new UnsupportedOperationException();
}
@Override
public ServletRegistration.Dynamic addServlet(String servletName, Class<? extends Servlet> servletClass) {
throw new UnsupportedOperationException();
}
@Override
public <T extends Servlet> T createServlet(Class<T> clazz) {
throw new UnsupportedOperationException();
}
@Override
public ServletRegistration getServletRegistration(String servletName) {
throw new UnsupportedOperationException();
}
@Override
public Map<String, ? extends ServletRegistration> getServletRegistrations() {
throw new UnsupportedOperationException();
}
@Override
public FilterRegistration.Dynamic addFilter(String filterName, String className) {
throw new UnsupportedOperationException();
}
@Override
public FilterRegistration.Dynamic addFilter(String filterName, Filter filter) {
throw new UnsupportedOperationException();
}
@Override
public FilterRegistration.Dynamic addFilter(String filterName, Class<? extends Filter> filterClass) {
throw new UnsupportedOperationException();
}
@Override
public <T extends Filter> T createFilter(Class<T> clazz) {
throw new UnsupportedOperationException();
}
@Override
public FilterRegistration getFilterRegistration(String filterName) {
throw new UnsupportedOperationException();
}
@Override
public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
throw new UnsupportedOperationException();
}
@Override
public SessionCookieConfig getSessionCookieConfig() {
throw new UnsupportedOperationException();
}
@Override
public void setSessionTrackingModes(Set<SessionTrackingMode> sessionTrackingModes) {
throw new UnsupportedOperationException();
}
@Override
public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
throw new UnsupportedOperationException();
}
@Override
public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
throw new UnsupportedOperationException();
}
@Override
public void addListener(String className) {
throw new UnsupportedOperationException();
}
@Override
public <T extends EventListener> void addListener(T t) {
throw new UnsupportedOperationException();
}
@Override
public void addListener(Class<? extends EventListener> listenerClass) {
throw new UnsupportedOperationException();
}
@Override
public <T extends EventListener> T createListener(Class<T> clazz) {
throw new UnsupportedOperationException();
}
@Override
public JspConfigDescriptor getJspConfigDescriptor() {
throw new UnsupportedOperationException();
}
@Override
public ClassLoader getClassLoader() {
return getClass().getClassLoader();
}
@Override
public void declareRoles(String... roleNames) {
throw new UnsupportedOperationException();
}
@Override
public String getVirtualServerName() {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,109 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtensionAdapter;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestFilter;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RestUtils;
import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.Arrays;
@Activate(onClass = "javax.servlet.Filter")
public final class FilterAdapter implements RestExtensionAdapter<Filter> {
private final ServletHttpMessageAdapterFactory adapterFactory;
public FilterAdapter(FrameworkModel frameworkModel) {
adapterFactory = (ServletHttpMessageAdapterFactory)
frameworkModel.getExtension(HttpMessageAdapterFactory.class, "servlet");
}
@Override
public boolean accept(Object extension) {
return extension instanceof Filter;
}
@Override
public RestFilter adapt(Filter extension) {
try {
String filterName = extension.getClass().getSimpleName();
extension.init(adapterFactory.adaptFilterConfig(filterName));
} catch (ServletException e) {
throw new RestException(e);
}
return new FilterRestFilter(extension);
}
private static final class FilterRestFilter implements RestFilter {
private final Filter filter;
@Override
public int getPriority() {
return RestUtils.getPriority(filter);
}
@Override
public String[] getPatterns() {
return RestUtils.getPattens(filter);
}
public FilterRestFilter(Filter filter) {
this.filter = filter;
}
@Override
public void doFilter(HttpRequest request, HttpResponse response, FilterChain chain) throws Exception {
filter.doFilter((ServletRequest) request, (ServletResponse) response, (q, p) -> {
try {
chain.doFilter(request, response);
} catch (RuntimeException | IOException | ServletException e) {
throw e;
} catch (Exception e) {
throw new ServletException(e);
}
});
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("RestFilter{filter=");
sb.append(filter);
int priority = getPriority();
if (priority != 0) {
sb.append(", priority=").append(priority);
}
String[] patterns = getPatterns();
if (patterns != null) {
sb.append(", patterns=").append(Arrays.toString(patterns));
}
return sb.append('}').toString();
}
}
}

View File

@ -0,0 +1,145 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.io.StreamUtils;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpRequest.FileUpload;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import javax.servlet.http.Part;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
final class Helper {
private Helper() {}
static javax.servlet.http.Cookie[] convertCookies(Collection<HttpCookie> hCookies) {
javax.servlet.http.Cookie[] cookies = new javax.servlet.http.Cookie[hCookies.size()];
int i = 0;
for (HttpCookie cookie : hCookies) {
cookies[i++] = convert(cookie);
}
return cookies;
}
static javax.servlet.http.Cookie convert(HttpCookie hCookie) {
javax.servlet.http.Cookie cookie = new javax.servlet.http.Cookie(hCookie.name(), hCookie.value());
if (hCookie.domain() != null) {
cookie.setDomain(hCookie.domain());
}
cookie.setMaxAge((int) hCookie.maxAge());
cookie.setHttpOnly(hCookie.httpOnly());
cookie.setPath(hCookie.path());
cookie.setSecure(hCookie.secure());
return cookie;
}
static HttpCookie convert(javax.servlet.http.Cookie sCookie) {
HttpCookie cookie = new HttpCookie(sCookie.getName(), sCookie.getValue());
cookie.setDomain(sCookie.getDomain());
cookie.setMaxAge(sCookie.getMaxAge());
cookie.setHttpOnly(sCookie.isHttpOnly());
cookie.setPath(sCookie.getPath());
cookie.setSecure(sCookie.getSecure());
return cookie;
}
public static Part convert(FileUpload part) {
return new FileUploadPart(part);
}
public static Collection<Part> convertParts(Collection<FileUpload> parts) {
if (CollectionUtils.isEmpty(parts)) {
return Collections.emptyList();
}
List<Part> result = new ArrayList<>(parts.size());
for (FileUpload part : parts) {
result.add(convert(part));
}
return result;
}
public static final class FileUploadPart implements Part {
private final FileUpload fileUpload;
public FileUploadPart(FileUpload fileUpload) {
this.fileUpload = fileUpload;
}
@Override
public InputStream getInputStream() {
return fileUpload.inputStream();
}
@Override
public String getContentType() {
return fileUpload.contentType();
}
@Override
public String getName() {
return fileUpload.name();
}
@Override
public String getSubmittedFileName() {
return fileUpload.filename();
}
@Override
public long getSize() {
return fileUpload.size();
}
@Override
public void write(String fileName) {
try (FileOutputStream fos = new FileOutputStream(fileName)) {
StreamUtils.copy(fileUpload.inputStream(), fos);
} catch (IOException e) {
throw new RestException(e);
}
}
@Override
public void delete() {}
@Override
public String getHeader(String name) {
return null;
}
@Override
public Collection<String> getHeaders(String name) {
return null;
}
@Override
public Collection<String> getHeaderNames() {
return Collections.emptyList();
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
public interface HttpSessionFactory extends RestExtension {
HttpSession getSession(HttpServletRequest request, boolean create);
}

View File

@ -0,0 +1,95 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.util.HashSet;
import java.util.Set;
@Activate(onClass = "javax.servlet.http.HttpServletRequest")
public class ServletArgumentResolver implements ArgumentResolver {
private static final Set<Class<?>> SUPPORTED_TYPES = new HashSet<>();
static {
SUPPORTED_TYPES.add(ServletRequest.class);
SUPPORTED_TYPES.add(HttpServletRequest.class);
SUPPORTED_TYPES.add(ServletResponse.class);
SUPPORTED_TYPES.add(HttpServletResponse.class);
SUPPORTED_TYPES.add(HttpSession.class);
SUPPORTED_TYPES.add(Cookie.class);
SUPPORTED_TYPES.add(Cookie[].class);
SUPPORTED_TYPES.add(Reader.class);
SUPPORTED_TYPES.add(Writer.class);
}
@Override
public boolean accept(ParameterMeta parameter) {
return SUPPORTED_TYPES.contains(parameter.getActualType());
}
@Override
public Object resolve(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
Class<?> type = parameter.getActualType();
if (type == ServletRequest.class || type == HttpServletRequest.class) {
return request;
}
if (type == ServletResponse.class || type == HttpServletResponse.class) {
return response;
}
if (type == HttpSession.class) {
return ((HttpServletRequest) request).getSession();
}
if (type == Cookie.class) {
return Helper.convert(request.cookie(parameter.getRequiredName()));
}
if (type == Cookie[].class) {
return ((HttpServletRequest) request).getCookies();
}
if (type == Reader.class) {
try {
return ((HttpServletRequest) request).getReader();
} catch (IOException e) {
throw new RestException(e);
}
}
if (type == Writer.class) {
try {
return ((HttpServletResponse) response).getWriter();
} catch (IOException e) {
throw new RestException(e);
}
}
return null;
}
}

View File

@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpChannel;
import org.apache.dubbo.remoting.http12.HttpMetadata;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.remoting.http12.message.HttpMessageAdapterFactory;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.filter.RestExtension;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
@Activate(order = -100, onClass = "javax.servlet.http.HttpServletRequest")
public final class ServletHttpMessageAdapterFactory
implements HttpMessageAdapterFactory<ServletHttpRequestAdaptee, HttpMetadata, Void> {
private final FrameworkModel frameworkModel;
private final ServletContext servletContext;
private final HttpSessionFactory httpSessionFactory;
public ServletHttpMessageAdapterFactory(FrameworkModel frameworkModel) {
this.frameworkModel = frameworkModel;
servletContext = new DummyServletContext(frameworkModel);
httpSessionFactory = getHttpSessionFactory(frameworkModel);
}
private HttpSessionFactory getHttpSessionFactory(FrameworkModel frameworkModel) {
for (RestExtension extension : frameworkModel.getActivateExtensions(RestExtension.class)) {
if (extension instanceof HttpSessionFactory) {
return (HttpSessionFactory) extension;
}
}
return null;
}
@Override
public ServletHttpRequestAdaptee adaptRequest(HttpMetadata rawRequest, HttpChannel channel) {
return new ServletHttpRequestAdaptee(rawRequest, channel, servletContext, httpSessionFactory);
}
@Override
public HttpResponse adaptResponse(ServletHttpRequestAdaptee request, HttpMetadata rawRequest, Void rawResponse) {
return new ServletHttpResponseAdaptee();
}
public FilterConfig adaptFilterConfig(String filterName) {
return new DummyFilterConfig(filterName, frameworkModel, servletContext);
}
}

View File

@ -0,0 +1,491 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.http12.HttpChannel;
import org.apache.dubbo.remoting.http12.HttpMetadata;
import org.apache.dubbo.remoting.http12.message.DefaultHttpRequest;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.ReadListener;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.Principal;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class ServletHttpRequestAdaptee extends DefaultHttpRequest implements HttpServletRequest {
private final ServletContext servletContext;
private final HttpSessionFactory sessionFactory;
private ServletInputStream sis;
private BufferedReader reader;
public ServletHttpRequestAdaptee(
HttpMetadata metadata,
HttpChannel channel,
ServletContext servletContext,
HttpSessionFactory sessionFactory) {
super(metadata, channel);
this.servletContext = servletContext;
this.sessionFactory = sessionFactory;
}
@Override
public String getAuthType() {
return header("www-authenticate");
}
@Override
public Cookie[] getCookies() {
return Helper.convertCookies(cookies());
}
@Override
public long getDateHeader(String name) {
Date date = dateHeader(name);
return date == null ? -1L : date.getTime();
}
@Override
public String getHeader(String name) {
return header(name);
}
@Override
public Enumeration<String> getHeaders(String name) {
return Collections.enumeration(headerValues(name));
}
@Override
public Enumeration<String> getHeaderNames() {
return Collections.enumeration(headerNames());
}
@Override
public int getIntHeader(String name) {
String headerValue = getHeader(name);
try {
return Integer.parseInt(headerValue);
} catch (NumberFormatException e) {
return -1;
}
}
@Override
public String getMethod() {
return method();
}
@Override
public String getPathInfo() {
return null;
}
@Override
public String getPathTranslated() {
return null;
}
@Override
public String getContextPath() {
return "/";
}
@Override
public String getQueryString() {
return query();
}
@Override
public String getRemoteUser() {
throw new UnsupportedOperationException();
}
@Override
public boolean isUserInRole(String role) {
throw new UnsupportedOperationException();
}
@Override
public Principal getUserPrincipal() {
throw new UnsupportedOperationException();
}
@Override
public String getRequestedSessionId() {
throw new UnsupportedOperationException();
}
@Override
public String getRequestURI() {
return path();
}
@Override
public StringBuffer getRequestURL() {
StringBuffer url = new StringBuffer(32);
String scheme = getScheme();
int port = getServerPort();
url.append(scheme).append("://").append(getServerName());
if (("http".equals(scheme) && port != 80) || ("https".equals(scheme) && port != 443)) {
url.append(':');
url.append(port);
}
url.append(path());
return url;
}
@Override
public String getServletPath() {
return path();
}
@Override
public HttpSession getSession(boolean create) {
if (sessionFactory == null) {
throw new UnsupportedOperationException("No HttpSessionFactory found");
}
return sessionFactory.getSession(this, create);
}
@Override
public HttpSession getSession() {
return getSession(true);
}
@Override
public String changeSessionId() {
return null;
}
@Override
public boolean isRequestedSessionIdValid() {
return true;
}
@Override
public boolean isRequestedSessionIdFromCookie() {
return true;
}
@Override
public boolean isRequestedSessionIdFromURL() {
return false;
}
@Override
public boolean isRequestedSessionIdFromUrl() {
return false;
}
@Override
public boolean authenticate(HttpServletResponse response) {
throw new UnsupportedOperationException();
}
@Override
public void login(String username, String password) {
throw new UnsupportedOperationException();
}
@Override
public void logout() {
throw new UnsupportedOperationException();
}
@Override
public Collection<Part> getParts() {
return Helper.convertParts(parts());
}
@Override
public Part getPart(String name) {
return Helper.convert(part(name));
}
@Override
public <T extends HttpUpgradeHandler> T upgrade(Class<T> handlerClass) {
throw new UnsupportedOperationException();
}
@Override
public Object getAttribute(String name) {
return attribute(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(attributeNames());
}
@Override
public String getCharacterEncoding() {
return charset();
}
@Override
public void setCharacterEncoding(String env) {
setCharset(env);
}
@Override
public int getContentLength() {
return contentLength();
}
@Override
public long getContentLengthLong() {
return contentLength();
}
@Override
public String getContentType() {
return contentType();
}
@Override
public ServletInputStream getInputStream() {
if (sis == null) {
sis = new HttpInputStream(inputStream());
}
return sis;
}
@Override
public String getParameter(String name) {
return parameter(name);
}
@Override
public Enumeration<String> getParameterNames() {
return Collections.enumeration(parameterNames());
}
@Override
public String[] getParameterValues(String name) {
List<String> values = parameterValues(name);
return values == null ? null : values.toArray(new String[0]);
}
@Override
public Map<String, String[]> getParameterMap() {
Collection<String> paramNames = parameterNames();
if (paramNames.isEmpty()) {
return Collections.emptyMap();
}
Map<String, String[]> result = CollectionUtils.newLinkedHashMap(paramNames.size());
for (String paramName : paramNames) {
result.put(paramName, getParameterValues(paramName));
}
return result;
}
@Override
public String getProtocol() {
return isHttp2() ? "HTTP/2.0" : "HTTP/1.1";
}
@Override
public String getScheme() {
return scheme();
}
@Override
public String getServerName() {
return serverName();
}
@Override
public int getServerPort() {
return serverPort();
}
@Override
public BufferedReader getReader() {
if (reader == null) {
reader = new BufferedReader(new InputStreamReader(inputStream(), charsetOrDefault()));
}
return reader;
}
@Override
public String getRemoteAddr() {
return remoteAddr();
}
@Override
public String getRemoteHost() {
return String.valueOf(remotePort());
}
@Override
public Locale getLocale() {
return locale();
}
@Override
public Enumeration<Locale> getLocales() {
return Collections.enumeration(locales());
}
@Override
public boolean isSecure() {
return "https".equals(scheme());
}
@Override
public RequestDispatcher getRequestDispatcher(String path) {
throw new UnsupportedOperationException();
}
@Override
public String getRealPath(String path) {
return null;
}
@Override
public int getRemotePort() {
return remotePort();
}
@Override
public String getLocalName() {
return localHost();
}
@Override
public String getLocalAddr() {
return localAddr();
}
@Override
public int getLocalPort() {
return localPort();
}
@Override
public ServletContext getServletContext() {
return servletContext;
}
@Override
public AsyncContext startAsync() throws IllegalStateException {
throw new IllegalStateException();
}
@Override
public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
throws IllegalStateException {
throw new IllegalStateException();
}
@Override
public boolean isAsyncStarted() {
return false;
}
@Override
public boolean isAsyncSupported() {
return false;
}
@Override
public AsyncContext getAsyncContext() {
throw new IllegalStateException();
}
@Override
public DispatcherType getDispatcherType() {
return DispatcherType.REQUEST;
}
@Override
public String toString() {
return "ServletHttpRequestAdaptee{" + fieldToString() + '}';
}
private static final class HttpInputStream extends ServletInputStream {
private final InputStream is;
HttpInputStream(InputStream is) {
this.is = is;
}
@Override
public int read() throws IOException {
return is.read();
}
@Override
public int read(byte[] b) throws IOException {
return is.read(b);
}
@Override
public void close() throws IOException {
is.close();
}
@Override
public int readLine(byte[] b, int off, int len) throws IOException {
return is.read(b, off, len);
}
@Override
public boolean isFinished() {
try {
return is.available() == 0;
} catch (IOException e) {
return false;
}
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -0,0 +1,221 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.servlet;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.http12.HttpUtils;
import org.apache.dubbo.remoting.http12.message.DefaultHttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
public class ServletHttpResponseAdaptee extends DefaultHttpResponse implements HttpServletResponse {
private ServletOutputStream sos;
private PrintWriter writer;
@Override
public void addCookie(Cookie cookie) {
addCookie(Helper.convert(cookie));
}
@Override
public boolean containsHeader(String name) {
return hasHeader(name);
}
@Override
public String encodeURL(String url) {
return RequestUtils.encodeURL(url);
}
@Override
public String encodeRedirectURL(String url) {
return RequestUtils.encodeURL(url);
}
@Override
public String encodeUrl(String url) {
return RequestUtils.encodeURL(url);
}
@Override
public String encodeRedirectUrl(String url) {
return RequestUtils.encodeURL(url);
}
@Override
public void setDateHeader(String name, long date) {
setHeader(name, new Date(date));
}
@Override
public void addDateHeader(String name, long date) {
addHeader(name, new Date(date));
}
@Override
public void setIntHeader(String name, int value) {
setHeader(name, String.valueOf(value));
}
@Override
public void addIntHeader(String name, int value) {
addHeader(name, String.valueOf(value));
}
@Override
public void setStatus(int sc, String sm) {
setStatus(sc);
setBody(sm);
}
@Override
public int getStatus() {
return status();
}
@Override
public String getHeader(String name) {
return header(name);
}
@Override
public Collection<String> getHeaders(String name) {
return headerValues(name);
}
@Override
public Collection<String> getHeaderNames() {
return headerNames();
}
@Override
public String getCharacterEncoding() {
return charset();
}
@Override
public String getContentType() {
return contentType();
}
@Override
public ServletOutputStream getOutputStream() {
if (sos == null) {
sos = new HttpOutputStream(outputStream());
}
return sos;
}
@Override
public PrintWriter getWriter() {
if (writer == null) {
writer = new PrintWriter(getOutputStream());
}
return writer;
}
@Override
public void setCharacterEncoding(String charset) {
setCharset(charset);
}
@Override
public void setContentLength(int len) {}
@Override
public void setContentLengthLong(long len) {}
@Override
public void setBufferSize(int size) {}
@Override
public int getBufferSize() {
return 0;
}
@Override
public void flushBuffer() throws IOException {
//noinspection resource
OutputStream os = outputStream();
if (os instanceof BufferedOutputStream) {
os.flush();
}
}
@Override
public void setLocale(Locale loc) {
setLocale(loc.toLanguageTag());
}
@Override
public Locale getLocale() {
Locale locale = CollectionUtils.first(HttpUtils.parseContentLanguage(locale()));
return locale == null ? Locale.getDefault() : locale;
}
@Override
public String toString() {
return "ServletHttpResponseAdaptee{" + fieldToString() + '}';
}
private static final class HttpOutputStream extends ServletOutputStream {
private final OutputStream outputStream;
private HttpOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
@Override
public void write(int b) throws IOException {
outputStream.write(b);
}
@Override
public void write(byte[] b) throws IOException {
outputStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
outputStream.write(b, off, len);
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
throw new UnsupportedOperationException();
}
}
}

View File

@ -0,0 +1 @@
servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletHttpMessageAdapterFactory

View File

@ -0,0 +1 @@
servlet=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.ServletArgumentResolver

View File

@ -0,0 +1 @@
servlet-filter=org.apache.dubbo.rpc.protocol.tri.rest.support.servlet.FilterAdapter

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF 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.
-->
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="%d{HH:mm:ss.SSS} |-%highlight{%-5p} [%t] %40.40c:%-3L -| %m%n%rEx{filters(jdk.internal.reflect,java.lang.reflect,sun.reflect,org.junit,org.mockito)}" charset="UTF-8"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils" level="error"/>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to the Apache Software Foundation (ASF) under one or more
~ contributor license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright ownership.
~ The ASF 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-plugin</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>dubbo-rest-spring</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-triple</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-config-spring</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rest-servlet</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-remoting-netty4</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-rpc-rest</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-jaxrs</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractAnnotationBaseArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import java.lang.annotation.Annotation;
public abstract class AbstractSpringArgumentResolver extends AbstractAnnotationBaseArgumentResolver {
@Override
protected NamedValueMeta createNamedValueMeta(ParameterMeta param, AnnotationMeta<Annotation> ann) {
return new NamedValueMeta(ann.getValue(), Helper.isRequired(ann), Helper.defaultValue(ann));
}
}

View File

@ -0,0 +1,62 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationEnum;
import java.lang.annotation.Annotation;
public enum Annotations implements AnnotationEnum {
RequestMapping,
PathVariable,
MatrixVariable,
RequestParam,
RequestHeader,
CookieValue,
RequestAttribute,
RequestPart,
RequestBody,
ModelAttribute,
ResponseStatus,
CrossOrigin,
ExceptionHandler,
Nonnull("javax.annotation.Nonnull");
private final String className;
private Class<Annotation> type;
Annotations() {
className = "org.springframework.web.bind.annotation." + name();
}
Annotations(String className) {
this.className = className;
}
@Override
public String className() {
return className;
}
@Override
public Class<Annotation> type() {
if (type == null) {
type = loadType();
}
return type;
}
}

View File

@ -0,0 +1,158 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Map;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.convert.ConversionService;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.WebDataBinder;
final class BeanArgumentBinder {
private final ArgumentResolver argumentResolver;
private final ConversionService conversionService;
private final Map<Class<?>, ConstructorMeta> cache = CollectionUtils.newConcurrentHashMap();
BeanArgumentBinder(FrameworkModel frameworkModel, ConversionService conversionService) {
ScopeBeanFactory beanFactory = frameworkModel.getBeanFactory();
argumentResolver = beanFactory.getOrRegisterBean(CompositeArgumentResolver.class);
this.conversionService = conversionService;
}
public Object bind(ParameterMeta parameter, HttpRequest request, HttpResponse response) {
String name = StringUtils.defaultIf(parameter.getName(), DataBinder.DEFAULT_OBJECT_NAME);
try {
Object bean = buildBean(parameter, request, response);
WebDataBinder binder = new WebDataBinder(bean, name);
binder.setConversionService(conversionService);
binder.bind(new MutablePropertyValues(RequestUtils.getParametersMap(request)));
BindingResult result = binder.getBindingResult();
if (result.hasErrors()) {
throw new BindException(result);
}
return binder.getTarget();
} catch (Exception e) {
throw new RestException(e, Messages.ARGUMENT_BIND_ERROR, name, parameter.getType());
}
}
private Object buildBean(ParameterMeta parameter, HttpRequest request, HttpResponse response) throws Exception {
Class<?> type = parameter.getActualType();
if (Modifier.isAbstract(type.getModifiers())) {
throw new IllegalStateException(Messages.ARGUMENT_COULD_NOT_RESOLVED.format(parameter.getDescription()));
}
ConstructorMeta ct = cache.computeIfAbsent(type, k -> resolveConstructor(parameter.getToolKit(), k));
ParameterMeta[] parameters = ct.parameters;
int len = parameters.length;
Object[] args = new Object[len];
for (int i = 0; i < len; i++) {
args[i] = argumentResolver.resolve(parameters[i], request, response);
}
return ct.newInstance(args);
}
private ConstructorMeta resolveConstructor(RestToolKit toolKit, Class<?> type) {
Constructor<?>[] constructors = type.getConstructors();
Constructor<?> ct = null;
if (constructors.length == 1) {
ct = constructors[0];
} else {
try {
ct = type.getDeclaredConstructor();
} catch (NoSuchMethodException ignored) {
}
}
if (ct == null) {
throw new IllegalArgumentException("No available default constructor found in " + type);
}
return new ConstructorMeta(toolKit, ct);
}
private static final class ConstructorMeta {
private final Constructor<?> constructor;
private final ConstructorParameterMeta[] parameters;
ConstructorMeta(RestToolKit toolKit, Constructor<?> constructor) {
this.constructor = constructor;
parameters = initParameters(toolKit, constructor);
}
private ConstructorParameterMeta[] initParameters(RestToolKit toolKit, Constructor<?> ct) {
Parameter[] cps = ct.getParameters();
int len = cps.length;
ConstructorParameterMeta[] parameters = new ConstructorParameterMeta[len];
for (int i = 0; i < len; i++) {
parameters[i] = new ConstructorParameterMeta(toolKit, cps[i]);
}
return parameters;
}
Object newInstance(Object[] args) throws Exception {
return constructor.newInstance(args);
}
}
public static final class ConstructorParameterMeta extends ParameterMeta {
private final Parameter parameter;
ConstructorParameterMeta(RestToolKit toolKit, Parameter parameter) {
super(toolKit, parameter.isNamePresent() ? parameter.getName() : null);
this.parameter = parameter;
}
@Override
protected AnnotatedElement getAnnotatedElement() {
return parameter;
}
@Override
public Class<?> getType() {
return parameter.getType();
}
@Override
public Type getGenericType() {
return parameter.getParameterizedType();
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.Properties;
@SuppressWarnings("serial")
public final class ConfigurationWrapper extends Properties {
private final Configuration configuration;
ConfigurationWrapper(ApplicationModel applicationModel) {
configuration = applicationModel.modelEnvironment().getConfiguration();
}
@Override
public String getProperty(String key) {
Object value = configuration.getProperty(key);
return value == null ? null : value.toString();
}
@Override
public Object get(Object key) {
return configuration.getProperty(key == null ? null : key.toString());
}
}

View File

@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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 org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.remoting.http12.HttpCookie;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@Activate(onClass = "org.springframework.web.bind.annotation.CookieValue")
public class CookieValueArgumentResolver extends AbstractSpringArgumentResolver {
@Override
public Class<Annotation> accept() {
return Annotations.CookieValue.type();
}
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return request.cookie(meta.name());
}
@Override
protected Object resolveCollectionValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Collection<HttpCookie> cookies = request.cookies();
if (cookies.isEmpty()) {
return Collections.emptyList();
}
String name = meta.name();
List<HttpCookie> result = new ArrayList<>(cookies.size());
for (HttpCookie cookie : cookies) {
if (name.equals(cookie.name())) {
result.add(cookie);
}
}
return result;
}
@Override
protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
Collection<HttpCookie> cookies = request.cookies();
if (cookies.isEmpty()) {
return Collections.emptyMap();
}
Map<String, List<HttpCookie>> mapValue = CollectionUtils.newLinkedHashMap(cookies.size());
for (HttpCookie cookie : cookies) {
mapValue.computeIfAbsent(cookie.name(), k -> new ArrayList<>()).add(cookie);
}
return mapValue;
}
}

Some files were not shown because too many files have changed in this diff Show More