?
Solved

Java Calling Class

Posted on 2003-03-07
26
Medium Priority
?
842 Views
Last Modified: 2013-11-23
I have an application where I want to get a reference back to the calling class in Java.

The calling class does not identify itself and I want to go back to the calling class and find out its name using the java.lang.reflect forName() function to get the name of the calling class.

How can I get the reference to the calling class?

0
Comment
Question by:wabar
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 4
  • 4
  • +7
26 Comments
 

Expert Comment

by:rwoodruf
ID: 8091108
Can you provide details as to what you are planning on doing with the calling object once you able to figure out what it is?  

I am confused as to how you expect to get the calling object when you don't pass a reference of the calling object to the object that is being called.

Hope this makes sense.
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8091121
You can't AFAIK
0
 

Author Comment

by:wabar
ID: 8091149
I have a logging utilty that is instantiated as a singleton. It would be great to know which class is writing to the log w/o going back to re-write the log API and updating 1000's of log calls to pass in the data.

If I could get a reference back to the calling class, then I could do .forName() call on that class (as an object).

0
Optimize your web performance

What's in the eBook?
- Full list of reasons for poor performance
- Ultimate measures to speed things up
- Primary web monitoring types
- KPIs you should be monitoring in order to increase your ROI

 
LVL 86

Expert Comment

by:CEHJ
ID: 8091163
You'll have to pass a reference
0
 

Accepted Solution

by:
agb6w earned 1000 total points
ID: 8091169
If you are running version 1.4 you could do something along the lines of this:

public static String getCallingClass() {
   try{
       throw new Exception();
   } catch (Exception e){
       StackTraceElement[] ste = e.getStackTrace();
       if (ste == null || ste.length < 3)
           return null;
       else
           return ste[2].getClassName();
   }
}

Basically it catches an exception and examines the stack trace.  This method will report the classname of the method that calls the method that calls this method, which I believe is what you need.  If you call this from a main method it would return null.  Hope that helps.
0
 

Author Comment

by:wabar
ID: 8091190
Wow, that is great! However, isn't throwing an exception  costly for perfomance? If I do this with every log will I not grind my application down to a crawl?

This maybe the only solution though ...
0
 
LVL 92

Expert Comment

by:objects
ID: 8091372
You could provide a parameter for passing the calling object in your logging api.

public void log(Object caller, ....);
0
 

Expert Comment

by:agb6w
ID: 8091445
True. Exceptions can be fairly expensive.  But that is usually in comparison to regular flow control.  If you are logging to a file then you I/O time is probably going to be so much more expensive that it really wouldn't matter.  Unfortunately, I don't know of any other way to access the call stack.

If you are using this for logging have you considered using something like log4j?  It is pretty powerful.
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8091559
Or use the logging in 1.4 if you're using that version of Java
0
 
LVL 1

Expert Comment

by:hj2k3
ID: 8091659
yes, the only way i can see a how a singleton instantiation could work would be to pass a reference to the logger each time like 'object' suggested.
Another way would be to instantiate a new logger for each application that requests logging. Then the application would need to call a logger method like setCaller(Object o) or in the constructor once before starting logging to identify itself, with the object reference being stored in each logger instance. This way, a reference would not need to be passed each time a log is made, just once at initialisation.
0
 
LVL 86

Expert Comment

by:CEHJ
ID: 8091723
>>to pass a reference to the logger each time like 'object' suggested

and what about

>>You'll have to pass a reference

;-)
0
 
LVL 1

Expert Comment

by:hj2k3
ID: 8091763
yep, credit to you too for simplicity
0
 
LVL 1

Expert Comment

by:ykaganov
ID: 8091987
I think exception stack trace is populated when you create the exception, not when you throw it.

So, you could just do

Exception e = new Exception();
StackTraceElement[] ste = e.getStackTrace();
if (ste == null || ste.length < 3)
  return null;
else
  return ste[2].getClassName();

BTW, if you don't have 1.4, you can still do something similar using e.printStackTrace(), and parse the stack trace for the class name. It's just much more tricky and JVM-dependent.

But as a matter of clean code, it's a better idea to pass the reference to the calling class :)

- Eugene
0
 
LVL 92

Expert Comment

by:objects
ID: 8092014
> I think exception stack trace is populated when you
> create the exception

You're still creating a new object everytime you add an entry to the log.
0
 
LVL 92

Expert Comment

by:objects
ID: 8092026
> then I could do .forName() call on that class (as an object).

What exactly do you plan to do with this Class object anyway?
0
 
LVL 1

Expert Comment

by:ykaganov
ID: 8092027
yes. sorry, that's the best I can do :)
0
 
LVL 2

Expert Comment

by:functionpointer
ID: 8093338
> I think exception stack trace is populated when you create the exception, not when you throw it.

The stack is populated with stack frames anytime the VM is running, and the StackTraceElement[] is just a dump of this. It's not really that big of a deal ( except for the creation of the Exception object ).

To finally and definitively answer your question,

> How can I get the reference to the calling class?

You can get it, but you gotta want it. You can access the stack using JDI ( Java 1.3 or higher ). Essentially, what you want is to find the MethodEntryEvent in the VM's EventQueue, at at that point, momentarily suspend the VM, looking at the previous frame on the stack and get the method, class, etc..  The jump to the next frame ( current frame ), set your values, and let the VM go again.

Example: ( i may have 2 posts just so it fits )
Class 1 -

package vm;
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
import java.util.*;
public class TestMain {
  public TestMain() {}
  public static void main(String[] args) throws Exception {
    TestMain test = new TestMain();
    VirtualMachineManager manager = Bootstrap.virtualMachineManager();
    LaunchingConnector connector = manager.defaultConnector();
    Map map = connector.defaultArguments();
    ((Connector.Argument)map.get("main")).setValue( "-classpath \"c:/vm/classes\" vm.LogMain" );
    VirtualMachine vm = connector.launch(map);
    EventRequestManager erm = vm.eventRequestManager();
    MethodEntryRequest mer = erm.createMethodEntryRequest();
    mer.addClassFilter( "vm.Log" );
    mer.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);
    mer.enable();
    vm.resume();
    EventQueue queue = vm.eventQueue();
    try {
      while( true ) {
        EventSet es = queue.remove();
        for ( EventIterator ei = es.eventIterator(); ei.hasNext(); ) {
          Event e = ei.nextEvent();
          if ( e instanceof MethodEntryEvent ) {
            MethodEntryEvent event = (MethodEntryEvent)e;
            if ( event.method().name().equals( "log" ) ) {
              List frameList = event.thread().frames( 0, 2 );
              StackFrame frame = (StackFrame)frameList.get(1);
              Location loc = frame.location();
              String callString = loc.method() + ": line[" + loc.lineNumber() + "]";
              frame = (StackFrame)frameList.get(0);
              ObjectReference ref = frame.thisObject();
              LocalVariable locVar = frame.visibleVariableByName( "message" );
              Value value = vm.mirrorOf( callString + "-->" + frame.getValue( locVar ));
              frame.setValue( locVar, value );
            }
            vm.resume();
          }
          else if( e instanceof VMDeathEvent ) return;
        }
      }
    }
    catch ( InterruptedException ie ) { ie.printStackTrace(); }
  }
}

Class 2-
package vm;
import java.io.*;
public class Log {
  private PrintWriter out;
  private static Log instance;
  public static Log getInstance() {
    if ( instance == null ) instance = new Log();
    return instance;
  }
  private Log() {
    try { out = new PrintWriter( new FileOutputStream( "logtest" ) ); }
    catch( Exception e ) { e.printStackTrace(); }
  }
  public synchronized void log( String message ) {
    out.println( message );
    out.flush();
  }
}

Class 3-
package vm;
public class LogMain {
  public LogMain() {}
  public static void main(String[] args) {
    LogMain logMain1 = new LogMain();
    Log log = Log.getInstance();
    new Thread( new OtherThread() ).start();
    for ( int i = 0; i < 10; i ++ ) {
      try { Thread.sleep( 1000 ); }
      catch ( Exception e ) { e.printStackTrace(); }
      log.log( "this is message #" + i );
    }
  }
}
0
 
LVL 2

Expert Comment

by:functionpointer
ID: 8093357
Class 4-
package vm;
public class OtherThread implements Runnable {
  public OtherThread() {}
  public void run() {
    Log log = Log.getInstance();
    for( int i = 0; i < 10; i++ ) {
      try { Thread.sleep( 1000 ); } catch(Exception e) {e.printStackTrace();}
      log.log( "this is from left field.");
    }
  }
}


If you compile and run this example, you'll get the idea. The output looks like this.

vm.LogMain.main(java.lang.String[]): line[11]-->"this is message #0"
vm.OtherThread.run(): line[8]-->"this is from left field."
vm.LogMain.main(java.lang.String[]): line[11]-->"this is message #1"
vm.OtherThread.run(): line[8]-->"this is from left field."
vm.LogMain.main(java.lang.String[]): line[11]-->"this is message #2"
vm.OtherThread.run(): line[8]-->"this is from left field."
vm.LogMain.main(java.lang.String[]): line[11]-->"this is message #3"
vm.OtherThread.run(): line[8]-->"this is from left field."

etc, etc....

Yeah, its kind complex, but hey...  you asked.

IMO, unless you want to implement this kind of complexity, it would be alot easier to just force the caller to identify himself to the log method.

BUT as far as
>>You'll have to pass a reference
Definitely not. You CAN do what you originally asked.  It is just a bit difficult.
0
 
LVL 2

Expert Comment

by:functionpointer
ID: 8093362
BTW, if you DO decide to play with this, make sure you modify the line
  ((Connector.Argument)map.get("main")).setValue( "-classpath \"c:/vm/classes\" vm.LogMain" );
to suit your classpath so it will work. :)

0
 
LVL 92

Expert Comment

by:objects
ID: 8095475
> It is just a bit difficult.

And most probably impractical for the required purpose due to performance reasons.
0
 
LVL 2

Expert Comment

by:bkrahmer
ID: 8095516
Using stacktraces and reflection is rediculous, IMO.  There is a much simpler way.  

interface UsesLogging
{
  String whoIsLogging();
}

class a implements UsesLogging
{
..
  logger.write(this, "message");
}

class Logger
{
  public void write(UsesLogging who, String message)
  {
     finalMessage = who.whoIsLogging() + ...
}

cheers,
brian
0
 
LVL 2

Expert Comment

by:functionpointer
ID: 8098264
>>And most probably impractical for the required purpose due to performance reasons.

objects,
Actually, the performance isn't as bad as you would think.
I would think a better dig on it would be "but you have to launch your app in another vm". ;) Either way, I was just answering the question.


>Using stacktraces and reflection is rediculous, IMO.

bkrahmer,
IMO, the only thing riduculous here is how many people FAIL to read what has been posted before and continue to post the same answers OVER and OVER.
>CEHJ:  You'll have to pass a reference
CEHJ posted that solution a long time ago.  Everyone agrees it is the right course of action. Either way, I was just answering the question.
FYI, it probably isn't helping wabar telling him his suggestion of using reflection is 'rediculous'. I am not positive, but confident, that's not what he's looking for.
0
 

Author Comment

by:wabar
ID: 8103423
Thank you everyone for the excellent repsonses over the weekend. I will further analyze the potential solutions and let everyone know what I choose to do : )
0
 
LVL 9

Expert Comment

by:doronb
ID: 8160007
You can try this approach:

At the begining of your class (the one that has to identify who's calling it) add the following code:

  // Static method to find out which class called our method..
  private static Method sysGetCallerClass;
  protected static int opOrder = 0;

  static {
    try {
      sysGetCallerClass = System.class.getDeclaredMethod("getCallerClass", null);
      sysGetCallerClass.setAccessible(true);
    } catch (Exception ex) {
    }
  }

Then, when you need to identify the calling class, add this code to get it:

    Class callingClass = null;
    try {
      callingClass = (Class)sysGetCallerClass.invoke(null, null);
    } catch (Exception ex) {
    }

WARNING: This works because of an undocumented method in the java.lang.System class, newer versions of the JDK (higher than 1.4) or non-Sun JDK implementations may not include this method.

Hope this helps.
Doron
0
 
LVL 9

Expert Comment

by:doronb
ID: 8160018
P.S. please disregard the opOrder, its not needed... sorry :)
0
 
LVL 9

Expert Comment

by:doronb
ID: 8160658
wabar, just wondering if you saw my post..
0

Featured Post

Optimize your web performance

What's in the eBook?
- Full list of reasons for poor performance
- Ultimate measures to speed things up
- Primary web monitoring types
- KPIs you should be monitoring in order to increase your ROI

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This was posted to the Netbeans forum a Feb, 2010 and I also sent it to Verisign. Who didn't help much in my struggles to get my application signed. ------------------------- Start The idea here is to target your cell phones with the correct…
Introduction This article is the second of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers the basic installation and configuration of the test automation tools used by…
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:
This tutorial explains how to use the VisualVM tool for the Java platform application. This video goes into detail on the Threads, Sampler, and Profiler tabs.
Suggested Courses
Course of the Month10 days, 16 hours left to enroll

770 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question