I made a windows service from a jar file using WinRun4J, so far it's very basic.
package org.boris.winrun4j.test;
import java.io.BufferedWriter;
import java.io.FileWriter;
import org.boris.winrun4j.Service;
import org.boris.winrun4j.ServiceException;
public class ServiceWrite implements Service
{
private volatile boolean shutdown = false;
public int serviceMain(String[] args) throws ServiceException {
int count = 0;
while (!shutdown) {
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
}
try {
FileWriter fstream = new FileWriter("result.txt");
BufferedWriter out = new BufferedWriter(fstream);
out.write("Counts: " + count);
out.close();
} catch (Exception e){
}
count++;
}
return 0;
}
public int serviceRequest(int control) throws ServiceException {
switch (control) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
shutdown = true;
break;
}
return 0;
}
}
When the service is started it just keeps writing every couple of seconds to result.txt located in the root folder.. (Just for trying out WinRun4J)
Now my question is, can I do a method in the service jar, like this
public void write(String s){
//Write some string s to result.txt
}
And then invoke this method from a different java file on the system, i.e
java WriteToFile SomeString
Where WriteToFile is supposed to invoke write with some argument.
Is it possible? if so, how ?
The overall purpose of this is to have a service running where I can invoke methods via a GUI.
to "invoke methods via a GUI", you can't do it with WinRun4J.
in general rule, a Windows Service can't have a GUI for security reason (except for special cases).
However, there are other tools to create a windows service from a Java application, with which it will be possible to have a service with GUI and able to interact with the Desktop.
Related
I am trying to write test methods in Intellij with jUnit. I can succesfully write to the file, however, I need to show that I can write a test method for this. I push in the write direction would be great. My ServerController class write to a file, checks a file for a username, and verifies username and password. This project is for a class and it appears that the most important lesson is learning about documentation (requirements, design, and testing). So, here I am trying to test.
package appLayer;
import java.io.*;
import java.util.Scanner;
public class ServerController {
public void writetoFile(String writeUsername, String writePassword) throws IOException {
PrintWriter fileWriting = new PrintWriter(new FileOutputStream("/Users/dannielsotelo/Documents/database.txt", true));
fileWriting.println(writeUsername + "," + writePassword); //
System.out.println(writeUsername + " was saved to database.txt");
fileWriting.close();
}
public boolean findUsername(String fUsername) {
File file = new File("/Users/dannielsotelo/Documents/database.txt");
try{
Scanner scanner = new Scanner(file);
int lineNum = 0;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
lineNum++;
if(line.contains(fUsername))
return true;
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
return false;
}
public boolean verifyCredentials(String lUsername, String lPassword) {
File file = new File("/Users/dannielsotelo/Documents/database.txt");
try{
Scanner scanner = new Scanner(file);
int lineNum = 0;
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
lineNum++;
if(line.contains(lUsername) && line.contains(lPassword))
return true;
}
} catch (FileNotFoundException e) {
System.out.println("File not found");
}
return false;
}
}
and my ServerControllerTest class
package appLayer;
import org.junit.Test;
import static org.junit.Assert.*;
public class ServerControllerTest {
#Test
public void testwritetoFile() {
}
#Test
public void findUsername() {
}
#Test
public void verifyCredentials() {
}
}
First, you should get rid of the redundant code when scanning the files. It would be sufficient to have one method that searches for arbitrary strings since technically it doesn't matter if you search for a username or a password.
Second, for testing purposes you really should not hard-code the database file. When writing JUnit tests you don't want your actual file to be written or read. Pass the file or the path to it as a constructor parameter.
Writing a test method is quite straight-forward. Write a value to the database, then read it and check if the previously written data is there. It's a good idea to start with a blank file for every test.
PS.: In terms of security, the approach of checking if a line contains username and password is a little disaster :D
Search this forum for testing void methods. But anyway your code is not really testable in sensible way. While you can test methods that return some value, you don't give it enough starting conditions to be sure they do what you want.
Don't hardcode path to files in the method, you'll be better off if it's an argument to the method. Then in tests you can be sure that if you create file with a specified content the method will do this or that or throw an exception if you don't create the file at all.
I want to print my file directly from webpage. For that I am using following reference and trying to implement same with ZUL and Composer.
http://tonny-bruckers.blogspot.in/2012/11/printing-files-directly-from-web-page.html
ZUL File :-
<zk>
<applet code = "PrintApplet.class" codebase = "applet/" id="printApplet" width="400px" style="border: 1px" />
<button id="btnClickMe" label="Click Me" sclass="light-btn"/>
</zk>
PrintApplet.class is present inside "WebContent/applet".
public class AppletComposer extends GenericForwardComposer<Window> {
private Applet printApplet;
public void doOverrideAfterComposer(Window comp) throws Exception {
}
public void onClick$btnClickMe(Event event) throws Exception {
String Originalstr = "ByteArrayInputStream Example!";
byte[] Originalbytes = Originalstr.getBytes();
ByteArrayInputStream bis=new ByteArrayInputStream(Originalbytes);
printApplet.invoke("print", bis);
}
}
PrintApplet Class :-
public class PrintApplet extends Applet {
public void init()
{
}
public void print(ByteArrayInputStream bis) throws PrintException
{
PrintService service = PrintServiceLookup.lookupDefaultPrintService();
if (service != null) {
DocFlavor psFormat = DocFlavor.INPUT_STREAM.PDF;
PrintRequestAttributeSet attributes = new HashPrintRequestAttributeSet();
DocPrintJob job = service.createPrintJob();
Doc pdfDoc = new SimpleDoc(bis,psFormat, null);
job.print(pdfDoc, attributes);
}
}
}
I am able to invoke PrintApplet with this approach but getting Null as service. PrintApplet is working fine with AppletViewer and with normal Java Application but unable to get default printer service while using the above approach.
First I want to mention that APPLET IS ALWAYS RUN ON CLIENT SIDE and APPLET only communicates to the server which from it is downloaded.
That’s why we have to specify codebase directory, so that we can download the applet on client browser by then JAVA Platform Environment plugin from browser takes on control, which in turn run on client JRE environment.
So we have to be very carefully that JDK environment is installed properly.
To trace applet log we can use of java applet console tool ‘jconsole’.
Steps for APPLET to run properly on client browser :
At browser (firefox,chrome,opera) check whether JAVA Platform plugin is there or not, because to run applet from the browser we require that plugin installed and enabled.
If you are working on linux machine: than it is bit complex,
You can find how to enable plugin for LINUX-BROWSER from here:
http://www.oracle.com/technetwork/java/javase/manual-plugin-install-linux-136395.html
Enable console log for applet, when it executes on client JRE, we can look into it for tracing.
Path : JDK_DIR/bin/jcontrol
[JControl Window][1]
For only development purpose: you can lower the security
We have to clear the cache of applet, every time we build new applet, to reflect the latest change we need to clear the cache first, otherwise it'll load cached applet class.
To clear we can use 'javaws -viewer'
Path : JAVA_HOME/bin/javaws -viewer
[Clear Applet Cache][2]
As per your code, your server side code (zul and composer) are perfect but the problem is at applet code.
You are looking for a default printer in print() method, which are one time configuration codes. It has to be in init().
PrintApplet.java
public class PrintApplet extends Applet {
private static final long serialVersionUID = 1L;
private PrintService service;
public void init()
{
JOptionPane.showMessageDialog(null, "Inside INIT()");
if(null==service){
service = PrintServiceLookup.lookupDefaultPrintService();
System.out.println(service.getName());
} else {
System.out.println(service.getName());
}
}
public void print(String str) throws PrintException
{
JOptionPane.showMessageDialog(null, "Inside print()");
JOptionPane.showMessageDialog(null, "String is:::"+str);
cPrint cP = new cPrint(str, service);
System.out.println((Integer) AccessController.doPrivileged(cP));
}
}
And you need another implementation of AccessController to give access to the default printer locate and print.
cPrint.java
class cPrint implements PrivilegedAction<Object> {
String str;
PrintService service;
public cPrint(String str, PrintService argPrintService) {
this.str = str;
this.service = argPrintService;
};
public Object run() {
// privileged code goes here
InputStream is = null;
try
{
JOptionPane.showMessageDialog(null, "String is:::"+str);
byte[] Originalbytes = str.getBytes();
JOptionPane.showMessageDialog(null, "Original bytes:::"+Originalbytes);
is=new ByteArrayInputStream(Originalbytes);
FileWriter fstream = new FileWriter("/home/test/out.pdf");
BufferedWriter out = new BufferedWriter(fstream);
for (Byte b: Originalbytes) {
out.write(b);
}
out.close();
DocPrintJob printJob = service.createPrintJob();
Doc doc;
DocAttributeSet docAttrSet = new HashDocAttributeSet();
PrintRequestAttributeSet printReqAttr = new HashPrintRequestAttributeSet();
doc = new SimpleDoc(is, DocFlavor.INPUT_STREAM.AUTOSENSE, docAttrSet);
PrintJobWatcher pjDone = new PrintJobWatcher(printJob);
printJob.print(doc, printReqAttr);
pjDone.waitForDone();
// It is now safe to close the input stream
is.close();
}
catch (Exception e) {
e.printStackTrace();
System.out.println(e);
return 1;
}
return 0;
}
static class PrintJobWatcher {
// true iff it is safe to close the print job's input stream
boolean done = false;
PrintJobWatcher(DocPrintJob job) {
// Add a listener to the print job
job.addPrintJobListener(new PrintJobAdapter() {
public void printJobCanceled(PrintJobEvent pje) {
allDone();
}
public void printJobCompleted(PrintJobEvent pje) {
allDone();
}
public void printJobFailed(PrintJobEvent pje) {
allDone();
}
public void printJobNoMoreEvents(PrintJobEvent pje) {
allDone();
}
void allDone() {
synchronized (PrintJobWatcher.this) {
done = true;
PrintJobWatcher.this.notify();
}
}
});
}
public synchronized void waitForDone() {
try {
while (!done) {
wait();
}
} catch (InterruptedException e) {
}
}
}
}
cPrint(str,PrintService)
Where str can be file name if you want file to be print, or byte array string.
Here in my example, I expected byte array, so I create pdf file from byte array given by the applet from the composer and then it'll sent to the default printer to the given PrintService.
So Actual flow for applet in zk to get access for default printer and to print is by this [graph][3].
So a little background;
I am working on a project in which a servlet is going to release crawlers upon a lot of text files within a file system. I was thinking of dividing the load under multiple threads, for example:
a crawler enters a directory, finds 3 files and 6 directories. it will start processing the files and start a thread with a new crawler for the other directories. So from my creator class I would create a single crawler upon a base directory. The crawler would assess the workload and if deemed needed it would spawn another crawler under another thread.
My crawler class looks like this
package com.fujitsu.spider;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
public class DocumentSpider implements Runnable, Serializable {
private static final long serialVersionUID = 8401649393078703808L;
private Spidermode currentMode = null;
private String URL = null;
private String[] terms = null;
private float score = 0;
private ArrayList<SpiderDataPair> resultList = null;
public enum Spidermode {
FILE, DIRECTORY
}
public DocumentSpider(String resourceURL, Spidermode mode, ArrayList<SpiderDataPair> resultList) {
currentMode = mode;
setURL(resourceURL);
this.setResultList(resultList);
}
#Override
public void run() {
try {
if (currentMode == Spidermode.FILE) {
doCrawlFile();
} else {
doCrawlDirectory();
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("SPIDER # " + URL + " HAS FINISHED.");
}
public Spidermode getCurrentMode() {
return currentMode;
}
public void setCurrentMode(Spidermode currentMode) {
this.currentMode = currentMode;
}
public String getURL() {
return URL;
}
public void setURL(String uRL) {
URL = uRL;
}
public void doCrawlFile() throws Exception {
File target = new File(URL);
if (target.isDirectory()) {
throw new Exception(
"This URL points to a directory while the spider is in FILE mode. Please change this spider to FILE mode.");
}
procesFile(target);
}
public void doCrawlDirectory() throws Exception {
File baseDir = new File(URL);
if (!baseDir.isDirectory()) {
throw new Exception(
"This URL points to a FILE while the spider is in DIRECTORY mode. Please change this spider to DIRECTORY mode.");
}
File[] directoryContent = baseDir.listFiles();
for (File f : directoryContent) {
if (f.isDirectory()) {
DocumentSpider spider = new DocumentSpider(f.getPath(), Spidermode.DIRECTORY, this.resultList);
spider.terms = this.terms;
(new Thread(spider)).start();
} else {
DocumentSpider spider = new DocumentSpider(f.getPath(), Spidermode.FILE, this.resultList);
spider.terms = this.terms;
(new Thread(spider)).start();
}
}
}
public void procesDirectory(String target) throws IOException {
File base = new File(target);
File[] directoryContent = base.listFiles();
for (File f : directoryContent) {
if (f.isDirectory()) {
procesDirectory(f.getPath());
} else {
procesFile(f);
}
}
}
public void procesFile(File target) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(target));
String line;
while ((line = br.readLine()) != null) {
String[] words = line.split(" ");
for (String currentWord : words) {
for (String a : terms) {
if (a.toLowerCase().equalsIgnoreCase(currentWord)) {
score += 1f;
}
;
if (currentWord.toLowerCase().contains(a)) {
score += 1f;
}
;
}
}
}
br.close();
resultList.add(new SpiderDataPair(this, URL));
}
public String[] getTerms() {
return terms;
}
public void setTerms(String[] terms) {
this.terms = terms;
}
public float getScore() {
return score;
}
public void setScore(float score) {
this.score = score;
}
public ArrayList<SpiderDataPair> getResultList() {
return resultList;
}
public void setResultList(ArrayList<SpiderDataPair> resultList) {
this.resultList = resultList;
}
}
The problem I am facing is that in my root crawler I have this list of results from every crawler that I want to process further. The operation to process the data from this list is called from the servlet (or main method for this example). However the operations is always called before all of the crawlers have completed their processing. thus launching the operation to process the results too soon, which leads to incomplete data.
I tried solving this using the join methods but unfortunately I cant seems to figure this one out.
package com.fujitsu.spider;
import java.util.ArrayList;
import com.fujitsu.spider.DocumentSpider.Spidermode;
public class Main {
public static void main(String[] args) throws InterruptedException {
ArrayList<SpiderDataPair> results = new ArrayList<SpiderDataPair>();
String [] terms = {"SERVER","CHANGE","MO"};
DocumentSpider spider1 = new DocumentSpider("C:\\Users\\Mark\\workspace\\Spider\\Files", Spidermode.DIRECTORY, results);
spider1.setTerms(terms);
DocumentSpider spider2 = new DocumentSpider("C:\\Users\\Mark\\workspace\\Spider\\File2", Spidermode.DIRECTORY, results);
spider2.setTerms(terms);
Thread t1 = new Thread(spider1);
Thread t2 = new Thread(spider2);
t1.start();
t1.join();
t2.start();
t2.join();
for(SpiderDataPair d : spider1.getResultList()){
System.out.println("PATH -> " + d.getFile() + " SCORE -> " + d.getSpider().getScore());
}
for(SpiderDataPair d : spider2.getResultList()){
System.out.println("PATH -> " + d.getFile() + " SCORE -> " + d.getSpider().getScore());
}
}
}
TL:DR
I really wish to understand this subject so any help would be immensely appreciated!.
You need a couple of changes in your code:
In the spider:
List<Thread> threads = new LinkedList<Thread>();
for (File f : directoryContent) {
if (f.isDirectory()) {
DocumentSpider spider = new DocumentSpider(f.getPath(), Spidermode.DIRECTORY, this.resultList);
spider.terms = this.terms;
Thread thread = new Thread(spider);
threads.add(thread)
thread.start();
} else {
DocumentSpider spider = new DocumentSpider(f.getPath(), Spidermode.FILE, this.resultList);
spider.terms = this.terms;
Thread thread = new Thread(spider);
threads.add(thread)
thread.start();
}
}
for (Thread thread: threads) thread.join()
The idea is to create a new thread for each spider and start it. Once they are all running, you wait until each on is done before the Spider itself finishes. This way each spider thread keeps running until all of its work is done (thus the top thread runs until all children and their children are finished).
You also need to change your runner so that it runs the two spiders in parallel instead of one after another like this:
Thread t1 = new Thread(spider1);
Thread t2 = new Thread(spider2);
t1.start();
t2.start();
t1.join();
t2.join();
You should use a higher-level library than bare Thread for this task. I would suggest looking into ExecutorService in particular and all of java.util.concurrent generally. There are abstractions there that can manage all of the threading issues while providing well-formed tasks a properly protected environment in which to run.
For your specific problem, I would recommend some sort of blocking queue of tasks and a standard producer-consumer architecture. Each task knows how to determine if its path is a file or directory. If it is a file, process the file; if it is a directory, crawl the directory's immediate contents and enqueue new tasks for each sub-path. You could also use some properly-synchronized shared state to cap the number of files processed, depth, etc. Also, the service provides the ability to await termination of its tasks, making the "join" simpler.
With this architecture, you decouple the notion of threads and thread management (handled by the ExecutorService) with your business logic of tasks (typically a Runnable or Callable). The service itself has the ability to tune how to instantiate, such as a fixed maximum number of threads or a scalable number depending on how many concurrent tasks exist (See factory methods on java.util.concurrent.Executors). Threads, which are more expensive than the Runnables they execute, are re-used to conserve resources.
If your objective is primarily something functional that works in production quality, then the library is the way to go. However, if your objective is to understand the lower-level details of thread management, then you may want to investigate the use of latches and perhaps thread groups to manage them at a lower level, exposing the details of the implementation so you can work with the details.
I have been trying to get a Windows service running from my JAR file. WinRun4j seems to be able to do the job, but I can't get it to work. I am especially finding it quite difficult to debug. I tried several methods for logging (writing to a .txt file, WinRun4j's EventLog class) but I can't seem to generate any output.
The service installs fine (eventually..) and I can start it. It should start a Jetty server that generates an XML file that can be reached over HTTP. The app works for a stand-alone version, just not for the service. The service is started, but as soon as I call the URL it stops without generating an error.
This is my Service class:
package com.some.package;
import org.boris.winrun4j.AbstractService;
import org.boris.winrun4j.ServiceException;
/**
* A basic service.
*/
public class StockService extends AbstractService {
private StockServer srv;
public int serviceMain(String[] args) throws ServiceException {
while (!shutdown) {
try {
Thread.sleep(5000);
} catch(InterruptedException e) {
}
if(srv == null) {
try {
srv = new StockServer();
srv.start();
} catch (Exception e) {
}
}
}
return 0;
}
}
I found out that the service didn't want to start if I started the Jetty server from the serviceMain class. I had to start a new thread. So StockServer extends Thread:
public class StockServer extends Thread {
private Server server;
public void run() {
if (server == null) {
try {
server = new Server(8080);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/example");
StockServlet stockServlet = new StockServlet();
context.addServlet(new ServletHolder(stockServlet), "/stock/*");
server.setHandler(context);
server.setStopAtShutdown(true);
server.start();
server.join();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}
Since it runs perfectly fine as Java application I just don't know how to get this thing debugged. I hope one of you can point me in the right direction :).
I ended up using the Java Service Wrapper (JSW). This seemed a lot more complex but ended up to be quite easy. It also provides logging by default so I could easily fix the errors. The JSW had problems finding the correct JDK, since JSW is 32bit and I installed JDK1.7 64 bit (and 1.6 32bit). Installing JDK1.7 32bit fixed it. That might have been the problem with WinRun4j as well, but that is something I will never know :).
I want to build call graphs on the fly, starting at an arbitrary method call or with a new thread, which ever is easier, from within the running JVM itself. (this piece of software is going to be a test fixture for load testing another piece of software that consumes call graphs)
I understand there are some SPI interfaces, but it looks like you need to run -javaagent flag with them. I want to access this directly in the VM itself.
Ideally, I'd like to get a callback for entry and exit of each method call, parameters to that method call, and time in that method. Within a single thread obviously.
I know AOP could probably do this, but I'm just wondering if there are tools within the JDK that would allow me to capture this.
There is no such API provided by the JVM— even for agents started with -javaagent. The JVM TI is a native interface provided for native agents started with the -agent option or for debuggers. Java agents might use the Instrumentation API which provides the lowlevel feature of class instrumentation but no direct profiling capability.
There are two types of profiling implementations, via sampling and via instrumentation.
Sampling works by recording stack traces (samples) periodically. This does not trace every method call but still detect hot spots as they occur multiple times in the recorded stack traces. The advantage is that it does not require agents nor special APIs and you have the control over the profiler’s overhead. You can implement it via the ThreadMXBean which allows you to get stack traces of all running threads. In fact, even a Thread.getAllStackTraces() would do but the ThreadMXBean provides more detailed information about the threads.
So the main task is to implement an efficient storage structure for the methods found in the stack traces, i.e. collapsing occurrences of the same method into single call tree items.
Here is an example of a very simple sampler working on its own JVM:
import java.lang.Thread.State;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Sampler {
private static final ThreadMXBean TMX=ManagementFactory.getThreadMXBean();
private static String CLASS, METHOD;
private static CallTree ROOT;
private static ScheduledExecutorService EXECUTOR;
public static synchronized void startSampling(String className, String method) {
if(EXECUTOR!=null) throw new IllegalStateException("sampling in progress");
System.out.println("sampling started");
CLASS=className;
METHOD=method;
EXECUTOR = Executors.newScheduledThreadPool(1);
// "fixed delay" reduces overhead, "fixed rate" raises precision
EXECUTOR.scheduleWithFixedDelay(new Runnable() {
public void run() {
newSample();
}
}, 150, 75, TimeUnit.MILLISECONDS);
}
public static synchronized CallTree stopSampling() throws InterruptedException {
if(EXECUTOR==null) throw new IllegalStateException("no sampling in progress");
EXECUTOR.shutdown();
EXECUTOR.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
EXECUTOR=null;
final CallTree root = ROOT;
ROOT=null;
return root;
}
public static void printCallTree(CallTree t) {
if(t==null) System.out.println("method not seen");
else printCallTree(t, 0, 100);
}
private static void printCallTree(CallTree t, int ind, long percent) {
long num=0;
for(CallTree ch:t.values()) num+=ch.count;
if(num==0) return;
for(Map.Entry<List<String>,CallTree> ch:t.entrySet()) {
CallTree cht=ch.getValue();
StringBuilder sb = new StringBuilder();
for(int p=0; p<ind; p++) sb.append(' ');
final long chPercent = cht.count*percent/num;
sb.append(chPercent).append("% (").append(cht.cpu*percent/num)
.append("% cpu) ").append(ch.getKey()).append(" ");
System.out.println(sb.toString());
printCallTree(cht, ind+2, chPercent);
}
}
static class CallTree extends HashMap<List<String>, CallTree> {
long count=1, cpu;
CallTree(boolean cpu) { if(cpu) this.cpu++; }
CallTree getOrAdd(String cl, String m, boolean cpu) {
List<String> key=Arrays.asList(cl, m);
CallTree t=get(key);
if(t!=null) { t.count++; if(cpu) t.cpu++; }
else put(key, t=new CallTree(cpu));
return t;
}
}
static void newSample() {
for(ThreadInfo ti:TMX.dumpAllThreads(false, false)) {
final boolean cpu = ti.getThreadState()==State.RUNNABLE;
StackTraceElement[] stack=ti.getStackTrace();
for(int ix = stack.length-1; ix>=0; ix--) {
StackTraceElement ste = stack[ix];
if(!ste.getClassName().equals(CLASS)||!ste.getMethodName().equals(METHOD))
continue;
CallTree t=ROOT;
if(t==null) ROOT=t=new CallTree(cpu);
for(ix--; ix>=0; ix--) {
ste = stack[ix];
t=t.getOrAdd(ste.getClassName(), ste.getMethodName(), cpu);
}
}
}
}
}
Profilers hunting for every method invocation without going through the debugging API use instrumentation to add notification code to every method they are interested in. The advantage is that they never miss a method invocation but on the other hand they are adding a significant overhead to the execution which might influence the result when searching for hot spots. And it’s way more complicated to implement. I can’t give you a code example for such a byte code transformation.
The Instrumentation API is provided to Java agents only but in case you want to go into the Instrumentation direction, here is a program which demonstrates how to connect to its own JVM and load itself as a Java agent:
import java.io.*;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
// this API comes from the tools.jar of your JDK
import com.sun.tools.attach.*;
public class SelfAttacher {
public static Instrumentation BACK_LINK;
public static void main(String[] args) throws Exception {
// create a special property to verify our JVM connection
String magic=UUID.randomUUID().toString()+'/'+System.nanoTime();
System.setProperty("magic", magic);
// the easiest way uses the non-standardized runtime name string
String name=ManagementFactory.getRuntimeMXBean().getName();
int ix=name.indexOf('#');
if(ix>=0) name=name.substring(0, ix);
VirtualMachine vm;
getVM: {
try {
vm = VirtualMachine.attach(name);
if(magic.equals(vm.getSystemProperties().getProperty("magic")))
break getVM;
} catch(Exception ex){}
// if the easy way failed, try iterating over all local JVMs
for(VirtualMachineDescriptor vd:VirtualMachine.list()) try {
vm=VirtualMachine.attach(vd);
if(magic.equals(vm.getSystemProperties().getProperty("magic")))
break getVM;
vm.detach();
} catch(Exception ex){}
// could not find our own JVM or could not attach to it
return;
}
System.out.println("attached to: "+vm.id()+'/'+vm.provider().type());
vm.loadAgent(createJar().getAbsolutePath());
synchronized(SelfAttacher.class) {
while(BACK_LINK==null) SelfAttacher.class.wait();
}
System.out.println("Now I have hands on instrumentation: "+BACK_LINK);
System.out.println(BACK_LINK.isModifiableClass(SelfAttacher.class));
vm.detach();
}
// create a JAR file for the agent; since our class is already in class path
// our jar consisting of a MANIFEST declaring our class as agent only
private static File createJar() throws IOException {
File f=File.createTempFile("agent", ".jar");
f.deleteOnExit();
Charset cs=StandardCharsets.ISO_8859_1;
try(FileOutputStream fos=new FileOutputStream(f);
ZipOutputStream os=new ZipOutputStream(fos)) {
os.putNextEntry(new ZipEntry("META-INF/MANIFEST.MF"));
ByteBuffer bb = cs.encode("Agent-Class: "+SelfAttacher.class.getName());
os.write(bb.array(), bb.arrayOffset()+bb.position(), bb.remaining());
os.write(10);
os.closeEntry();
}
return f;
}
// invoked when the agent is loaded into the JVM, pass inst back to the caller
public static void agentmain(String agentArgs, Instrumentation inst) {
synchronized(SelfAttacher.class) {
BACK_LINK=inst;
SelfAttacher.class.notifyAll();
}
}
}
You can modify bytecode of each method adding routine to log method's enter/exit events. Javassist will help you http://www.csg.ci.i.u-tokyo.ac.jp/~chiba/javassist/
Also check out a nice tutorial: https://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html