diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java index 95ef839b87..89d1861dc5 100644 --- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java +++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java @@ -25,16 +25,23 @@ import org.apache.dubbo.rpc.protocol.tri.rest.util.DefaultRestToolKit; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.ParamConverter; + +import java.util.Optional; final class JaxrsRestToolKit extends DefaultRestToolKit { private final BeanArgumentBinder binder; + private final ParamConverterFactory paramConverterFactory; + public JaxrsRestToolKit(FrameworkModel frameworkModel) { super(frameworkModel); binder = new BeanArgumentBinder(frameworkModel); + paramConverterFactory = new ParamConverterFactory(); } + @SuppressWarnings({"unchecked", "rawtypes"}) @Override public Object convert(Object value, ParameterMeta parameter) { if (MultivaluedMap.class.isAssignableFrom(parameter.getType())) { @@ -43,6 +50,19 @@ final class JaxrsRestToolKit extends DefaultRestToolKit { } return typeConverter.convert(value, MultivaluedHashMap.class); } + + Optional optional = paramConverterFactory.getParamConverter( + parameter.getType(), parameter.getGenericType(), parameter.getRealAnnotations()); + if (optional.isPresent()) { + ParamConverter paramConverter = optional.get(); + Object result = value.getClass() == String.class + ? paramConverter.fromString((String) value) + : paramConverter.toString(value); + if (result != null) { + return result; + } + } + return super.convert(value, parameter); } diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java new file mode 100644 index 0000000000..bef0a63b90 --- /dev/null +++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/ParamConverterFactory.java @@ -0,0 +1,73 @@ +/* + * 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.logger.ErrorTypeAwareLogger; +import org.apache.dubbo.common.logger.LoggerFactory; +import org.apache.dubbo.common.utils.CollectionUtils; +import org.apache.dubbo.common.utils.Pair; + +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.ServiceLoader; + +import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_ERROR_LOAD_EXTENSION; + +@SuppressWarnings({"rawtypes"}) +public class ParamConverterFactory { + + private static final ErrorTypeAwareLogger logger = + LoggerFactory.getErrorTypeAwareLogger(ParamConverterFactory.class); + private final Map, Type>, Annotation[]>, Optional> cache = + CollectionUtils.newConcurrentHashMap(); + private final List providers = new ArrayList<>(); + + ParamConverterFactory() { + ServiceLoader serviceLoader = ServiceLoader.load(ParamConverterProvider.class); + Iterator iterator = serviceLoader.iterator(); + while (iterator.hasNext()) { + try { + ParamConverterProvider paramConverterProvider = iterator.next(); + providers.add(paramConverterProvider); + } catch (Throwable e) { + logger.error(COMMON_ERROR_LOAD_EXTENSION, "", "", "Spi Fail to load ParamConverterProvider"); + } + } + } + + public Optional getParamConverter( + Class rawType, Type genericType, Annotation[] annotations) { + Pair, Type>, Annotation[]> pair = Pair.of(Pair.of(rawType, genericType), annotations); + return cache.computeIfAbsent(pair, k -> { + for (ParamConverterProvider provider : providers) { + ParamConverter converter = provider.getConverter(rawType, genericType, annotations); + if (converter != null) { + return Optional.of(converter); + } + } + return Optional.empty(); + }); + } +} diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java index fa4b681843..f46c31310a 100644 --- a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java +++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/JaxrsRestProtocolTest.java @@ -39,6 +39,8 @@ import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.Anot 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.ParamConverterService; +import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.ParamConverterServiceImpl; 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; @@ -562,4 +564,24 @@ class JaxrsRestProtocolTest { repository.registerProvider(providerModel); return url.setServiceModel(providerModel); } + + @Test + void testParamConverter() { + ParamConverterService service = new ParamConverterServiceImpl(); + URL exportUrl = URL.valueOf( + "tri://127.0.0.1:" + availablePort + "/rest?interface=" + ParamConverterService.class.getName()); + + URL nettyUrl = this.registerProvider(exportUrl, service, ParamConverterService.class); + + tProtocol.export(proxy.getInvoker(service, ParamConverterService.class, nettyUrl)); + + ParamConverterService paramConverterService = + this.proxy.getProxy(protocol.refer(ParamConverterService.class, nettyUrl)); + + User user = paramConverterService.convert(User.getInstance()); + User u = new User(); + u.setAge(20); + u.setId(1L); + Assertions.assertEquals(u, user); + } } diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterProviderImpl.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterProviderImpl.java new file mode 100644 index 0000000000..24d0a472cb --- /dev/null +++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterProviderImpl.java @@ -0,0 +1,51 @@ +/* + * 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.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +public class ParamConverterProviderImpl implements ParamConverterProvider { + + @Override + public ParamConverter getConverter(Class rawType, Type type, Annotation[] annotations) { + if (rawType.isAssignableFrom(User.class)) { + return (ParamConverter) new UserParamConverter(); + } + return null; + } + + static class UserParamConverter implements ParamConverter { + @Override + public User fromString(String param) { + User user = new User(); + user.setId(1L); + user.setAge(20); + return user; + } + + @Override + public String toString(User user) { + return "User{" + "id=" + user.getId() + ", name='" + user.getName() + '\'' + ", age=" + user.getAge() + '}'; + } + } +} diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java new file mode 100644 index 0000000000..fa1c20c12d --- /dev/null +++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterService.java @@ -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.compatible.rest; + +import org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.User; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; + +@Path("/ParamConverterService") +public interface ParamConverterService { + + @GET() + @Path("/convert") + User convert(@QueryParam("user") User user); +} diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java new file mode 100644 index 0000000000..2b0bedfd21 --- /dev/null +++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/compatible/rest/ParamConverterServiceImpl.java @@ -0,0 +1,26 @@ +/* + * 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; + +public class ParamConverterServiceImpl implements ParamConverterService { + @Override + public User convert(User user) { + return user; + } +} diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider b/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider new file mode 100644 index 0000000000..b794deb158 --- /dev/null +++ b/dubbo-plugin/dubbo-rest-jaxrs/src/test/resources/META-INF/services/javax.ws.rs.ext.ParamConverterProvider @@ -0,0 +1 @@ +org.apache.dubbo.rpc.protocol.tri.rest.support.jaxrs.compatible.rest.ParamConverterProviderImpl