I need to produce two separate jar files, both interact with each other but do different things. I have two projects loaded into Eclipse, but both use a lot of the same imports, and so I have them in subfolders under the same workspace.
One of them gets "java class xxxxcould not be found" when I try to run it.
As I attempted to fix that, I was comparing the two projects and noticed that a folder was part of the external build path of the working one, but not the non-working one. I added it to the non-working one and broke the working one.
The one that had been working now has an error on the main class name. I call the program ZSTATEngine, and so the class is
public class ZSTATEngine implements ETOSFilterEngine
Now that name is highlighted and when I mouse over it is says:"the type ZSTATEngine must implement the inherited abstract method ETOSFilterEngine.doFilter(MessageBlock)"
What could have changed? It was working fine before, and nothing in the code itself changed. I don't understand how the referenced libraries work, but it at least appears nothing changed in their structure in the formerly-working project.
Ok some further information: I do actually have a section within that class:
public MessageBlock doFilter(MessageBlock msgBlock)
so I am implementing that method... but that method has an error inside of it now,
"The method addFilteredMessage(MessageBlock) in the type FilterFramework is not applicable or the arguments (MessageBlock) ...
How could that have gone bad? It was working fine too.
Here's the full code:
package com.ibm.tpf.internal;
import java.awt.Color;
/*import java.util.ArrayList;*/
/*import java.util.*;*/
import com.ibm.tpf.etos.TPFFilter.*;
//import com.ibm.tpf.etos.TPFFilter.TernarySwitch;
import com.ibm.tpf.etos.api.*;
/*
import com.ibm.tpf.etos.api.Constants;
import com.ibm.tpf.etos.api.MessageBlock;
*/
import com.ibm.tpf.etos.filter.*;
/*
import com.ibm.tpf.etos.filter.ETOSFilterEngine;
import com.ibm.tpf.etos.filter.FilterConfigurationException;
import com.ibm.tpf.etos.filter.FilterFramework;
import com.ibm.tpf.etos.filter.FilterRuntimeException;
*/
public class ZSTATEngine implements ETOSFilterEngine {
FilterFramework fw = null;
String[] names = null;
public ZSTATEngine(FilterFramework filFW, String[] parms) {
super();
this.fw = filFW;
}
/* AAES0009I 13.45.01 FROM TA 05 : AUTC0000I TOSFCOLOR_GREEN TOSBCOLOR_NONE TOSHOLD_0 TOSSAVE_0 TOSALERT_0 AUTC1111I 12.04.41 OK */
public MessageBlock doFilter(MessageBlock msgBlock) throws FilterRuntimeException {
if(msgBlock.getMsgID().equals("AAES0009I")) { /* only handle messages that start with AAES0009I */
if(msgBlock.getMsg().indexOf("ZUVRT") != -1) { /* if the message contains "ZUVRT" then let it through. We want to react to the result of it, not the ZUVRT itself. */
return msgBlock;
}
if(msgBlock.getMsg().indexOf("AUTC0000I") != -1) { /* search string to see if "AUTC0000I is in it. If it is then do..." */
String myString = msgBlock.getMsg();
Color fColor = Color.WHITE; /* set default colors */
Color bColor = Color.BLACK;
msgBlock.setSuppressed(TernarySwitch.ON); /* suppress original message to display new one */
String[] myStringParts = myString.split("\\s+",13); /* divide message into 13 parts. The 13th part is everything remaining. */
String finalPart = myStringParts[12].toString(); /* print last part to the screen */
MessageBlock mb = new MessageBlock(finalPart, Constants.ETOS_ONE_MSG);
String fColorMsg = myStringParts[7].toString(); /* Process the foreground color portion */
if (!fColorMsg.contains("NONE")) {
fColor = ColorStringInterpreter(fColorMsg);
mb.setForeground(fColor);
}
String bColorMsg = myStringParts[8].toString(); /* Process the background color portion */
if (!bColorMsg.contains("NONE")) {
bColor = ColorStringInterpreter(bColorMsg);
mb.setBackground(bColor);
}
String holdMsg = myStringParts[9].toString(); /* Process the hold message portion */
if (holdMsg.toUpperCase().startsWith("TOS")) { /* if it starts with TOS, grab only the number at the end */
String[] holdPart = holdMsg.split("_",2);
if (holdPart[1].toString().equals("1")) {
mb.setHeld(TernarySwitch.ON);
}
}
else {
if (holdMsg.equals("1")) { /* otherwise, just use the number */
mb.setHeld(TernarySwitch.ON);
}
}
String saveMsg = myStringParts[10].toString(); /* Process the save areas. These have two formats currently: TOSSAVE_X_X_X_X and BBBBBBBBB, where X is a digit 1-32, and B is binary. */
if (saveMsg.toUpperCase().startsWith("TOS")) {
String[] savePart = saveMsg.split("_"); /* handle the multiple digit save areas, and ignore the first split which is TOSSAVE */
if (!savePart[1].toString().equals("0")) {
long areaBits = 0;
for (int i=1; i<savePart.length; i++) {
areaBits |= 1L << Integer.parseInt(savePart[i]);
}
mb.setSave(areaBits);
}
}
else { /* otherwise, just use the binary string directly */
long areaBits = Long.parseLong(myStringParts[10].toString(), 2);
mb.setSave(areaBits);
}
fw.addFilteredMessage(mb); /* this is the command that pieces the whole message together */
}
}
int plusLocation = msgBlock.getMsg().lastIndexOf('+');
if (plusLocation > 0) {
MessageBlock mb1 = new MessageBlock(msgBlock.getMsg(), msgBlock.getFlag());
fw.addFilteredMessage(mb1);
msgBlock.setSuppressed(TernarySwitch.ON);
MessageBlock mb2 = new MessageBlock("", Constants.ETOS_ONE_MSG);
fw.addFilteredMessage(mb2);
}
return msgBlock; /* whatever gets returned is what the system prints */
}
private Color ColorStringInterpreter(String colorMsg) throws FilterRuntimeException {
if (colorMsg.toUpperCase().startsWith("TOS")) { /* if it starts with TOS, then we're using color names */
String[] colorParts = colorMsg.split("_",2);
String colorTxt = colorParts[1].toString().trim();
if (colorTxt.toUpperCase() != "NONE") {
Color finalColor = Colors.fromString(colorTxt);
return finalColor;
}
}
else {
String[] colorParts = colorMsg.split("_",3); /* otherwise we're using RGB values */
String sRed = colorParts[0].toString().trim();
String sGreen = colorParts[1].toString().trim();
String sBlue = colorParts[2].toString().trim();
/*mb = new MessageBlock(sRed, Constants.ETOS_ONE_MSG);*/
int iRed = Integer.parseInt(sRed);
int iGreen = Integer.parseInt(sGreen);
int iBlue = Integer.parseInt(sBlue);
Color finalColor = new Color (iRed, iGreen, iBlue);
return finalColor;
}
return null;
}
public String getName() {
return null;
}
public void modifyState(Object[] newParams) throws FilterConfigurationException, FilterRuntimeException {
}
public boolean isActive() {
return false;
}
public void shutdown() {
}
}
public class ZSTATEngine implements ETOSFilterEngine
According to above code, your class ZSTATEngine is implementing an interface ETOSFilterEngine, which means your class need to implement all the abstract methods of ETOSFilterEngine.
From Java doc:
Interfaces form a contract between the class and the outside world,
and this contract is enforced at build time by the compiler. If your
class claims to implement an interface, all methods defined by that
interface must appear in its source code before the class will
successfully compile.
Check the link: http://www-01.ibm.com/support/knowledgecenter/SSB23S_1.1.0.9/com.ibm.tpfops.doc_1112/aaeo1/fengapi.html?cp=SSB23S_1.1.0.9%2F2-3-10-3
Below are the 5 methods that are present in ETOSFilterEngine, which you need to implement.
public MessageBlock doFilter (MessageBlock) throws
FilterRuntimeException;
public void modifyState (Object[ ]) throws
FilterConfigurationException,
FilterRuntimeException;
public boolean isActive();
public void shutdown();
public String getName();
Above link has a code example on how to properly implement this interface. You can see that the class ZSTATEngine in the example is implementing all the 5 methods provided by ETOSFilterEngine.
Check the type of MessageBlock in your imports, it should be import
com.ibm.tpf.etos.api.MessageBlock; I can see that you have commented your import which is wrong.
Uncomment the line : import com.ibm.tpf.etos.api.MessageBlock;
AS mentioned by "Jakub Zaverka" perhaps you have two versions in classpath or build path. Check the jar order, whether is picking the right class... It will happen even if no code has changed.
One way to find it out is, just do an F3 on ETOSFilterEngine and click "Link with editor" option in package explorer. It will show the .class file and the jar from which its picked up.. If its from the wrong jar or old jar, just go to Project>Properties>Build Path>Order and Export and change the order of the right jar to the top, by clicking on Top button..
Related
My question is more of a design issue than anything else. I'm currently developing a classic Server-Client chat program in Java. Everything is fine until I get to the commands. I thought it would be convenient for users to send commands that would then be treated by the server for changing their nickname for example. The thing is I want to make flexible code and above all, object-oriented code. To avoid endless if/else if statements to know what command was typed I believe it would be better to create a class for each command which inherit from a superclass Command. Then I could return the specific command through a getCommand() function overriden in all subclasses. But it does not solve my problem at all. The server still needs to test with instanceof what command has been returned. One way to do it dynamically would be to sort of auto downcasting it from the superclass Command and then call the appropriate function in the server class. For example:
public void processCommand(CommandNick c) {}
public void processCommand(CommandKick c) {}
But I haven't found any proper way of doing that and even if I did, I feel like there's still a design issue here. And I am convinced there is a nice and flexible way to do it but days weren't enough for me to figure it out. Any ideas? Thanks in advance! :)
I assume your server receives the message as an Object with a Sender and a String. Create your Command classes, and in the server init code, make a HashMap<String, AbstractCommand> with a String as key and your AbstractCommand class as value. Your commands should extend this class. Register all your commands, like so:
commandRegistry.put("help", new HelpCommandHandler());
I assume a command is a message with a ! before it. So when you receive a message, check if it is a command:
Message message = (Your Message)
String messageBody = message.getBody();
Sender messageSender = message.getSender();
if(messageBody.startsWith("!")) {
// Split the message after every space
String[] commandParts = messageBody.split(" ");
// The first element is the command base, like: !help
String baseCommand = commandParts[0];
// Remove the first character from the base, turns !help into help
baseCommand = baseCommand.substring(1, baseCommand.length());
// Creates a new array for the arguments. The length is smaller, because we won't copy the command base
String[] args = new String[commandParts.length - 1];
// Copy the elements of the commandParts array from index 1 into args from index 0
if(args.length > 0) {
System.arraycopy(commandParts, 1, args, 0, commandParts.length - 1);
}
// Your parse method
processCommand(sender, baseCommand, args);
}
public void processCommand(Sender sender, String base, String[] args) {
if(commandRegistry.containsKey(base)) {
commandRegistry.get(base).execute(sender, args);
} else {
// Handle unknown command
}
}
public abstract class AbstractCommand {
public abstract void execute(Sender sender, String[] args);
}
Sample implementation. I assume your server is a Singleton, and you can get on Object of it with Server.get() or any similar method.
public class HelpCommandHandler extends AbstractCommand { /* !help */
#Override
public void execute(Sender sender, String[] args) {
sender.sendMessage("You asked for help."); // Your code might not work like this.
}
}
public class ChangeNickCommandHandler extends AbstractCommand { /* !changenick newNick */
#Override
public void execute(Sender sender, String[] args) {
// I assume you have a List with connected players in your Server class
String username = sender.getUsername(); // Your code might not work like this
Server server = Server.get(); // Get Server instance
server.getUsers().get(username).setNickname(args[0]); // Argument 0. Check if it even exists.
}
}
// Server class. If it isn't singleton, you can make it one like this:
public class Server {
private static Server self;
public static Server init(/* Your args you'd use in a constructor */) { self = new Server(); return get(); }
public static Server get() { return self; }
private List<User> users = new List<User>();
private HashMap<String, AbstractCommand> commandRegitry = new HashMap<>();
// Make construcor private, use init() instead.
private Server() {
commandRegistry.put("help", new HelpCommandHandler());
commandRegistry.put("changenick", new ChangeNickCommandHandler());
}
// Getters
public List<User> getUsers() {
return users;
}
public HashMap<String, AbstractCommand> getRegistry() {
return commandRegistry;
}
}
This is a bit of pseudo code to illustrate that your controller doesn't need to know about the command processors (no need for instanceof).
abstract class CommandProcessor {
/* return boolean if this Command processed the request */
public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat);
}
/* Handle anything */
public class CommandRemainder extends CommandProcessor {
#Override
public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat) {
chat.appendText("[" + user.getName() + "] " + command);
return true;
}
}
/* Handle color changing */
public class CommandColorizer extends CommandProcessor {
protected static List<String> ALLOWED_COLORS = new ArrayList<>(Arrays.asList("red", "blue", "green"));
#Override
public static boolean processCommand(String command, User user, Properties chatProperties, Chat chat) {
if ("fg:".equals(command.trim().substring(0,3)) {
String color = command.trim().substring(3).trim();
if (ALLOWED_COLORS.contains(color)) {
chat.setForeground(color);
}
return true;
}
return false;
}
}
public class ChatController {
protected Chat chat = new Chat();
protected User user = getUser();
protected Properties chatProperties = getChatProperties();
protected List<CommandProcessor> commandProcessors = getCommandProcessors();
{
chat.addChatListener(new ChatListener(){
#Override
public void userChatted(String userChatString) {
for (CommandProcessor processor : commandProcessors) {
if (processor.processCommand(userChatString, user, chatProperties, chat)) {
break;
}
}
}
});
}
List<CommandProcessor> getCommandProcessors() {
List<CommandProcessor> commandProcessors = new ArrayList<>();
commandProcessors.add(new CommandColorizer());
commandProcessors.add(new CommandRemainder()); // needs to be last
return commandProcessors;
}
}
Getting my feet wet on RxJava. I have a class that implements Iterable I want to convert to an Observable. Using Observable.from() seems easy. However I need to setup and tear-down the code that provides me the individual entries (the next() in the iterator.
When I run through the entire sequence, that's easy. I added the call to the hasNext() function and when there is no next I run the teardown. However one of the very promising operators I want to use is take(someNumber). If the taking stops before the Iterator runs out of items, the cleanup code never runs.
What can I do to get my cleanup running? If using something else than from(Iterable), I'm OK with that. I'm stuck on Java6 for now. To illustrate my predicament I created a minimal sample:
Update: Based on feedback not to mix Iterator and Iterable together, I updated the code below. To understand the original answers, the original code is in that gist.
Updated Test code (still bad):
import rx.Observable;
import rx.functions.Action0;
import rx.functions.Action1;
/**
* #author stw
*
*/
public class RXTest {
/**
* #param args
*/
public static void main(String[] args) {
ComplicatedObject co = new ComplicatedObject();
Observable<FancyObject> fancy = Observable.from(co);
// if the take is less than the elements cleanup never
// runs. If you take the take out, cleanup runs
fancy.take(3).subscribe(
new Action1<FancyObject>() {
public void call(FancyObject item) {
System.out.println(item.getName());
}
},
new Action1<Throwable>() {
public void call(Throwable error) {
System.out.println("Error encountered: " + error.getMessage());
}
},
new Action0() {
public void call() {
System.out.println("Sequence complete");
}
}
);
}
}
The fancy object:
import java.util.Date;
import java.util.UUID;
/**
* #author stw
*
*/
public class FancyObject {
private String name = UUID.randomUUID().toString();
private Date created = new Date();
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreated() {
return this.created;
}
public void setCreated(Date created) {
this.created = created;
}
}
The iterator:
import java.util.Iterator;
/**
* #author stw
*
*/
public class FancyIterator implements Iterator<FancyObject> {
private final ComplicatedObject theObject;
private int fancyCount = 0;
public FancyIterator(ComplicatedObject co) {
this.theObject = co;
}
public boolean hasNext() {
return this.theObject.hasObject(this.fancyCount);
}
public FancyObject next() {
FancyObject result = this.theObject.getOne(this.fancyCount);
this.fancyCount++;
return result;
}
}
The Iterable:
import java.util.Iterator;
import java.util.Vector;
/**
* #author stw
*
*/
public class ComplicatedObject implements Iterable<FancyObject> {
private boolean isInitialized = false;
Vector<FancyObject> allOfThem = new Vector<FancyObject>();
public Iterator<FancyObject> iterator() {
return new FancyIterator(this);
}
public boolean hasObject(int whichone) {
if (!this.isInitialized) {
this.setupAccesstoFancyObject();
}
return (whichone < this.allOfThem.size());
}
public FancyObject getOne(int whichone) {
if (!this.isInitialized) {
this.setupAccesstoFancyObject();
}
if (whichone < this.allOfThem.size()) {
return this.allOfThem.get(whichone);
}
// If we ask bejond...
this.isInitialized = false;
this.teardownAccessToFancyObjects();
return null;
}
private void setupAccesstoFancyObject() {
System.out.println("Initializing fancy objects");
for (int i = 0; i < 20; i++) {
this.allOfThem.addElement(new FancyObject());
}
this.isInitialized = true;
}
private void teardownAccessToFancyObjects() {
System.out.println("I'm doing proper cleanup here");
}
}
But the real question (thx #Andreas) seem to be:
What construct can I use to create an Observable when the underlying code need setup/teardown, especially when one expects that not all elements are pulled. The Iterable just was my first idea
Update 2: Based on Dave's answer I created a gist with my working solution. The iterator isn't perfect, but it's a start.
Observable.using is used for tearing down on termination (completion or error) or unsubscription. To use it you need to make the tear-down code accessible so that your source observable can look like this:
source = Observable.using(
resourceFactory,
observableFactory,
resourceDisposer);
With your code it might look like this:
source = Observable.using(
() -> new ComplicatedObject(),
co -> Observable.from(co),
co -> co.tearDown());
If you want that kind of control you need to separate the implementation of Iterable from Iterator. Iterable means the class can provide an Iterator that is meaningful in whatever fashion makes sense for the class.
However, if you implement Iterator in the same class, then you are stuck with only ever having one Iterator for each instance of ComplicatedObject. The correct approach is to implement
class FancyObjectIterator implements Iterator<FancyObject>
{
...
}
separately from ComplicatedObject so you can merely discard the partially-used iterators when you are done with them. ComplicatedObject should implement only Iterable<FancyObject>.
If you object to that approach because the iterator has more state that needs special cleanup, then something is wrong with your design. The only state an Iterator should be aware of is the current position in the base "collection", for a very loose definition of "collection" and "position" since the concept of an iterator can apply to much more than typical collections.
You cannot implement Iterator and Iterable at the same time, since Iterable.iterator() must return a new Iterator or every call.
Code is allowed to iterate the same Iterable multiple times in parallel.
Example: An over-simplified way to find duplicate elements in an Iterable:
Iterable<MyObject> myIterable = ...;
for (MyObject myObj1 : myIterable) {
for (MyObject myObj2 : myIterable) {
if (myObj1 != myObj2 && myObj1.equals(myObj2)) {
// found duplicate
}
}
}
The enhanced for loops used here will each use an Iterator.
As you can see, each Iterator must maintain it's own independent position. Therefore, the iterator() method needs to return a new object, with it's own state.
And for your question on cleanup code, an Iterator does not have a close() method. Iterator state should not require cleanup. If they absolutely must, a finalizer can do it, but finalizers may take a very long time to be invoked. The general recommendation for finalizers is: DON'T.
I'm learning how to use Eclipse Scout and started with the tutorials found at
Eclipse Scout Tutorials
I've proudly completed the first hello world tutorial and got stuck while trying to complete the Minicrm Tutorial
Everything went well until this step, when I needed to restart the server and any of the GUI clients to see that the table in the outline I just created is not well formatted. The problem: None of the clients show me the created table, they are all empty.
I ticked the visible field in every newly added column (all but the primary key column) and I don't see why no table is shown. I even tried to go on with the tutorial and setting the column width to 200 as desired, but still no table. I pasted the code for the Class CompanyTablePage below. A screenshot of the Scout Explorer is also provided. I really just started with Eclipse Scout and would appreciate any help or hints!
Thanks,
Isa
/**
*
*/
package org.eclipsescout.demo.minicrm.client;
import org.eclipse.scout.commons.annotations.Order;
import org.eclipse.scout.commons.annotations.PageData;
import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractLongColumn;
import org.eclipse.scout.rt.client.ui.basic.table.columns.AbstractStringColumn;
import org.eclipse.scout.rt.client.ui.desktop.outline.pages.AbstractPageWithTable;
import org.eclipse.scout.rt.extension.client.ui.basic.table.AbstractExtensibleTable;
import org.eclipse.scout.rt.shared.TEXTS;
import org.eclipsescout.demo.minicrm.client.CompanyTablePage.Table;
import org.eclipsescout.demo.minicrm.shared.CompanyTablePageData;
import org.eclipsescout.demo.minicrm.client.CompanyTablePage.Table.NameColumn;
/**
* #author Isa
*/
#PageData(CompanyTablePageData.class)
public class CompanyTablePage extends AbstractPageWithTable<Table> {
#Override
protected String getConfiguredTitle() {
return TEXTS.get("Company");
}
#Order(10.0)
public class Table extends AbstractExtensibleTable {
/**
* #return the ShortNameColumn
*/
public ShortNameColumn getShortNameColumn() {
return getColumnSet().getColumnByClass(ShortNameColumn.class);
}
/**
* #return the NameColumn
*/
public NameColumn getNameColumn() {
return getColumnSet().getColumnByClass(NameColumn.class);
}
/**
* #return the CompanyNrColumn
*/
public CompanyNrColumn getCompanyNrColumn() {
return getColumnSet().getColumnByClass(CompanyNrColumn.class);
}
#Order(10.0)
public class CompanyNrColumn extends AbstractLongColumn {
#Override
protected boolean getConfiguredDisplayable() {
return false;
}
#Override
protected boolean getConfiguredPrimaryKey() {
return true;
}
#Override
protected boolean getConfiguredVisible() {
return false;
}
}
#Order(20.0)
public class ShortNameColumn extends AbstractStringColumn {
#Override
protected String getConfiguredHeaderText() {
return TEXTS.get("ShortName");
}
#Override
protected int getConfiguredWidth() {
return 200;
}
}
#Order(30.0)
public class NameColumn extends AbstractStringColumn {
#Override
protected String getConfiguredHeaderText() {
return TEXTS.get("Name");
}
#Override
protected int getConfiguredWidth() {
return 200;
}
}
}
}
it seems to me that you selected the wrong template when you created your Scout project.
Are you sure you choose "Outline based application" ?
The different types are described here: type of application.
The main difference is located in Desktop#execOpened() of your scout application. You will need to change this implementation by hand.
Depending on the chosen template, the SDK add some default elements (a Form, an Outline...) during the project creation. You can add these elements after the project creation.
I just got simple problem, but it seems that I cant find a solution for it. Well the following code is part of open-source project, but this part is written by me from scratch.
Well, everything inside this "script" works well without problems except of one thing,
the int variable CB_State doesn't change after calling StartParticipation() method:
import java.util.Calendar;
import java.util.logging.Logger;
import com.l2jserver.gameserver.Announcements;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
public final class CastleBattle
{
private static Logger _log = Logger.getLogger("CastleBattle");
private static String htm_path = "data/scripts/l2dc/CastleBattle/";
public static int CB_State = 1; // 0 - Disabled, 1 - Not running, 2 - Participation start, 3 - Participation end, 4 - Running, 5 - Event ended
public CastleBattle()
{
CB_Init();
}
// Initialize Engine
private static void CB_Init()
{
if (CB_State == 1)
{
SetStartTime();
}
}
// Event Loop
public static void SetStartTime()
{
Calendar _nextTime = Calendar.getInstance();
int _m = _nextTime.get(Calendar.MINUTE);
int x = 1;
while (_m > 5)
{
_m -= 5;
x++;
}
_nextTime.set(Calendar.MINUTE, x * 5);
ThreadPoolManager.getInstance().scheduleGeneral(new CastleBattleLoop(), _nextTime.getTimeInMillis() - System.currentTimeMillis());
}
// Allow players to participate in the event
public static void StartParticipation()
{
CB_State = 2;
Announcements.getInstance().announceToAll("Castle Battle participation has started.");
_log.info("Castle Battle participation has started.");
}
// Player requests to join event via NPC
public static void CB_bypass(String _cmd, L2PcInstance _player)
{
if (_cmd.startsWith("InitHtmlRequest"))
{
if (CB_State == 0)
{
NpcHtmlMessage _html = new NpcHtmlMessage(0);
_html.setFile("", htm_path + "CB_Disabled.htm");
_player.sendPacket(_html);
}
if (CB_State == 1)
{
NpcHtmlMessage _html = new NpcHtmlMessage(0);
_html.setFile("", htm_path + "CB_NotRunning.htm");
_player.sendPacket(_html);
}
if (CB_State == 2)
{
NpcHtmlMessage _html = new NpcHtmlMessage(0);
_html.setFile("", htm_path + "CB_Participate.htm");
_player.sendPacket(_html);
}
}
}
public static void main(String[] args)
{
_log.info("# Castle Battle Engine #");
_log.info("Author : HyperByter");
_log.info("Version : Beta");
_log.info("Version : 3.7.2013");
new CastleBattle();
}
}
class CastleBattleLoop implements Runnable
{
#Override
public void run()
{
if (CastleBattle.CB_State == 1)
{
CastleBattle.StartParticipation();
}
}
}
So any suggestions how to fix this problem?
The method StartParticipation() is probably never called:
main() calls the constructor of CastleBattle
The CastleBattle constructor calls CB_Init()
CB_Init() calls SetStartTime()
SetStartTime() invokes this line:
ThreadPoolManager.getInstance().scheduleGeneral(new CastleBattleLoop(), _nextTime.getTimeInMillis() - System.currentTimeMillis());
after some whacky and indecipherable arithmetic on _nextTime, it's likely that the schedule interval is either very large, or perhaps negative, either of which may cause the Runnable CastleBattleLoop to never be started, in which case StartParticipation() would never be called.
I don't know what ThreadPoolManager does with strange input, but I would start by debugging what value is being passed into the scheduleGeneral() method and read the javadoc to see what effect such a value would have.
The class at the bottom is called CastleBattleLoop , but it does not contain anything loopy, so StartParticipation() gets called only once (if CB_State is 1 at that moment).
You should add something like
while(running){
if (CastleBattle.CB_State == 1)
{
CastleBattle.StartParticipation();
}
Thread.sleep(100);
}
StartParticipation() is being called inside a thread.
Check if you are trying to figure out its value even before actual change occurs.
[Not sure how are you figuring out the value of "CastleBattle.CB_State" in later part of the code]
I need to get a list of all caller methods for a method of interest for me in Java. Is there a tool that can help me with this?
Edit: I forgot to mention that I need to do this from a program. I'm usig Java Pathfinder and I want to run it an all the methods that call my method of interest.
For analyzing bytecode, I would recommend ASM. Given a list of Classes to analyze, a visitor can be made which finds the method calls you're interested in. One implementation which analyses classes in a jar file is below.
Note that ASM uses internalNames with '/' instead of '.' as a separator. Specify the target method as a standard declaration without modifiers.
For example, to list methods that could be calling System.out.println("foo") in the java runtime jar:
java -cp "classes;asm-3.1.jar;asm-commons-3.1.jar" App \
c:/java/jdk/jre/lib/rt.jar \
java/io/PrintStream "void println(String)"
Edit: source and line numbers added: Note that this only indicates the last target method invocation per calling method - the original q only wanted to know which methods. I leave it as an exercise for the reader to show line numbers of the calling method declaration, or the line numbers of every target invocation, depending on what you're actually after. :)
results in:
LogSupport.java:44 com/sun/activation/registries/LogSupport log (Ljava/lang/String;)V
LogSupport.java:50 com/sun/activation/registries/LogSupport log (Ljava/lang/String;Ljava/lang/Throwable;)V
...
Throwable.java:498 java/lang/Throwable printStackTraceAsCause (Ljava/io/PrintStream;[Ljava/lang/StackTraceElement;)V
--
885 methods invoke java/io/PrintStream println (Ljava/lang/String;)V
source:
public class App {
private String targetClass;
private Method targetMethod;
private AppClassVisitor cv;
private ArrayList<Callee> callees = new ArrayList<Callee>();
private static class Callee {
String className;
String methodName;
String methodDesc;
String source;
int line;
public Callee(String cName, String mName, String mDesc, String src, int ln) {
className = cName; methodName = mName; methodDesc = mDesc; source = src; line = ln;
}
}
private class AppMethodVisitor extends MethodAdapter {
boolean callsTarget;
int line;
public AppMethodVisitor() { super(new EmptyVisitor()); }
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(targetClass)
&& name.equals(targetMethod.getName())
&& desc.equals(targetMethod.getDescriptor())) {
callsTarget = true;
}
}
public void visitCode() {
callsTarget = false;
}
public void visitLineNumber(int line, Label start) {
this.line = line;
}
public void visitEnd() {
if (callsTarget)
callees.add(new Callee(cv.className, cv.methodName, cv.methodDesc,
cv.source, line));
}
}
private class AppClassVisitor extends ClassAdapter {
private AppMethodVisitor mv = new AppMethodVisitor();
public String source;
public String className;
public String methodName;
public String methodDesc;
public AppClassVisitor() { super(new EmptyVisitor()); }
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
}
public void visitSource(String source, String debug) {
this.source = source;
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
methodName = name;
methodDesc = desc;
return mv;
}
}
public void findCallingMethodsInJar(String jarPath, String targetClass,
String targetMethodDeclaration) throws Exception {
this.targetClass = targetClass;
this.targetMethod = Method.getMethod(targetMethodDeclaration);
this.cv = new AppClassVisitor();
JarFile jarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
InputStream stream = new BufferedInputStream(jarFile.getInputStream(entry), 1024);
ClassReader reader = new ClassReader(stream);
reader.accept(cv, 0);
stream.close();
}
}
}
public static void main( String[] args ) {
try {
App app = new App();
app.findCallingMethodsInJar(args[0], args[1], args[2]);
for (Callee c : app.callees) {
System.out.println(c.source+":"+c.line+" "+c.className+" "+c.methodName+" "+c.methodDesc);
}
System.out.println("--\n"+app.callees.size()+" methods invoke "+
app.targetClass+" "+
app.targetMethod.getName()+" "+app.targetMethod.getDescriptor());
} catch(Exception x) {
x.printStackTrace();
}
}
}
Edit: the original question was edited to indicate a runtime solution was needed - this answer was given before that edit and only indicates how to do it during development.
If you are using Eclipse you can right click the method and choose "Open call hierarchy" to get this information.
Updated after reading comments: Other IDEs support this as well in a similar fashion (at least Netbeans and IntelliJ do)
Annotate the method with #Deprecated ( or tag it with #deprecated ), turn on deprecation warnings, run your compile and see which warnings get triggered.
The run your compile bit can be done either by invoking an external ant process or by using the Java 6 compiler API.
right click on method
Go to references and (depending on your requirement)
choose workspace/project/Hierarchy.
This pops up a panel that shows all references to this functions. Eclipse FTW !
In eclipse, highlight the method name and then Ctrl+Shift+G
There isn't a way to do this (programmatically) via the Java reflection libraries - you can't ask a java.lang.reflect.Method "which methods do you call?"
That leaves two other options I can think of:
Static analysis of the source code. I'm sure this is what the Eclipse Java toolset does - you could look at the Eclipse source behind the JDT, and find what it does when you ask Eclipse to "Find References" to a method.
Bytecode analysis. You could inspect the bytecode for calls to the method. I'm not sure what libraries or examples are out there to help with this - but I can't imagine that something doesn't exist.
Yes, most modern IDE:s will let you either search for usages of a method or variable. Alternatively, you could use a debugger and set a trace point on the method entry, printing a stack trace or whatever every time the method is invoked.
Finally, you could use some simple shell util to just grep for the method, such as
find . -name '*.java' -exec grep -H methodName {} ;
The only method that will let you find invokations made through some reflection method, though, would be using the debugger.
I made a small example using #Chadwick's one. It's a test that assesses if calls to getDatabaseEngine() are made by methods that implement #Transaction.
/**
* Ensures that methods that call {#link DatabaseProvider#getDatabaseEngine()}
* implement the {#link #Transaction} annotation.
*
* #throws Exception If something occurs while testing.
*/
#Test
public void ensure() throws Exception {
final Method method = Method.getMethod(
DatabaseEngine.class.getCanonicalName() + " getDatabaseEngine()");
final ArrayList<java.lang.reflect.Method> faultyMethods = Lists.newArrayList();
for (Path p : getAllClasses()) {
try (InputStream stream = new BufferedInputStream(Files.newInputStream(p))) {
ClassReader reader = new ClassReader(stream);
reader.accept(new ClassAdapter(new EmptyVisitor()) {
#Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
return new MethodAdapter(new EmptyVisitor()) {
#Override
public void visitMethodInsn(int opcode, String owner, String nameCode, String descCode) {
try {
final Class<?> klass = Class.forName(Type.getObjectType(owner).getClassName());
if (DatabaseProvider.class.isAssignableFrom(klass) &&
nameCode.equals(method.getName()) &&
descCode.equals(method.getDescriptor())) {
final java.lang.reflect.Method method = klass.getDeclaredMethod(name,
getParameters(desc).toArray(new Class[]{}));
for (Annotation annotation : method.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(Transaction.class)) {
return;
}
}
faultyMethods.add(method);
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
};
}
}, 0);
}
}
if (!faultyMethods.isEmpty()) {
fail("\n\nThe following methods must implement #Transaction because they're calling getDatabaseEngine().\n\n" + Joiner.on("\n").join
(faultyMethods) + "\n\n");
}
}
/**
* Gets all the classes from target.
*
* #return The list of classes.
* #throws IOException If something occurs while collecting those classes.
*/
private List<Path> getAllClasses() throws IOException {
final ImmutableList.Builder<Path> builder = new ImmutableList.Builder<>();
Files.walkFileTree(Paths.get("target", "classes"), new SimpleFileVisitor<Path>() {
#Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(".class")) {
builder.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return builder.build();
}
/**
* Gets the list of parameters given the description.
*
* #param desc The method description.
* #return The list of parameters.
* #throws Exception If something occurs getting the parameters.
*/
private List<Class<?>> getParameters(String desc) throws Exception {
ImmutableList.Builder<Class<?>> obj = new ImmutableList.Builder<>();
for (Type type : Type.getArgumentTypes(desc)) {
obj.add(ClassUtils.getClass(type.getClassName()));
}
return obj.build();
}
1)In eclipse it is ->right click on the method and select open call hierarchy or CLT+ALT+H
2)In jdeveloper it is -> right click on the method and select calls or ALT+SHIFT+H
The closest that I could find was the method described in this StackOverflow questions selected answer.check this out
You can do this with something in your IDE such as "Find Usages" (which is what it is called in Netbeans and JDeveloper). A couple of things to note:
If your method implements a method from an interface or base class, you can only know that your method is POSSIBLY called.
A lot of Java frameworks use Reflection to call your method (IE Spring, Hibernate, JSF, etc), so be careful of that.
On the same note, your method could be called by some framework, reflectively or not, so again be careful.