简体   繁体   中英

Proper way to process different types of unmarshalled XML objects in Java

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

  1. a Processor can handle multiple xml message types (eg AuthorizationDataFacade handles AccessCheck and Login messages)
  2. a convention for process methods is enforced: it must be named "process", have one arg that is of type of one xml message and have a known return type. this convention is used in searching for the right process method for a given type of xml msg.
  3. When you need to process a new xml msg, you write its 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM