3

Would the following use case be considered as justified for Reflection?

There are bunch of classes generated from XSDs (hundreds currently on project) which represent various Responses.

All of these Responses include common response data structure, rather then extending it.

When event such as timeout happens, i only need to set single String to specific value.

If these classes were extending common response structure i could always set this response code without reflection, but this is not the case.

Therefore i wrote simple utility for my services which uses reflection to get setter method for the String field and invoke it with predefined value. Only known alternative to me would be to have class specific methods which would duplicate code to handle timeout, with the only difference of returned Response class.

protected T handleTimeout(Class<T> timeoutClass) {
    try {
        T timeout = timeoutClass.newInstance();
        Method setCode = timeoutClass.getDeclaredMethod(SET_RESPONSE_CODE, String.class);
        setCode.invoke(timeout, Response.TIMEOUT.getCode());
        return timeout;
    } catch (InstantiationException | IllegalAccessException  | SecurityException | NoSuchMethodException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
        throw new RuntimeException("Response classes must have field: \"code\" !");
    }

}

Relevant fact:

  • this setter method should never change as it would require rework of hundreds of interfaces

Could somebody point out if there are some pitfalls i have missed or if there is alternate solution for reflection which would achieve the same result ?

Edit: I simply have no authority to get any changes done on XSDs, so any solution would have to be done locally. There should be no problems with serializing such objects, as they are shared between components.

John
  • 5,189
  • 2
  • 38
  • 62
  • You have excellent idea, it is good to use reflection here, if not here, then for what purpose java has reflection? – Krzysztof Cichocki Jun 02 '15 at 11:52
  • @KrzysztofCichocki All sorts of things, really. – biziclop Jun 02 '15 at 11:53
  • 3
    Is it not possible to let the generated classes implement an interface which contains this method? That's what I would think of as good solution. – kutschkem Jun 02 '15 at 11:53
  • 3
    The trouble you have is that you're already in a bad situation. The ideal solution would be to amend or extend the code generator to create the classes that you need. But failing that your only choice is between different flavours of wrong. – biziclop Jun 02 '15 at 11:54
  • I would personally post-process the generated source code and flag every class to implement my interface. – biziclop Jun 02 '15 at 11:55
  • What kutschkem and biziclop said. Even if they're not dynamically generated, you can quickly add the interface with a simple script (python, perhaps?) and a little bit of tweaking. – Andy Turner Jun 02 '15 at 11:55
  • 1
    Problem is that i cannot modify current XSDs, there are to many components using them. – John Jun 02 '15 at 11:56
  • @user3360241 Don't modify the XSDs, modify the generated source files. (Using some kind of script, obviously.) – biziclop Jun 02 '15 at 11:56
  • 1
    @biziclop how would this work when another component which is not in my control receives such Response? – John Jun 02 '15 at 11:57
  • @user3360241 It shouldn't care much about it, as you haven't modified any methods, you just tagged the classes with an interface. – biziclop Jun 02 '15 at 11:58
  • 1
    Could you show very small example how that would work :) – John Jun 02 '15 at 11:59
  • Will do in an answer. – biziclop Jun 02 '15 at 11:59
  • *Emulating a language feature* sounds like a suboptimal strategy, given that language feature has a perfectly good implementation already. – Andy Turner Jun 02 '15 at 12:06
  • If you are not able to change the code that needs to be changed for your problem to be solved correctly, we can not change that. The correct solution stays the same. – LionC Jun 02 '15 at 12:48
  • Did you consider using a xjc binding to let all classes generated by jaxb extend from your own (non-generated) class providing the setter? – guido Jun 02 '15 at 13:14
  • Yeah, i was making same comment bellow in one of answer. My concern with this is still deserialization on receiving component. I do not have time to test this now but will do so. – John Jun 02 '15 at 13:20
  • you seem concerned about deserialization, but isn't only marshalling/unmarshalling relevant in your scenario? – guido Jun 02 '15 at 14:17

5 Answers5

4

Firstly there is a standard, normal everyday solution as suggested by @kutschkem, specifically: declare an interface that only contains this one setter method and implement that interface in every class which requires it. This uses standard polymorphism to do exactly what you need.

I understand this requires changing the definition of a lot of classes (but the change is trivial - just add 'implements MytimeoutThing' to every class) - even for 1000's of classes this seems a fairly easy fix for me.

I think that there are real problems with reflection:

  1. You are creating a secret interface to all your classes that must be supported but there is no contract for this interface - when a new developer wants to add a new class he has to magically know about the name and signature for this method - if he gets it wrong the code fails at run-time as the compiler doesn't know about this contract. (So something as simple as misspelling the setters name isn;t picked up by the compiler)

  2. It's ugly, hidden and not clearly part of any particular part of the software. A dev maintaining ANY of these classes will find this function (the setter) notice that it is never being called and just delete it - after all no code in the rest of the project refers to that setter so it obviously isn't needed.

  3. A whole lot of static analysis tools won;t work - for example in most IDE's you can establish all the places that specific function is called from and all the places that a specific function calls - obviously this kind of functionality is not available if you use reflection. In a project with hundreds of near identical classes I would hate to loose this facility.

Krzysztof Cichocki
  • 6,294
  • 1
  • 16
  • 32
Elemental
  • 7,365
  • 2
  • 28
  • 33
  • Hm but it isnt secret contract. It is documented that all Respones must have this structure. I also do not have control over the XSD definitions so any change on XSDs would have to come from Solution architects - which is not going to happen given number of components that would have to recompile and adjust. Basically, nobody would make use of this interface change and it would not be done only for me – John Jun 02 '15 at 12:18
  • It is not obvious, that no one usese this setter, if there is some use of concrete class, the code could use this field, everywhere. Give answer, how to solve that, not just another philosophy! – Krzysztof Cichocki Jun 02 '15 at 12:18
  • Unless this could be done locally, so that i implement this interface by modification of generated code - would be nice to see alternative, assuming there won't be problems with serialization afterwards. – John Jun 02 '15 at 12:19
  • @user3360241 This is exactly the solution proposed, that you add `implements Timeoutable` to the generated code. – biziclop Jun 02 '15 at 12:22
  • Can you show me this as answer and i will accept it :) Still i am worried that such class would be impossible to deserialize on component which would receive such response. – John Jun 02 '15 at 12:24
3

The actual problem you are facing is that you have a lot of classes that should share a common abstraction between them (inheriting the same class or implementing the same interface), but they don't. Trying to keep it that way and designing around would basically be taking care of the symptoms instead of the cause and will likely cause more problems in the future.

I suggest to solve the root cause instead by making all the generated classes have a common interface / superclass. You do not have to do this by hand - as they are all generated it should be possible to change them automatically without much struggle.

LionC
  • 3,106
  • 1
  • 22
  • 31
  • There are 20 or so components using these Responses, and i am not working on all of them. Such change would require that all these components re implement each interface which would be to expensive. Therefore i am looking for possible workarounds for the given sitaution – John Jun 02 '15 at 12:11
  • 4
    @user3360241 Fixing design mistakes is expensive in most cases, but pays off in the long run. Using a reflection approach that magically does things that are not possible with the code itself will cause confusion, harder refactorings and eventually hard-to-trace bugs in the future. – LionC Jun 02 '15 at 12:15
2

I'd try an alternate solution for generating your classes from the xml schema, in preference over reflection.

You can supply xjc with a custom binding like this:

<?xml version="1.0" encoding="UTF-8"?>
<bindings xmlns="http://java.sun.com/xml/ns/jaxb"
      xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance"
      xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
      xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd" version="2.1">
    <globalBindings>
        <xjc:superClass name="XmlSuperClass" />
    </globalBindings>
</bindings>

and implement you XmlSuperClass like this:

@XmlTransient                     // to prevent that the shadowed responseCode be marshalled
public class XmlSuperClass {
    private String responseCode;         // this will be shadowed
    public String getResponseCode() {    // this will be overridden
        return responseCode;
    }
    public void setResponseCode(String value) { //overridden too
        this.responseCode = value;
    }
}

Invoking xjc like this:

xjc -extension -b <yourbinding.xjb> -cp <XmlSuperClass> <xmlschemas.xsd...>

will generate bound classes like:

@XmlRootElement(name = "whatever")
public class Whatever extends XmlSuperClass {
    @XmlElement(required = true)
    protected String responseCode;    // shadowing
    public void setResponseCode(String...) //overriding
}
guido
  • 18,864
  • 6
  • 70
  • 95
1

To become objective again:

There is no mention of the object instantiation. If you would have postulated a constructor with a String code parameter:

T timeout = timeoutClass.getConstructor(String.class)
    .newInstance(Response.TIMEOUT.getCode());

Would the save critics arise? To a lower extent, as parametrized constructors are even more indeterminate. Let's await the voting here.

Interface is better looking though.

interface CodeSetter {
   void setCode(String code);
}

protected <T extends CodeSetter> handleTimeout(Class<T> timeoutClass) {
    try {
        T timeout = timeoutClass.newInstance();
        timeout.setCode(Response.TIMEOUT.getCode());
        return timeout;
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • My only concern is that receiver cannot deserialize class which implements interface if he does not do the same. I will try this approach later today and see if there will be issues. – John Jun 02 '15 at 12:48
1

Okay, so let's suppose you've got this generated code:

public class Response1 {
   public void setResponseCode(int code) {...}
}

public class Response2 {
   public void setResponseCode(int code) {...}
}

What you need to do then is write an interface:

public interface ResponseCodeAware { //sorry for the poor name
   public void setResponseCode(int code);
} 

Then you need to write a script that goes through all the generated code files and simply adds implements ResponseCodeAware after every class definition. (That's assuming that there are no interfaces implemented already, in that case you have to play around a bit with the string processing.)

So your generated and post-processed classes will now look like this:

public class Response1 implements ResponseCodeAware {
   public void setResponseCode(int code) {...}
}

public class Response2 implements ResponseCodeAware {
   public void setResponseCode(int code) {...}
}

Note that nothing else changed, so code that doesn't know about your interface (including serialization) should work exactly the same.

And finally we can rewrite your method:

protected T handleTimeout(Class<T extends ResponseCodeAware> timeoutClass) {
    try {
        T timeout = timeoutClass.newInstance();
        timeout.setResponseCode( Response.TIMEOUT.getCode() );
        return timeout;
    } catch (InstantiationException | IllegalAccessException  | SecurityException | NoSuchMethodException | IllegalArgumentException | InvocationTargetException e) {
        e.printStackTrace();
        throw new RuntimeException("Response class couldn't be instantiated.");
    }
}

As you can see, unfortunately we still have to use reflection to create our object, and unless we also create some kind of factory, that will stay that way. But code generation can help you here too, you can build up a factory class in parallel to marking the classes with the interface.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • I guess i could make JAXB add interface implementation during code generation. I will test later to see if there are really no serialization issues if the receiver does not also implement the interface. – John Jun 02 '15 at 12:49
  • 1
    @user3360241 If you could convince JAXB to do it, it would indeed be preferable to post-processing the generated source. – biziclop Jun 02 '15 at 12:56
  • it is possible by using a custom binding file for xjc, by using an XmlTransient base class – guido Jun 02 '15 at 14:24
  • @ᴳᵁᴵᴰᴼ Thanks, I'll have a look into it, I haven't used JAXB to generate classes for a while, I always happen to start with the classes. – biziclop Jun 02 '15 at 14:26