4

Is it possible to customise the code for a JAXWS class. What I want to do is add some helper methods to the generated class to help extract information from it. I know I can subclass the generated source but this means I have to cast it everywhere I want the info.

Is it possible to have jaxws (or another tool) merge together the generated source code from the WSDL with some custom code containing my methods? (In C# I could do this with a partial class but java doesn't appear to have an equivalent)...

Spence
  • 28,526
  • 15
  • 68
  • 103
  • Not that I'm aware of. [JAX-WS bindings customizations](http://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/2.0/jaxws/customizations.html#2.7_Java_Method_customization) give you an all-or-nothing approach; still too much of a hassle IMO to avoid the minor inconvenience of casting. Not to mention that 1) Most of the artefacts generated by wsimport conform to some abstract parent class or interface an 2)there are likely other ways to retrieve the information you're trying to retrieve. If you didn't put the information in there yourself, I'd hazard a guess that there's some code... – kolossus Feb 27 '16 at 23:02
  • ...malpractice on your part if you find yourself depending on that information. Perhaps if you shared your specific use-case and there might be a cleaner alternative to retrieving that information. It might be worthwhile to re-evaluate your approach when you find yourself looking to alter what is essentially boilerplate code – kolossus Feb 27 '16 at 23:04
  • Sometimes there are things you can't express in XSD. For example how the toString should work. If you are working with Credit Card numbers for example the default behaviour of toString should be to not show the full card number but to mask out part of. Personally I would have a thin interface layer and then translate to an internal model... though that isn't popular. The other option is to look what can be done with CXF etc. – Gavin Feb 28 '16 at 19:29
  • I have some helper methods that either derive information from the XML or set data from a complex object (or database serialization). Right now I have a set of static methods which take the jaxws object as the first parameter, I just wanted to add these methods onto the class object in a way that won't be lost if I regenerate the code. – Spence Feb 29 '16 at 01:52
  • @kolossus we are dealing with an XSD we didn't write. All I'm trying to do is elegantly derive information from it. A great example where this "isn't" leaking would be a validate method. Something might be legal XML but is logically incorrect. – Spence Mar 01 '16 at 02:40

2 Answers2

4

One approach to achieve this would be to introduce those helper methods via aspects. AspectJ would be a potential candidate for this, or if you're using Spring, then Spring AOP could also do the job.

So using AspectJ, you'd leverage the feature called "inter-type declaration", which allows one to introduce new features into existing classes. If your generated JAXWS class is called MyTypeGen, then you'd introduce new methods using an aspect (arbitrarily named MyTypeAspect) like this:

// generated class
class MyTypeGen  {      
    int x, y;

    public void setX(int x) { this.x = x; }
    public void setY(int y) { this.y = y; }
}

// introduce two new methods into MyTypeGen
aspect MyTypeAspect {
    public int MyTypeGen.multiply() {
        return x * y;
    }
    public int MyTypeGen.sum() {
        return x + y;
    }
}

At runtime, you can call those two new methods which have been weaved into MyTypeGen at build-time. No casting needed.

MyTypeGen mytype = new MyTypeGen();
mytype.setX(3);
mytype.setY(4);
int product = mytype.multiply();
// product = 12

If you decide to use Spring AOP instead, you'd create a normal Java class with the @Aspect annotation, in which you'd leverage the introductions feature.

Val
  • 207,596
  • 13
  • 358
  • 360
  • 1
    So effectively use an aspect to weave the methods into an existing class. Not quite an aspect, but effectively achieving what I'm asking for. Quite a trip to introduce the aspectJ compilation into our environment, but definitely the best option I've seen so far. – Spence Feb 29 '16 at 01:56
  • Is there anything else you're looking for? – Val Mar 01 '16 at 06:16
  • 1
    mytype.multiply() was EXACTLY what I was looking for. Just wanted to know if I'd missed something simple on jaxws to do it before applying brute force for the result. – Spence Mar 01 '16 at 07:46
  • sorry mate, awarded the bounty, figured that would have answered it too. – Spence Mar 03 '16 at 04:21
  • Thanks Spence, no worries, much appreciated ;-) – Val Mar 03 '16 at 04:23
1

There are many ways to do this, one of them which is using aspect is already mentioned by another answer.

I would like to suggest a lighter alternative, which is to just extend the generated class:

public GeneratedClass {

    public void doThis();

}

public ImprovedClass extends GeneratedClass {

    public void doExtraStuff();

}

The advantage of this is that you have a clear definition of that GeneratedClass can do. If you start weaving methods into the GeneratedClass, the one who work on the project a few years later on might get the idea that all similar instance of the generated class have the same method. Or they might be confused why some generated classes have these extra methods, and some not. The extra functionality should be kept in a clearly defined class. Only the subclass of that type can do the extra stuff. This makes it clear where the method comes from.

It's not like I have something against code weaving, but I think code weaving should only be used for something that doesn't involve business logic like logs, metrics, or for inside a framework. Business logic related code should not be weaved to a class.

Anyway, another way to do this is to wrap the class, but this will need an interface.

public GeneratedClass implements SomeInterface {

    public void doThis();

}

public ImprovedClass implements SomeInterface {

    public SomeInterface impl;

    public Improvedclass(SomeInterface impl) {
        this.impl = impl;
    }

    public void doThis() {
        this.impl.doThis();
    }

    public void doExtraStuff() {
        // the extra stuff
    }

}
Rowanto
  • 2,819
  • 3
  • 18
  • 26
  • I tried this approach at first. The issue is I want the class to round trip back to the XML. Deriving from it created many issues, particularly with the namespace and the element names generated by the derived version of the class. As I had multiple constructors, it was interesting to make sure that I could manage that. Not saying it wouldn't work but it seemed like a way to introduce insidious bugs. Plus I had to make an upcast operator to use my derived class which was painful. – Spence Mar 01 '16 at 01:15