This is an automated email from the ASF dual-hosted git repository. robertlazarski pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git
The following commit(s) were added to refs/heads/master by this push: new 408f35a439 AXIS2-6071 Add new class JSONBasedDefaultDispatcher that skips legacy SOAP code 408f35a439 is described below commit 408f35a439671ab147d552b9441ea5505a26c943 Author: Robert Lazarski <rlazar...@alphatheory.com> AuthorDate: Thu Jul 18 10:49:31 2024 -1000 AXIS2-6071 Add new class JSONBasedDefaultDispatcher that skips legacy SOAP code --- .../apache/axis2/json/factory/JsonConstant.java | 2 + .../apache/axis2/json/gson/JSONMessageHandler.java | 34 +++++- .../gson/rpc/JsonInOnlyRPCMessageReceiver.java | 7 +- .../json/gson/rpc/JsonRpcMessageReceiver.java | 8 +- .../org/apache/axis2/json/gson/rpc/JsonUtils.java | 25 +++-- .../axis2/json/moshi/JSONMessageHandler.java | 32 +++++- .../moshi/rpc/JsonInOnlyRPCMessageReceiver.java | 7 +- .../json/moshi/rpc/JsonRpcMessageReceiver.java | 8 +- .../org/apache/axis2/json/moshi/rpc/JsonUtils.java | 23 ++-- .../org/apache/axis2/deployment/util/Utils.java | 73 +++++++++++++ .../org/apache/axis2/description/AxisService.java | 64 +++++++++++ .../dispatchers/JSONBasedDefaultDispatcher.java | 118 +++++++++++++++++++++ .../springbootdemo/resources-axis2/conf/axis2.xml | 44 +------- src/site/xdoc/docs/json-springboot-userguide.xml | 2 +- 14 files changed, 370 insertions(+), 77 deletions(-) diff --git a/modules/json/src/org/apache/axis2/json/factory/JsonConstant.java b/modules/json/src/org/apache/axis2/json/factory/JsonConstant.java index 9effd0882f..f4ad434861 100644 --- a/modules/json/src/org/apache/axis2/json/factory/JsonConstant.java +++ b/modules/json/src/org/apache/axis2/json/factory/JsonConstant.java @@ -34,6 +34,8 @@ public class JsonConstant { public static final String MOSHI_XML_STREAM_READER = "MoshiXMLStreamReader"; + public static final String JSON_MESSAGE_NAME = "jsonMessageName"; + public static final String XMLNODES = "xmlnodes"; diff --git a/modules/json/src/org/apache/axis2/json/gson/JSONMessageHandler.java b/modules/json/src/org/apache/axis2/json/gson/JSONMessageHandler.java index 7cee12aa4c..f7bfadea3d 100644 --- a/modules/json/src/org/apache/axis2/json/gson/JSONMessageHandler.java +++ b/modules/json/src/org/apache/axis2/json/gson/JSONMessageHandler.java @@ -35,7 +35,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ws.commons.schema.XmlSchema; +import com.google.gson.stream.JsonReader; + import javax.xml.namespace.QName; +import java.io.IOException; import java.util.List; public class JSONMessageHandler extends AbstractHandler { @@ -73,7 +76,6 @@ public class JSONMessageHandler extends AbstractHandler { log.debug("JSON MessageReceiver found, proceeding with the JSON request"); Object tempObj = msgContext.getProperty(JsonConstant.IS_JSON_STREAM); if (tempObj != null) { - log.debug("JSON MessageReceiver found JSON stream, proceeding with the JSON request"); boolean isJSON = Boolean.valueOf(tempObj.toString()); Object o = msgContext.getProperty(JsonConstant.GSON_XML_STREAM_READER); if (o != null) { @@ -83,7 +85,6 @@ public class JSONMessageHandler extends AbstractHandler { gsonXMLStreamReader.initXmlStreamReader(elementQname, schemas, msgContext.getConfigurationContext()); OMXMLParserWrapper stAXOMBuilder = OMXMLBuilderFactory.createStAXOMBuilder(gsonXMLStreamReader); OMElement omElement = stAXOMBuilder.getDocumentElement(); - log.debug("GsonXMLStreamReader found elementQname: " + elementQname); msgContext.getEnvelope().getBody().addChild(omElement); } else { log.error("GsonXMLStreamReader is null"); @@ -94,7 +95,34 @@ public class JSONMessageHandler extends AbstractHandler { } } } else { - log.debug("Axis operation is null, message hasn't been dispatched to operation, ignore it"); + String enableJSONOnly = (String) msgContext.getAxisService().getParameterValue("enableJSONOnly"); + if (enableJSONOnly !=null && enableJSONOnly.equalsIgnoreCase("true")) { + log.warn("On enableJSONOnly=true Axis operation is null on JSON request, message hasn't been dispatched to an operation, proceeding on JSON message name discovery and AxisOperation mapping"); + try{ + Object tempObj = msgContext.getProperty(JsonConstant.IS_JSON_STREAM); + if (tempObj != null) { + boolean isJSON = Boolean.valueOf(tempObj.toString()); + Object o = msgContext.getProperty(JsonConstant.MOSHI_XML_STREAM_READER); + if (o != null) { + GsonXMLStreamReader gsonXMLStreamReader = (GsonXMLStreamReader) o; + JsonReader jsonReader = gsonXMLStreamReader.getJsonReader(); + jsonReader.beginObject(); + String messageName=jsonReader.nextName(); // get message name from input json stream + if (messageName == null) { + log.error("JSONMessageHandler can't find messageName: " +messageName); + throw new IOException("Bad Request"); + } else { + log.warn("JSONMessageHandler found messageName: " +messageName); + msgContext.setProperty("jsonMessageName", messageName); + } + } + } + } catch(Exception e){ + log.error("JSONMessageHandler error: " +e.getMessage()); + } + } else { + log.warn("On enableJSONOnly=false Axis operation is null, ignore it"); + } } return InvocationResponse.CONTINUE; } diff --git a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java index a77c61726f..5f933e6988 100644 --- a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java +++ b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonInOnlyRPCMessageReceiver.java @@ -58,7 +58,8 @@ public class JsonInOnlyRPCMessageReceiver extends RPCInOnlyMessageReceiver { AxisOperation op = inMessage.getOperationContext().getAxisOperation(); String operation = op.getName().getLocalPart(); log.debug("JsonInOnlyRPCMessageReceiver.invokeBusinessLogic() executing invokeService() with operation: " + operation); - invokeService(jsonReader, serviceObj, operation); + String enableJSONOnly = (String) inMessage.getAxisService().getParameterValue("enableJSONOnly"); + invokeService(jsonReader, serviceObj, operation, enableJSONOnly); } else { throw new AxisFault("GsonXMLStreamReader should have put as a property of messageContext " + "to evaluate JSON message"); @@ -68,7 +69,7 @@ public class JsonInOnlyRPCMessageReceiver extends RPCInOnlyMessageReceiver { } } - public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name) throws AxisFault { + public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name, String enableJSONOnly) throws AxisFault { String msg; Class implClass = serviceObj.getClass(); Method[] allMethods = implClass.getDeclaredMethods(); @@ -76,7 +77,7 @@ public class JsonInOnlyRPCMessageReceiver extends RPCInOnlyMessageReceiver { Class[] paramClasses = method.getParameterTypes(); try { int paramCount = paramClasses.length; - JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount); + JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount, enableJSONOnly); } catch (IllegalAccessException e) { msg = "Does not have access to " + "the definition of the specified class, field, method or constructor"; diff --git a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java index 7cd29e2fc0..e13dd99c8b 100644 --- a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java +++ b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonRpcMessageReceiver.java @@ -57,7 +57,8 @@ public class JsonRpcMessageReceiver extends RPCMessageReceiver { Object serviceObj = getTheImplementationObject(inMessage); AxisOperation op = inMessage.getOperationContext().getAxisOperation(); String operation = op.getName().getLocalPart(); - invokeService(jsonReader, serviceObj, operation , outMessage); + String enableJSONOnly = (String) inMessage.getAxisService().getParameterValue("enableJSONOnly"); + invokeService(jsonReader, serviceObj, operation , outMessage, enableJSONOnly); } else { throw new AxisFault("GsonXMLStreamReader should be put as a property of messageContext " + "to evaluate JSON message"); @@ -67,8 +68,7 @@ public class JsonRpcMessageReceiver extends RPCMessageReceiver { } } - public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name, - MessageContext outMes) throws AxisFault { + public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name, MessageContext outMes, String enableJSONOnly) throws AxisFault { String msg; Class implClass = serviceObj.getClass(); Method[] allMethods = implClass.getDeclaredMethods(); @@ -76,7 +76,7 @@ public class JsonRpcMessageReceiver extends RPCMessageReceiver { Class[] paramClasses = method.getParameterTypes(); try { int paramCount = paramClasses.length; - Object retObj = JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount); + Object retObj = JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount, enableJSONOnly); // handle response outMes.setProperty(JsonConstant.RETURN_OBJECT, retObj); diff --git a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonUtils.java b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonUtils.java index b0cfcf7888..aca3de9ca9 100644 --- a/modules/json/src/org/apache/axis2/json/gson/rpc/JsonUtils.java +++ b/modules/json/src/org/apache/axis2/json/gson/rpc/JsonUtils.java @@ -38,8 +38,8 @@ public class JsonUtils { Object service, Method operation , Class[] paramClasses , - int paramCount ) throws InvocationTargetException, - IllegalAccessException, IOException { + int paramCount, + String enableJSONOnly ) throws InvocationTargetException, IllegalAccessException, IOException { Object[] methodParam = new Object[paramCount]; try { @@ -49,20 +49,27 @@ public class JsonUtils { if( ! jsonReader.isLenient()){ jsonReader.setLenient(true); } - jsonReader.beginObject(); - String messageName=jsonReader.nextName(); // get message name from input json stream - if (messageName == null || !messageName.equals(operation.getName())) { - log.error("JsonUtils.invokeServiceClass() throwing IOException, messageName: " +messageName+ " is unknown, it does not match the axis2 operation, the method name: " + operation.getName()); - throw new IOException("Bad Request"); - } + + if (enableJSONOnly ==null || enableJSONOnly.equalsIgnoreCase("false")) { + log.debug("JsonUtils.invokeServiceClass() detected enableJSONOnly=false, executing jsonReader.beginObject() and then jsonReader.beginArray() on method name: " + operation.getName()); + jsonReader.beginObject(); + String messageName=jsonReader.nextName(); // get message name from input json stream + if (messageName == null || !messageName.equals(operation.getName())) { + log.error("JsonUtils.invokeServiceClass() throwing IOException, messageName: " +messageName+ " is unknown, it does not match the axis2 operation, the method name: " + operation.getName()); + throw new IOException("Bad Request"); + } + } else { + log.debug("JsonUtils.invokeServiceClass() detected enableJSONOnly=true, executing jsonReader.beginArray()"); + } + jsonReader.beginArray(); int i = 0; for (Class paramType : paramClasses) { jsonReader.beginObject(); argNames[i] = jsonReader.nextName(); - log.debug("JsonUtils.invokeServiceClass() on messageName: " +messageName+ " , is currently processing argName: " + argNames[i]); methodParam[i] = gson.fromJson(jsonReader, paramType); // gson handle all types well and return an object from it + log.trace("JsonUtils.invokeServiceClass() completed processing on argNames: " +argNames[i]+ " , methodParam: " +methodParam[i].getClass().getName()+ " , from argNames.length: " + argNames.length); jsonReader.endObject(); i++; } diff --git a/modules/json/src/org/apache/axis2/json/moshi/JSONMessageHandler.java b/modules/json/src/org/apache/axis2/json/moshi/JSONMessageHandler.java index 1b7a1fe181..815a647109 100644 --- a/modules/json/src/org/apache/axis2/json/moshi/JSONMessageHandler.java +++ b/modules/json/src/org/apache/axis2/json/moshi/JSONMessageHandler.java @@ -35,7 +35,10 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ws.commons.schema.XmlSchema; +import com.squareup.moshi.JsonReader; + import javax.xml.namespace.QName; +import java.io.IOException; import java.util.List; public class JSONMessageHandler extends AbstractHandler { @@ -92,7 +95,34 @@ public class JSONMessageHandler extends AbstractHandler { } } } else { - log.debug("Axis operation is null, message hasn't been dispatched to operation, ignore it"); + String enableJSONOnly = (String) msgContext.getAxisService().getParameterValue("enableJSONOnly"); + if (enableJSONOnly !=null && enableJSONOnly.equalsIgnoreCase("true")) { + log.warn("On enableJSONOnly=true Axis operation is null on JSON request, message hasn't been dispatched to an operation, proceeding on JSON message name discovery and AxisOperation mapping"); + try{ + Object tempObj = msgContext.getProperty(JsonConstant.IS_JSON_STREAM); + if (tempObj != null) { + boolean isJSON = Boolean.valueOf(tempObj.toString()); + Object o = msgContext.getProperty(JsonConstant.MOSHI_XML_STREAM_READER); + if (o != null) { + MoshiXMLStreamReader moshiXMLStreamReader = (MoshiXMLStreamReader) o; + JsonReader jsonReader = moshiXMLStreamReader.getJsonReader(); + jsonReader.beginObject(); + String messageName=jsonReader.nextName(); // get message name from input json stream + if (messageName == null) { + log.error("JSONMessageHandler can't find messageName: " +messageName); + throw new IOException("Bad Request"); + } else { + log.warn("JSONMessageHandler found messageName: " +messageName); + msgContext.setProperty("jsonMessageName", messageName); + } + } + } + } catch(Exception e){ + log.error("JSONMessageHandler error: " +e.getMessage()); + } + } else { + log.warn("On enableJSONOnly=false Axis operation is null, ignore it"); + } } return InvocationResponse.CONTINUE; } diff --git a/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonInOnlyRPCMessageReceiver.java b/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonInOnlyRPCMessageReceiver.java index 6f2c35dacc..4514906a8c 100644 --- a/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonInOnlyRPCMessageReceiver.java +++ b/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonInOnlyRPCMessageReceiver.java @@ -58,7 +58,8 @@ public class JsonInOnlyRPCMessageReceiver extends RPCInOnlyMessageReceiver { AxisOperation op = inMessage.getOperationContext().getAxisOperation(); String operation = op.getName().getLocalPart(); log.debug("JsonInOnlyRPCMessageReceiver.invokeBusinessLogic() executing invokeService() with operation: " + operation); - invokeService(jsonReader, serviceObj, operation); + String enableJSONOnly = (String) inMessage.getAxisService().getParameterValue("enableJSONOnly"); + invokeService(jsonReader, serviceObj, operation, enableJSONOnly); } else { throw new AxisFault("MoshiXMLStreamReader should have put as a property of messageContext " + "to evaluate JSON message"); @@ -68,7 +69,7 @@ public class JsonInOnlyRPCMessageReceiver extends RPCInOnlyMessageReceiver { } } - public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name) throws AxisFault { + public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name, String enableJSONOnly) throws AxisFault { String msg; Class implClass = serviceObj.getClass(); Method[] allMethods = implClass.getDeclaredMethods(); @@ -76,7 +77,7 @@ public class JsonInOnlyRPCMessageReceiver extends RPCInOnlyMessageReceiver { Class[] paramClasses = method.getParameterTypes(); try { int paramCount = paramClasses.length; - JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount); + JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount, enableJSONOnly); } catch (IllegalAccessException e) { msg = "Does not have access to " + "the definition of the specified class, field, method or constructor"; diff --git a/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonRpcMessageReceiver.java b/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonRpcMessageReceiver.java index bf90f30f1f..0c74df2010 100644 --- a/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonRpcMessageReceiver.java +++ b/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonRpcMessageReceiver.java @@ -57,7 +57,8 @@ public class JsonRpcMessageReceiver extends RPCMessageReceiver { Object serviceObj = getTheImplementationObject(inMessage); AxisOperation op = inMessage.getOperationContext().getAxisOperation(); String operation = op.getName().getLocalPart(); - invokeService(jsonReader, serviceObj, operation , outMessage); + String enableJSONOnly = (String) inMessage.getAxisService().getParameterValue("enableJSONOnly"); + invokeService(jsonReader, serviceObj, operation , outMessage, enableJSONOnly); } else { throw new AxisFault("MoshiXMLStreamReader should be put as a property of messageContext " + "to evaluate JSON message"); @@ -67,8 +68,7 @@ public class JsonRpcMessageReceiver extends RPCMessageReceiver { } } - public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name, - MessageContext outMes) throws AxisFault { + public void invokeService(JsonReader jsonReader, Object serviceObj, String operation_name, MessageContext outMes, String enableJSONOnly) throws AxisFault { String msg; Class implClass = serviceObj.getClass(); Method[] allMethods = implClass.getDeclaredMethods(); @@ -76,7 +76,7 @@ public class JsonRpcMessageReceiver extends RPCMessageReceiver { Class[] paramClasses = method.getParameterTypes(); try { int paramCount = paramClasses.length; - Object retObj = JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount); + Object retObj = JsonUtils.invokeServiceClass(jsonReader, serviceObj, method, paramClasses, paramCount, enableJSONOnly); // handle response outMes.setProperty(JsonConstant.RETURN_OBJECT, retObj); diff --git a/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonUtils.java b/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonUtils.java index 712f6ce937..84e7fc5da6 100644 --- a/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonUtils.java +++ b/modules/json/src/org/apache/axis2/json/moshi/rpc/JsonUtils.java @@ -49,8 +49,8 @@ public class JsonUtils { Object service, Method operation , Class[] paramClasses , - int paramCount ) throws InvocationTargetException, - IllegalAccessException, IOException { + int paramCount, + String enableJSONOnly ) throws InvocationTargetException, IllegalAccessException, IOException { Object[] methodParam = new Object[paramCount]; try { @@ -102,12 +102,17 @@ public class JsonUtils { Moshi moshiFrom = new Moshi.Builder().add(objectFactory).add(Date.class, new Rfc3339DateJsonAdapter()).build(); String[] argNames = new String[paramCount]; - jsonReader.beginObject(); - String messageName=jsonReader.nextName(); // get message name from input json stream - if (messageName == null || !messageName.equals(operation.getName())) { - log.error("JsonUtils.invokeServiceClass() throwing IOException, messageName: " +messageName+ " is unknown, it does not match the axis2 operation, the method name: " + operation.getName()); - throw new IOException("Bad Request"); - } + if (enableJSONOnly ==null || enableJSONOnly.equalsIgnoreCase("false")) { + log.debug("JsonUtils.invokeServiceClass() detected enableJSONOnly=false, executing jsonReader.beginObject() and then jsonReader.beginArray() on method name: " + operation.getName()); + jsonReader.beginObject(); + String messageName=jsonReader.nextName(); // get message name from input json stream + if (messageName == null || !messageName.equals(operation.getName())) { + log.error("JsonUtils.invokeServiceClass() throwing IOException, messageName: " +messageName+ " is unknown, it does not match the axis2 operation, the method name: " + operation.getName()); + throw new IOException("Bad Request"); + } + } else { + log.debug("JsonUtils.invokeServiceClass() detected enableJSONOnly=true, executing jsonReader.beginArray()"); + } jsonReader.beginArray(); int i = 0; @@ -117,7 +122,7 @@ public class JsonUtils { jsonReader.beginObject(); argNames[i] = jsonReader.nextName(); methodParam[i] = moshiFromJsonAdapter.fromJson(jsonReader); // moshi handles all types well and returns an object from it - log.trace("JsonUtils.invokeServiceClass() completed processing on messageName: " +messageName+ " , arg name: " +argNames[i]+ " , methodParam: " +methodParam[i].getClass().getName()+ " , from argNames.length: " + argNames.length); + log.trace("JsonUtils.invokeServiceClass() completed processing on argNames: " +argNames[i]+ " , methodParam: " +methodParam[i].getClass().getName()+ " , from argNames.length: " + argNames.length); jsonReader.endObject(); i++; } diff --git a/modules/kernel/src/org/apache/axis2/deployment/util/Utils.java b/modules/kernel/src/org/apache/axis2/deployment/util/Utils.java index 88ee519ea9..39b1f28b02 100644 --- a/modules/kernel/src/org/apache/axis2/deployment/util/Utils.java +++ b/modules/kernel/src/org/apache/axis2/deployment/util/Utils.java @@ -27,6 +27,7 @@ import org.apache.axiom.soap.SOAP12Constants; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.jaxrs.JAXRSModel; +import org.apache.axis2.jaxrs.JAXRSUtils; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.deployment.DeploymentClassLoader; import org.apache.axis2.deployment.DeploymentConstants; @@ -60,6 +61,7 @@ import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.XMLStreamException; import java.io.*; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -402,6 +404,77 @@ public class Utils { if (serviceClass == null) { return; } + String enableJSONOnly = (String) axisConfig.getParameterValue("enableJSONOnly"); + if (enableJSONOnly !=null && enableJSONOnly.equalsIgnoreCase("true")) { + log.warn("on enableJSONOnly: " +enableJSONOnly+ " starting fillAxisService(), serviceClass.name: " + serviceClass.getName()); + List<Method> serviceMethods = new ArrayList<Method>(); + Map<String, Method> uniqueMethods = new LinkedHashMap<String, Method>(); + for (Method method : serviceClass.getMethods()) { + if (method.getDeclaringClass() == Object.class) { + continue; + } + if (!Modifier.isPublic(method.getModifiers())) { + // skip non public methods + continue; + } + String methodName = method.getName(); + if (excludeOperations.contains(methodName)) { + continue; + } + boolean addToService = false; + AxisOperation axisOperation = axisService.getOperation(new QName(methodName)); + if (axisOperation == null) { + axisOperation = getAxisOperationForJmethod(method); + axisService.addOperation(axisOperation); + log.warn("on methodName: " +methodName+ " , enableJSONOnly: " +enableJSONOnly+ " , axisOperation added to service: " +axisService.getName()+ " , operation: " + axisOperation.getName().getLocalPart()); + } + // by now axis operation should be assigned but we better recheck & add the paramether + if (axisOperation != null) { + axisOperation.addParameter("JAXRSAnnotaion", JAXRSUtils.getMethodModel(JAXRSUtils.getClassModel(serviceClass), method)); + } + if (method.getDeclaringClass() != Object.class) { + serviceMethods.add(method); + } + } + // The order of the methods returned by getMethods is undefined, but the test cases assume that the + // order is the same on all Java versions. Java 6 seems to use reverse lexical order, so we use that + // here to make things deterministic. + Collections.sort(serviceMethods, new Comparator<Method>() { + public int compare(Method o1, Method o2) { + return -o1.getName().compareTo(o2.getName()); + } + }); + + log.debug("fillAxisService() on enableJSONOnly=true found serviceMethods: " +serviceMethods); + + PhasesInfo pinfo = axisConfig.getPhasesInfo(); + + for (Method jmethod : serviceMethods) { + String opName = jmethod.getName(); + AxisOperation operation = axisService + .getOperation(new QName(opName)); + // if the operation there in services.xml then try to set it schema + // element name + if (operation == null) { + operation = axisService.getOperation(new QName( + jmethod.getName())); + } + MessageReceiver mr = + axisService.getMessageReceiver(operation.getMessageExchangePattern()); + if (mr == null) { + mr = axisConfig.getMessageReceiver(operation.getMessageExchangePattern()); + } + if (operation.getMessageReceiver() == null) { + operation.setMessageReceiver(mr); + } + pinfo.setOperationPhases(operation); + axisService.addOperation(operation); + axisService.addJSONMessageNameToOperationMapping(opName, operation); + } + log.warn("fillAxisService() completed on enableJSONOnly=true , axisService name: " + axisService.getName()); + return; + } + ClassLoader serviceClassLoader = axisService.getClassLoader(); // adding name spaces NamespaceMap map = new NamespaceMap(); diff --git a/modules/kernel/src/org/apache/axis2/description/AxisService.java b/modules/kernel/src/org/apache/axis2/description/AxisService.java index a89454ec3e..6d1c0fe099 100644 --- a/modules/kernel/src/org/apache/axis2/description/AxisService.java +++ b/modules/kernel/src/org/apache/axis2/description/AxisService.java @@ -156,6 +156,12 @@ public class AxisService extends AxisDescription { */ private Map<QName, AxisOperation> messageElementQNameToOperationMap = new HashMap<QName, AxisOperation>(); + /* + * This is a map between the JSON Object name of a message specified in + * the received JSON stream to the Axis2 operations defined in the services.xml file + */ + private Map<String, AxisOperation> jsonMessageNameToOperationMap = new HashMap<String, AxisOperation>(); + private int nsCount = 0; private static final Log log = LogFactory.getLog(AxisService.class); private URL fileName; @@ -3232,6 +3238,64 @@ public class AxisService extends AxisDescription { this.messageElementQNameToOperationMap = messageElementQNameToOperationMap; } + /** + * Look up an AxisOperation for this service based off of a JSON message name + * from the first 'name' read from a JSON message. + * + * @param messageName + * The message name to search for. + * @return The AxisOperation registered to the JSON message name or null if no match was + * found. + * @see #setJSONMessageNameToOperationMap(Map) + */ + public AxisOperation getOperationByJSONMessageName( + String messageName) { + + return (AxisOperation) jsonMessageNameToOperationMap + .get(messageName); + } + + /** + * Set the map of JSON message names as Strings to AxisOperations for this + * service. This map is used during JSON Object name-based routing by + * reading the first name in a JSON message during the transport phase in + * JSONMessageHandler. + * + * @param jsonMessageNameToOperationMap + * The map from JSON message names to AxisOperations. + */ + public void setJSONMessageNameToOperationMap( + Map jsonMessageNameToOperationMap) { + this.jsonMessageNameToOperationMap = jsonMessageNameToOperationMap; + } + + /** + * Add an entry to the map between JSON message names in JSON and + * AxisOperations for this service. + * + * @param messageName + * The message name of the JSON on the first name from the input message that maps to the + * given operation. + * @param operation + * The AxisOperation to be mapped to. + * @see #setJSONMessageNameToOperationMap(Map) + */ + public void addJSONMessageNameToOperationMapping( + String messageName, AxisOperation operation) { + // when setting an operation we have to set it only if the + // messegeName does not + // exist in the map. + if (jsonMessageNameToOperationMap.containsKey(messageName) + && jsonMessageNameToOperationMap.get(messageName) != operation) { + log.error("jsonMessageNameToOperationMap skipping 'put' on messageName: " + messageName + " , containsKey() returned true or value not equal to operation: " + operation.getName().getLocalPart()); + } else { + jsonMessageNameToOperationMap.put(messageName, + operation); + log.debug("jsonMessageNameToOperationMap 'put' on messageName: " + messageName + " with operation: " + operation.getName().getLocalPart()); + } + + } + /** * Look up an AxisOperation for this service based off of an element QName * from a WSDL message element. diff --git a/modules/kernel/src/org/apache/axis2/dispatchers/JSONBasedDefaultDispatcher.java b/modules/kernel/src/org/apache/axis2/dispatchers/JSONBasedDefaultDispatcher.java new file mode 100644 index 0000000000..d281180a26 --- /dev/null +++ b/modules/kernel/src/org/apache/axis2/dispatchers/JSONBasedDefaultDispatcher.java @@ -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.axis2.dispatchers; + +import org.apache.axiom.om.OMNamespace; +import org.apache.axis2.AxisFault; +import org.apache.axis2.context.ConfigurationContext; +import org.apache.axis2.context.MessageContext; +import org.apache.axis2.description.AxisOperation; +import org.apache.axis2.description.AxisService; +import org.apache.axis2.description.HandlerDescription; +import org.apache.axis2.description.Parameter; +import org.apache.axis2.engine.AbstractDispatcher; +import org.apache.axis2.engine.AxisConfiguration; +import org.apache.axis2.util.LoggingControl; +import org.apache.axis2.util.Utils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import javax.xml.namespace.QName; + +/** + * Dispatches based on the namespace URI of the first child of + * the body. + */ +public class JSONBasedDefaultDispatcher extends AbstractDispatcher { + + /** + * Field NAME + */ + public static final String NAME = "JSONBasedDefaultDispatcher"; + private static final Log log = LogFactory.getLog(JSONBasedDefaultDispatcher.class); + + public AxisOperation findOperation(AxisService service, MessageContext messageContext) + throws AxisFault { + + String jsonMessageName = (String) messageContext.getProperty("jsonMessageName"); + + if (jsonMessageName == null) { + log.error("JSONBasedDefaultDispatcher.findOperation() returning null on null jsonMessageName"); + return null; + } + + // Parameter jsonMessageNameParam = messageContext.getParameter("jsonMessageName"); + // String jsonMessageName = Utils.getParameterValue(jsonMessageNameParam); + // AxisOperation axisOperation = service.getOperationByMessageElementQName(null); + AxisOperation axisOperation = service.getOperationByJSONMessageName(jsonMessageName); + // this is required for services uses the RPC message receiver + if (axisOperation == null){ + log.error(messageContext.getLogIDString() + " , axisOperation is null in findOperation() with jsonMessageName: " +jsonMessageName+ " , service name: " + service.getName()); + return null; + } else { + log.debug(messageContext.getLogIDString() + " , axisOperation found from with service name: " + service.getName() + " , operation: " + axisOperation.getName().getLocalPart() + " , jsonMessageName: " +jsonMessageName); + } + return axisOperation; + } + + /* + * (non-Javadoc) + * @see org.apache.axis2.engine.AbstractDispatcher#findService(org.apache.axis2.context.MessageContext) + */ + public AxisService findService(MessageContext messageContext) throws AxisFault { + String serviceName; + + String localPart = messageContext.getEnvelope().getSOAPBodyFirstElementLocalName(); + + if (localPart != null) { + OMNamespace ns = messageContext.getEnvelope().getSOAPBodyFirstElementNS(); + + if (ns != null) { + String filePart = ns.getNamespaceURI(); + + if (LoggingControl.debugLoggingAllowed && log.isDebugEnabled()) { + log.debug(messageContext.getLogIDString() + + " Checking for Service using SOAP message body's first child's namespace : " + + filePart); + } + ConfigurationContext configurationContext = + messageContext.getConfigurationContext(); + String[] values = Utils.parseRequestURLForServiceAndOperation(filePart, + configurationContext.getServiceContextPath()); + + if (values[0] != null) { + serviceName = values[0]; + + AxisConfiguration registry = + configurationContext.getAxisConfiguration(); + + return registry.getService(serviceName); + } + } + } + + return null; + } + + public void initDispatcher() { + init(new HandlerDescription(NAME)); + } +} diff --git a/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml b/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml index 71c176ee50..3b22530667 100644 --- a/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml +++ b/modules/samples/userguide/src/userguide/springbootdemo/resources-axis2/conf/axis2.xml @@ -437,14 +437,6 @@ <phaseOrder type="InFlow"> <!-- System predefined phases --> <phase name="Transport"> - <handler name="RequestURIBasedDispatcher" - class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"> - <order phase="Transport"/> - </handler> - <handler name="SOAPActionBasedDispatcher" - class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"> - <order phase="Transport"/> - </handler> <handler name="JSONMessageHandler" class="org.apache.axis2.json.moshi.JSONMessageHandler"> <order phase="Transport"/> @@ -459,20 +451,8 @@ <phase name="Security"/> <phase name="PreDispatch"/> <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase"> - <handler name="RequestURIBasedDispatcher" - class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/> - <handler name="SOAPActionBasedDispatcher" - class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/> - <handler name="RequestURIOperationDispatcher" - class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/> - <handler name="SOAPMessageBodyBasedDispatcher" - class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/> - <handler name="HTTPLocationBasedDispatcher" - class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/> - <handler name="GenericProviderDispatcher" - class="org.apache.axis2.jaxws.dispatchers.GenericProviderDispatcher"/> - <handler name="MustUnderstandValidationDispatcher" - class="org.apache.axis2.jaxws.dispatchers.MustUnderstandValidationDispatcher"/> + <handler name="JSONBasedDefaultDispatcher" + class="org.apache.axis2.dispatchers.JSONBasedDefaultDispatcher"/> </phase> <phase name="RMPhase"/> <!-- System predefined phases --> @@ -483,11 +463,9 @@ <order phase="OperationInPhase"/> </handler> </phase> - <phase name="soapmonitorPhase"/> </phaseOrder> <phaseOrder type="OutFlow"> <!-- user can add his own phases to this area --> - <phase name="soapmonitorPhase"/> <phase name="OperationOutPhase"/> <!--system predefined phase--> <!--these phase will run irrespective of the service--> @@ -506,29 +484,15 @@ <phase name="Security"/> <phase name="PreDispatch"/> <phase name="Dispatch" class="org.apache.axis2.engine.DispatchPhase"> - <handler name="RequestURIBasedDispatcher" - class="org.apache.axis2.dispatchers.RequestURIBasedDispatcher"/> - <handler name="SOAPActionBasedDispatcher" - class="org.apache.axis2.dispatchers.SOAPActionBasedDispatcher"/> - <handler name="RequestURIOperationDispatcher" - class="org.apache.axis2.dispatchers.RequestURIOperationDispatcher"/> - <handler name="SOAPMessageBodyBasedDispatcher" - class="org.apache.axis2.dispatchers.SOAPMessageBodyBasedDispatcher"/> - <handler name="HTTPLocationBasedDispatcher" - class="org.apache.axis2.dispatchers.HTTPLocationBasedDispatcher"/> - <handler name="GenericProviderDispatcher" - class="org.apache.axis2.jaxws.dispatchers.GenericProviderDispatcher"/> - <handler name="MustUnderstandValidationDispatcher" - class="org.apache.axis2.jaxws.dispatchers.MustUnderstandValidationDispatcher"/> + <handler name="JSONBasedDefaultDispatcher" + class="org.apache.axis2.dispatchers.JSONBasedDefaultDispatcher"/> </phase> <phase name="RMPhase"/> <!-- user can add his own phases to this area --> <phase name="OperationInFaultPhase"/> - <phase name="soapmonitorPhase"/> </phaseOrder> <phaseOrder type="OutFaultFlow"> <!-- user can add his own phases to this area --> - <phase name="soapmonitorPhase"/> <phase name="OperationOutFaultPhase"/> <phase name="RMPhase"/> <phase name="PolicyDetermination"/> diff --git a/src/site/xdoc/docs/json-springboot-userguide.xml b/src/site/xdoc/docs/json-springboot-userguide.xml index b5604e3c21..be360fe5dc 100644 --- a/src/site/xdoc/docs/json-springboot-userguide.xml +++ b/src/site/xdoc/docs/json-springboot-userguide.xml @@ -193,7 +193,7 @@ WEB-INF/conf/axis2.xml. </pre> <p> Other required classes for JSON in the axis2.xml file include JsonRpcMessageReceiver, -JsonInOnlyRPCMessageReceiver, JsonBuilder, and JSONMessageHandler. +JsonInOnlyRPCMessageReceiver, JsonBuilder, JSONBasedDefaultDispatcher and JSONMessageHandler. </p> <p> Invoking the client for a login that returns a token can be done as follows: