I'm currently working on a Java application which receives XML messages through sockets. There are different types of XML messages and I'm now looking for the most elegant way/best practice to properly dispatch and handle those.
Currently I have a working test setup with a general Dispatch class containing a process method which receives the already parsed message. For each of these types I then have a specific process method. Works for testing connections etc but not so pretty.
public class Dispatch
public Object process(XMLMessage xml){
Object obj = xml.getXml();
if (obj instanceof AccessCheck)
return processObject((AccessCheck) obj);
else if (obj instanceof Note)
return processObject((Note) obj);
else if (obj instanceof Login)
return processObject((Login) obj);
...
return null;
}
}
Since the ultimate solution contains more message types and should also contain a database connection and cache, I'm looking for a way to group the handling of messages per domain and preferably also eliminate or reduce this large if-statement with explicit casts.
I thought about doing it like this with a facade per domain (which would also allow for a separate cache per domain):
public class Dispatch
public Object process(XMLMessage xml){
Object obj = xml.getXml();
if (obj instanceof AccessCheck)
return AuthorizationDataFacade.process((AccessCheck) obj);
else if (obj instanceof Note)
return SomeOtherDataFacade.process((Note) obj);
else if (obj instanceof Login)
return AuthorizationDataFacade.process((Login) obj);
...
return null;
}
}
public class AuthorizationDataFacade {
public Object processAccessCheck(AccessCheck check){
//do something and return the response
return null;
}
public Object processLogin(Login login){
//do something and return the response
return null;
}
But is there a way to eliminate that large if statement with explicit casts? Or should I rethink my XSD/XML design and merge some messages together (ie. merge AccessCheck and Login into a general Authorization xml)? Any thoughts?
One possible solution is with reflection. The solution presented here assumes the following
AuthorizationDataFacade
handles AccessCheck
and Login
messages) process()
method according to the convention (in any class you see fit) and add an entry to the dispatchMap
. that's it. The solution is specified below, I added some comments to clarify things:
import java.lang.reflect.*;
import java.util.*;
public class Dispatcher
{
// the map associates xml msg type to processor type
private static Map<Class<?>, Class<?>> dispatchMap = new HashMap<>();
static
{
dispatchMap.put(AccessCheck.class, AuthorizationDataFacade.class);
dispatchMap.put(Login.class, AuthorizationDataFacade.class);
dispatchMap.put(Note.class, SomeOtherDataFacade.class);
}
public String dispatch(Object obj)
{
Class<?> xmlMsgType = obj.getClass();
// search for xml msg type in dispatch map
if (!dispatchMap.containsKey(xmlMsgType)) {
return "unrecognized message type";
}
// found match, now locate process method
try {
Class<?> ProcessorType = dispatchMap.get(xmlMsgType);
Method[] processorTypeMethods = ProcessorType.getDeclaredMethods();
Method chosenProcssMethod =
Arrays.stream(processorTypeMethods)
.filter(m -> methodFitForXmlMsg(m, xmlMsgType))
.findFirst()
.orElse(null);
if (chosenProcssMethod != null) {
// found match, instantiate processor and invoke method
Object processorInstance = ProcessorType.newInstance();
return (String)chosenProcssMethod.invoke(
processorInstance,
chosenProcssMethod.getParameterTypes()[0].cast(obj));
}
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
// no match found or error
return "error";
}
// a method is dimmed fit for processing a given xml msg type
// if it is named "process", returns a String, has one arg that is of the given type
private boolean methodFitForXmlMsg(Method m, Class<?> xmlMsgType)
{
return m.getName().equals("process") &&
m.getReturnType() == String.class &&
m.getParameterCount() == 1 &&
m.getParameterTypes()[0].equals(xmlMsgType);
}
public static void main(String... args)
{
Dispatcher d = new Dispatcher();
System.out.println(d.dispatch(new AccessCheck()));
System.out.println(d.dispatch(new Login()));
System.out.println(d.dispatch(new Note()));
System.out.println(d.dispatch("something else"));
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.