How to prune a Java program
Asked Answered
I

2

5

Let's me start from what I want to do then raising some questions I have.

I want to develop a general Java program which is a superset of a number of programs (let's call them program variants). In particular, the general program has methods which are only used by one or more program variants (but not all). Given a particular configuration, I want to remove unnecessary methods and just keep the smallest set of methods for one program variant.

For example, I have a general program as below:

public class GeneralProgram {

    // this method is common for all variants
    public void method1() {};

    // this method is specific to variant 1
    public void method2() {};

    // this method is specific to variant 2
    public void method3() {};
}

Then after pruning the program based on configuration for variant 1, the result is

public class GeneralProgram {

    // this method is common for all variants
    public void method1() {};

    // this method is specific to variant 1
    public void method2() {};
}

It doesn't matter if the resulting class name is the same as the original one or not. I just want to prune the content of the class.

So, here are my questions:

  1. Do you have any idea how to realize this except low level text processing?

  2. I know that I can use aspectJ to disable/enable specific methods at runtime but what I really want to do is performing this task before deploying the program. Is there any technique in Java for this purpose?

Iridaceous answered 18/6, 2011 at 3:22 Comment(2)
Why do you want to remove methods rather than merely the calls to those methods?Megargee
The requirement comes from my research project which tries to support tens or hundreds variants. So it is not effective if I deploy unnecessary resources (e.g. methods, classes specific to other variants) for a particular variant. I didn't mention the detail of the project since it is a long story :)Iridaceous
G
6

It seems to me that the right solution here is to use some object oriented programming and layer your program:

base.jar contains:

package foo.base;
class GeneralProgram {
   public void method1(){ }
}

var1.jar contains:

package foo.var1;
import foo.base.GeneralProgram;
class GeneralProgramVar1 extends GeneralProgram {
   public void method2(){ }
}

var2.jar contains:

package foo.var2;
import foo.base.GeneralProgram;
class GeneralProgramVar2 extends GeneralProgram {
   public void method3(){ }
}

Some deployments will have both base.jar and var1.jar, others will have base.jar and var2.jar. You'll have to mess with the classpaths a bit to resolve the dependencies.


If you can separate your variants well enough so that there are truly unused functions then you can use a compression utility like ProGuard to remove unused methods from the classes. You might find, however, that the effort required to reap the benefits of ProGuard are the same as the structure I recommend above.

Grenadines answered 18/6, 2011 at 3:28 Comment(6)
This seems more sensible. The was the question was asked I suspect tuan is coming from a C++ background and trying to apply those techniques to Java.Expediency
Thanks Mark. I like your solution. In fact, I want the general program to support tens or hundreds of variants so that developing source codes specific to each variant (like your solution) is not preferred. Instead, it is better if I provide a program to generate such variant based on the general program. You might argue that the effort to well separate the source code within a program is similar or more than the effort to develop each variant separately. However, the benefit comes from techniques to efficiently capture and manage such variability among variants.Iridaceous
It's hard to discuss in detail here but this requirement comes from Software Product Line (SPL) domain.Iridaceous
@tuan: it's unclear why proper program structure would violate your program requirements.Grenadines
@Mark: One of my requirements is to derive a program variant from a general program (which is similar to a program template). This comes from a research problem that I am addressing.Iridaceous
@tuan: I still fail to see any conflict: separate, in package/jar space the derived program variants and the general program.Grenadines
D
3

@Mark Elliot's answer gives you a "right way" to do this.

There are a number of reasons why your way is not a good idea in general, and for Java applications in particular:

  • Java does not support this. Specifically, it does not support conditional compilation.

  • While source code preprocessors are sometimes used, mainstream Java tool chains don't support them. (Same for (hypothetical?) tools that operate at the bytecode level ... though that's not what you seem to be talking about.)

  • With conditionally compilation variants, it is easier for a change made in one variant to break another. (By contrast, a good O-O design will isolate variant-specific code to particular classes where they can't affect the behaviour of other variants.)

  • A codebase with rampant conditional compilation is much harder to understand.

  • Conditional compilation variants make testing more complicated. You basically have to treat each variant as a separate application that has to be tested separately. This makes writing tests more complicated, and running tests more expensive. (And testing the variants IS important because of the fragility of code bases that rely on conditional compilation; see previous.)

  • Test coverage analysis is harder / more work with variants because of tool issues; see previous.


In a comment the OP writes:

So it is not effective if I deploy unnecessary resources (e.g. methods, classes specific to other variants) for a particular variant.

What do you mean by "not effective"?

In most cases it simply does not matter that a code base includes functionality that is not used in certain use-cases or on certain platforms. Java applications use lots of memory, and code size is generally not the major cause of this. In short, in most cases it is "effective" to deploy code that won't be used: it does the job and the overheads don't really matter.

If you have one of those unusual applications where JAR file size or code memory usage is really significant (and not just a hypothetical issue), you still don't need to resort to conditional compilation or bytecode hacking.

  • If JAR file size is the critical issue, then there are tools that will strip out classes and methods that the tool determines will not be used; e.g. assuming that the application is started from a specified main method.

  • If memory usage is the critical issue, you can structure your code so that it uses dynamic loading to load variant, platform or even use-case specific classes.

Dibasic answered 18/6, 2011 at 6:43 Comment(3)
Yes, there are issues that need to be considered. Specifically, addressing dependencies among variants is challenging. But testing is not an issue because it will be developed and performed with regard to a particular variant. And as you said, a good OO design will significantly reduce the mixture of codes belonging to different variants. But it is unavoidable because I don't want to develop each variant separately. I realized what I need is a code transformation tool that I can use to purge unnecessary methods & classes, and then package and deploy only relevant artifacts.Iridaceous
The first one I found is Smooks that supports Java-to-Java transformation. Not sure if it works as expected. But codes transformation tool is really what I am looking for.Iridaceous
@Iridaceous - "And as you said, a good OO design will significantly reduce the mixture of codes belonging to different variants. But it is unavoidable because I don't want to develop each variant separately." I don't understand your reasoning. OO does not require you to develop each variant separately. Also, you have not given a convincing argument why you NEED to do this. (And if you don't NEED to do it, you shouldn't be doing it.)Dibasic

© 2022 - 2024 — McMap. All rights reserved.