【OSPP】Json check compatiblity (#12910)
* Initialize * update code style * delete redundant file * Change position * delete redundant import * Add JsonCompatibilityUtils * Delete unused import * change funcs * Add test case * update color class * add license * fix style * check interface * Fix log style * Add attribbute of check-json-level * Update log style * Update log style * Fix bugs * Add test and document * Fix bugs * Delete yaml * Delete xml * Add license * Add license * Merge upstream branch * Update IllegalStateException
This commit is contained in:
parent
679ce4496c
commit
f91246ab09
|
|
@ -128,6 +128,8 @@ public interface CommonConstants {
|
|||
|
||||
String THREAD_POOL_EXHAUSTED_LISTENERS_KEY = "thread-pool-exhausted-listeners";
|
||||
|
||||
String JSON_CHECK_LEVEL_KEY = "jsonCheckLevel";
|
||||
|
||||
String THREADS_KEY = "threads";
|
||||
|
||||
String QUEUES_KEY = "queues";
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.JSON_CHECK_LEVEL_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.SSL_ENABLED_KEY;
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.THREAD_POOL_EXHAUSTED_LISTENERS_KEY;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.COMMON_UNEXPECTED_EXCEPTION;
|
||||
|
|
@ -229,6 +230,8 @@ public class ProtocolConfig extends AbstractConfig {
|
|||
*/
|
||||
private String extProtocol;
|
||||
|
||||
private String jsonCheckLevel;
|
||||
|
||||
public ProtocolConfig() {
|
||||
}
|
||||
|
||||
|
|
@ -331,6 +334,15 @@ public class ProtocolConfig extends AbstractConfig {
|
|||
this.threadname = threadname;
|
||||
}
|
||||
|
||||
@Parameter(key = JSON_CHECK_LEVEL_KEY)
|
||||
public String getJsonCheckLevel() {
|
||||
return jsonCheckLevel;
|
||||
}
|
||||
|
||||
public void setJsonCheckLevel(String jsonCheckLevel) {
|
||||
this.jsonCheckLevel = jsonCheckLevel;
|
||||
}
|
||||
|
||||
@Parameter(key = THREAD_POOL_EXHAUSTED_LISTENERS_KEY)
|
||||
public String getThreadPoolExhaustedListeners() {
|
||||
return threadPoolExhaustedListeners;
|
||||
|
|
|
|||
|
|
@ -1692,6 +1692,11 @@
|
|||
<xsd:documentation><![CDATA[ The protocol context path. ]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="json-check-level" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[ The level of json compatibility check. ]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="register" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[ The protocol can be register to registry. ]]></xsd:documentation>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.apache.dubbo.rpc.protocol.rest;
|
|||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.utils.ConcurrentHashMapUtils;
|
||||
import org.apache.dubbo.common.utils.JsonCompatibilityUtil;
|
||||
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
|
||||
import org.apache.dubbo.remoting.api.pu.DefaultPuHandler;
|
||||
import org.apache.dubbo.remoting.exchange.PortUnificationExchanger;
|
||||
|
|
@ -37,6 +38,7 @@ import org.apache.dubbo.rpc.protocol.rest.deploy.ServiceDeployerManager;
|
|||
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
|
@ -47,6 +49,9 @@ import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY;
|
|||
import static org.apache.dubbo.common.constants.CommonConstants.REST_SERVICE_DEPLOYER_URL_ATTRIBUTE_KEY;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_CLIENT;
|
||||
import static org.apache.dubbo.common.constants.LoggerCodeConstants.PROTOCOL_ERROR_CLOSE_SERVER;
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.JSON_CHECK_LEVEL;
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.JSON_CHECK_LEVEL_STRICT;
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.JSON_CHECK_LEVEL_WARN;
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.PATH_SEPARATOR;
|
||||
|
||||
public class RestProtocol extends AbstractProtocol {
|
||||
|
|
@ -90,6 +95,10 @@ public class RestProtocol extends AbstractProtocol {
|
|||
MetadataResolver.resolveProviderServiceMetadata(url.getServiceModel().getProxyObject().getClass(),
|
||||
url, getContextPath(url));
|
||||
|
||||
// check json compatibility
|
||||
String jsonCheckLevel = url.getUrlParam().getParameter(JSON_CHECK_LEVEL);
|
||||
checkJsonCompatibility(invoker.getInterface(), jsonCheckLevel);
|
||||
|
||||
|
||||
// deploy service
|
||||
URL newURL = ServiceDeployerManager.deploy(url, serviceRestMetadata, invoker);
|
||||
|
|
@ -113,6 +122,29 @@ public class RestProtocol extends AbstractProtocol {
|
|||
return exporter;
|
||||
}
|
||||
|
||||
private void checkJsonCompatibility(Class<?> clazz, String jsonCheckLevel) throws RpcException {
|
||||
|
||||
if (jsonCheckLevel == null || JSON_CHECK_LEVEL_WARN.equals(jsonCheckLevel)) {
|
||||
boolean compatibility = JsonCompatibilityUtil.checkClassCompatibility(clazz);
|
||||
if (!compatibility) {
|
||||
List<String> unsupportedMethods = JsonCompatibilityUtil.getUnsupportedMethods(clazz);
|
||||
assert unsupportedMethods != null;
|
||||
logger.warn("", "", "", String.format("Interface %s does not support json serialization, the specific methods are %s.", clazz.getName(), unsupportedMethods));
|
||||
} else {
|
||||
logger.debug("Check json compatibility complete, all methods of {} can be serialized using json.", clazz.getName());
|
||||
}
|
||||
} else if (JSON_CHECK_LEVEL_STRICT.equals(jsonCheckLevel)) {
|
||||
boolean compatibility = JsonCompatibilityUtil.checkClassCompatibility(clazz);
|
||||
if (!compatibility) {
|
||||
List<String> unsupportedMethods = JsonCompatibilityUtil.getUnsupportedMethods(clazz);
|
||||
assert unsupportedMethods != null;
|
||||
throw new IllegalStateException(String.format("Interface %s does not support json serialization, the specific methods are %s.", clazz.getName(), unsupportedMethods));
|
||||
} else {
|
||||
logger.debug("Check json compatibility complete, all methods of {} can be serialized using json.", clazz.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected <T> Invoker<T> protocolBindingRefer(final Class<T> type, final URL url) throws RpcException {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ public interface RestConstant {
|
|||
String IDLE_TIMEOUT_PARAM = "idle.timeout";
|
||||
String KEEP_ALIVE_TIMEOUT_PARAM = "keep.alive.timeout";
|
||||
|
||||
String JSON_CHECK_LEVEL = "jsonCheckLevel";
|
||||
String JSON_CHECK_LEVEL_DISABLED = "disabled";
|
||||
String JSON_CHECK_LEVEL_WARN = "warn";
|
||||
String JSON_CHECK_LEVEL_STRICT = "strict";
|
||||
|
||||
int MAX_REQUEST_SIZE = 1024 * 1024 * 10;
|
||||
int MAX_INITIAL_LINE_LENGTH = 4096;
|
||||
int MAX_HEADER_SIZE = 8192;
|
||||
|
|
|
|||
|
|
@ -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.rest;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.extension.ExtensionLoader;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.rpc.Exporter;
|
||||
import org.apache.dubbo.rpc.Protocol;
|
||||
import org.apache.dubbo.rpc.ProxyFactory;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
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.rest.compatibility.RestDemoService;
|
||||
import org.apache.dubbo.rpc.protocol.rest.compatibility.RestDemoServiceImpl;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.JSON_CHECK_LEVEL;
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.JSON_CHECK_LEVEL_DISABLED;
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.JSON_CHECK_LEVEL_STRICT;
|
||||
import static org.apache.dubbo.rpc.protocol.rest.constans.RestConstant.JSON_CHECK_LEVEL_WARN;
|
||||
|
||||
|
||||
public class JsonCompatibilityCheckTest {
|
||||
|
||||
private final int availablePort = NetUtils.getAvailablePort();
|
||||
private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.compatibility.RestDemoService");
|
||||
private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
|
||||
|
||||
private final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
|
||||
private final ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
|
||||
|
||||
|
||||
@Test
|
||||
public void testJsonCheckDisabled() {
|
||||
|
||||
RestDemoService server = new RestDemoServiceImpl();
|
||||
|
||||
URL url = this.registerProvider(exportUrl, server, RestDemoService.class);
|
||||
|
||||
url = url.addParameter(JSON_CHECK_LEVEL, JSON_CHECK_LEVEL_DISABLED);
|
||||
|
||||
Exporter<RestDemoService> exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, url));
|
||||
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonCheckWarn() {
|
||||
RestDemoService server = new RestDemoServiceImpl();
|
||||
|
||||
URL url = this.registerProvider(exportUrl, server, RestDemoService.class);
|
||||
|
||||
url = url.addParameter(JSON_CHECK_LEVEL, JSON_CHECK_LEVEL_WARN);
|
||||
|
||||
Exporter<RestDemoService> exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, url));
|
||||
|
||||
exporter.unexport();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonCheckStrict() {
|
||||
RestDemoService server = new RestDemoServiceImpl();
|
||||
|
||||
URL url = this.registerProvider(exportUrl, server, RestDemoService.class);
|
||||
|
||||
URL newUrl = url.addParameter(JSON_CHECK_LEVEL, JSON_CHECK_LEVEL_STRICT);
|
||||
|
||||
Assertions.assertThrowsExactly(IllegalStateException.class, () -> {
|
||||
Exporter<RestDemoService> exporter = null;
|
||||
try {
|
||||
exporter = protocol.export(proxy.getInvoker(server, RestDemoService.class, newUrl));
|
||||
} finally {
|
||||
if (exporter != null) 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.rest.compatibility;
|
||||
|
||||
public class Apple implements Fruit {
|
||||
|
||||
@Override
|
||||
public String sayName() {
|
||||
return "This is apple";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.rest.compatibility;
|
||||
|
||||
public interface Fruit {
|
||||
String sayName();
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.rest.compatibility;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
@Path("rest")
|
||||
public interface RestDemoService {
|
||||
@GET
|
||||
@Path("hello")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
String sayHello(String name);
|
||||
|
||||
@GET
|
||||
@Path("hi")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
String sayHi();
|
||||
|
||||
@GET
|
||||
@Path("fruit")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
Fruit sayFruit();
|
||||
|
||||
@GET
|
||||
@Path("apple")
|
||||
@Consumes({MediaType.APPLICATION_JSON})
|
||||
Apple sayApple();
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.rest.compatibility;
|
||||
|
||||
public class RestDemoServiceImpl implements RestDemoService {
|
||||
@Override
|
||||
public String sayHello(String name) {
|
||||
return "hello";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sayHi() {
|
||||
return "hi";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fruit sayFruit() {
|
||||
return new Apple();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Apple sayApple() {
|
||||
return new Apple();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue