HOW TO: Stereotyping a java Class

In this HowTo post I will show how we can stereotype a class with another class. Why is this useful?

  • When there are a lot of BCI’ing happening in your project, it is not prudent to let every developer write the BCI code.
    • For one, this does not abstract the BCI library used. Thus changing the library itself becomes difficult.
    • BCI is complex. The probability of introducing bugs increase if every developer has to understand BCI.
    • It would be better to write a framework that allows a developer to write an interface and a class which can be used for BCI’ing.
  • Given that java does not support Multiple inheritance, stereotyping can be used to achieve Multiple inheritance without delegating. Check here for multiple inheritance options.
  • There exists aspects of code such as profiling that need to be present only if the code is being tested. Production code is best without being splattered with debug code. Stereotyping can be used in this case by totally varying the classloader used to load the classes, one that adds the profile code and one that does not.

StereoTyping means…

Say there was an interface:

public interface PerfInterface
{
    public void start(String nm);
    public void end();
    public String getValue(String value);
}

You write an implementation for this interface:

import java.util.Stack;

public class PerfTemplate implements PerfInterface
{
    private Stack _stats;

    public void start(String nm)
    {
        PerfStats stat = new PerfStats();
        stat.start(nm);
        if (_stats == null)
            _stats = new Stack();

        _stats.push(stat);
    }

    public void end()
    {
        try
        {
            PerfStats stat = (PerfStats)_stats.pop();
            stat.end();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    public String getValue(String val)
    {
        return "PerfTemplate:Modified:" + val;
    }
}

You want all classes that need to implement the interface to use the above implementation without a developer actually coding it. At runtime you want the class to adapt this behavior. For eg., you want to code a class as:

public class ClassToStereoType
{
    .....
}

Which does not implement the PerfInterface. But when running it for performance you want the class to be:

public class ClassToStereoType implements PerfInterface
{
    .....
}

with all the functions of PerfInterface implemented. This is called stereotyping.

How to Stereotype?

Here we will use the asm library to stereotype. We will follow the same steps as in “BCI during runtime“. We will create a ClassNode object from the class from which we have to stereotype as below:

InputStream nstr = new FileInputStream("PerfTemplate.class");
        ClassReader n = new ClassReader(nstr);
        ClassNode cn = new ClassNode();
        n.accept(cn, ClassReader.EXPAND_FRAMES);

Here we read the class PerfTemplate.class and accept it into a ClassNode which now contains all the fields and methods from PerfTemplate class.

We will write a ClassVisitor that overrides the visitEnd to add the fields and methods from the ClassNode created.

public void visitEnd()
        {
            System.out.println("In visit End. Adding Fields");

            for (Iterator it = _cn.fields.iterator(); it.hasNext();)
            {
                ((FieldNode) it.next()).accept(cv);
            }

            for(Iterator it = _cn.methods.iterator(); it.hasNext();)
            {
                MethodNode mn = (MethodNode) it.next();
                if (!mn.name.equals("")) //ignore constructor
                {
                    String[] exceptions = new String[mn.exceptions.size()];
                    mn.exceptions.toArray(exceptions);
                    MethodVisitor mv = cv.visitMethod( mn.access, mn.name, mn.desc, mn.signature, exceptions);
                    mn.instructions.resetLabels();
                    mn.accept(new RemappingMethodAdapter( mn.access, mn.desc, mv, new SimpleRemapper(_cn.name, _name)));
                }
            }            super.visitEnd();
        }

In the above code, we iterate through the fields and add it to the class that we are modifying (i.e., ClassToStereoType). When adding the methods we should be careful to ensure all references to PerfTemplate class is modified to ClassToStereoType. For this, we use the RemappingMethodAdapter which is a class provided by asm.

To add the interfaces from PerfTemplate to ClassToStereoType we override the visit method. Here we add the interfaces from the ClassNode to the current class.

public void visit (int version, int access, String name, String signature, String superName, String[] interfaces)
        {
            System.out.println("Class Name is: " + name + ":" + signature + ":" + superName);
            int len = 0;
            List ndeints = _cn.interfaces;
            if (interfaces != null) len = interfaces.length;
            String[] modinterfaces = new String[len + ndeints.size()];
            int cnt = 0;
            for (cnt = 0; (interfaces != null) && ( cnt < interfaces.length); cnt++)
            {
                modinterfaces[cnt] = interfaces[cnt];
            }

            for (String inter : ndeints)
                modinterfaces[cnt++] = inter;
            cv.visit(version, Opcodes.ACC_PUBLIC, name, signature, superName, modinterfaces);
            _name = name;
        }

In the above code, we append all the interfaces from the ClassNode got by calling _cn.interfaces to the interfaces of ClassToStereoType. We use this modified list of interfaces to visit the class. This ensures that the interfaces are implemented in the loaded class.

Now the ClassVisitor implemented with these changes can be used to modify the bytes in the class loader to stereotype the class.

The code for this HOWTO can be found here. Run the commands in compiletst.sh to try the sample out.
 

Reference: HOW TO: Stereotyping a java Class from our JCG partner Raji Sankar at the Reflections blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


+ five = 8



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close