I am trying to write base code for my program, where I want to make some authentication check before returning some information.
Here is a sample code of it,
//Man.java
package com.test;
import java.lang.reflect.Method;
public abstract class Man {
protected abstract String getPassword();
public final String getMailPassword(){
try{
Method method = getClass().getDeclaredMethod("getPassword");
System.out.println("this: " + this);
System.out.println("method: "+ method.toString());
//check something if all OK
return (String)method.invoke(this);
//else return null
}catch(Exception ex){
ex.printStackTrace();
}
return null;
}
}
//Boss.java
package com.test;
import com.test.Man;
public class Boss extends Man {
#Override
protected String getPassword() {
return "Boss'_password";
}
}
//Tester.java
package com.test;
import com.test.test2.Boss;
public class Tester {
public static void main(String arg[]){
//System.out.println(new Servent().getMailPassword());
System.out.println(new Boss().getMailPassword());
}
}
When I execute above code (Tester.java) It execute correctly (all the files in same package), and I get following output.
this: com.test.Boss#22509bfc
method: protected java.lang.String
com.test.Boss.getPassword() Boss'_password
But If I move then Boss.java to different package "test2", I get exception.
java.lang.IllegalAccessException: Class com.test.Man can not access a member of class com.test.test2.Boss with modifiers "protected"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:109)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:261)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:253)
at java.lang.reflect.Method.invoke(Method.java:599)
at com.test.Man.getMailPassword(Man.java:14)
at com.test.Tester.main(Tester.java:8)
this: com.test.test2.Boss#5a30cefd
method: protected java.lang.String com.test.test2.Boss.getPassword()
null
I have printed 'this' which gives this: com.test.test2.Boss#5a30cefd, but the exception detects the super class in this case Class com.test.Man can not access a member of class com.test.test2.Boss with modifiers "protected". I understood the exception, but I didn't understood why in second case it detected super class. Please if some one can help me to fix this out (I have need of having sub classes in different packages, I can't put them in same package)
Call method.setAccessible(true) before calling invoke()
Related
public class test extends AbstractTableModel {
public static void main(String[] args) {
}
public String valuePass(int rowIn)
{
String value = "open";
return value;
}
test(mdpTEST parentPanel) {
m_parentPanel = parentPanel;
}
...
}
import demo.test;
public class order{
public void new()
{
test blah = new test(null);
String text = blah.valuePass(0);
}
}
In the code above, "blah" should be referencing the class "test" which is public, however I'm told to change the visibility of "test()" to public as I get an error in the line: "test blah = new test(null);". I'm confused at why "public class test" is not being referenced by "blah" and how the second instance of "test()" is being utilized here. I appreciate any help in understanding this problem!
Two issues with the code that you have shown
1) You cannot have new() as method name as new it is a keyword
2) Line test blah = new test(null); is calling a constructor of a test class which is in a different package. So default visibility is applied to test(...) constructor in test class. And as per java visibility rule, you have to make it public to access it in a different package
Do these changes and your code should work fine
This method is called regularly
public static void stynax(String N[]) {
if (N[1].equals("echo")) { echo.s(); main(); }
if (N[1].equals("detectos")) { detectos.s(); main(); }
if (N[1].equals("getuser")) { getuser.s(); main(); }
if (N[1].equals("exit")) { exit.s(); main(); }
if (N[1].equals("makefile")) { makefile.s(); main(); }
if (N[1].equals("cd")) { cd.s(); main(); }
if (N[1].equals("system")) { system.s(); main(); }
main();
}
How can I invoke all these methods
system.s();
echo.s();
Ect, by seeing if the class exists, then calling the corresponding method.
N[1] is always the class name. The class where this method is stored is in a class called main, and the classes that are called are in a different package called Commands.
I always seem to get this error, when trying to make a Class variable, i think this is the main issue.
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
So it never gets to invoke the method.
To simplify.
1) The program gets the class name as a String as N[1]
2) It sees if the class exists
3) If the class exists it calls it by the name of the class N[1].s();
Edit: Imports used
import java.io.ByteArrayOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import cgameing.Commands.FileBrowser;
import cgameing.Commands.banner;
import cgameing.Commands.cd;
import cgameing.Commands.detectos;
import cgameing.Commands.echo;
import cgameing.Commands.exit;
import cgameing.Commands.getuser;
import cgameing.Commands.makefile;
import cgameing.Commands.system;
end of edit:
This one works, for anyone wanting to do the same thing
(Class.forName("commands."+N[1])).getDeclaredMethod("s", null).invoke(null,null);
Thanks everyone
You'll need to use reflection. Try something as follows. Use fully qualified class name instead of "XYZ" if your class is in a different package.
import java.lang.reflect.*;
import java.lang.*;
public class ReflectionTest {
public static void main(String[] args)throws NoSuchMethodException,
ClassNotFoundException,
IllegalAccessException,
InvocationTargetException {
(Class.forName("XYZ")).getDeclaredMethod("ABC", null).invoke(null,null);
}
}
class XYZ
{
public static void ABC()
{
System.out.println("Lulz");
}
}
For your use case given your classes are in commands package (as you stated in a comment). The fully qualified name will then be commands.classname
(Class.forName("commands."+N[1])).getDeclaredMethod("s", null).invoke(null,null);
You can use Reflection.
You have Class name coming in Array.
You can use "Class" and "Method" class. Class can determine if the class exists or not, and method can be used to call method you need to call.
try {
Class<?> c = Class.forName(N[1]);
Object t = c.newInstance();
Method[] allMethods = c.getDeclaredMethods();
for (Method m : allMethods) {
String mname = m.getName();
// if name matches use invoke method.
}
} catch (ClassNotFoundException x) {
//handle exception
}
Please consult API if you need to see more details.
I recommend you avoid using reflection if you possibly can. Better is to define the commands you expect to see - ideally in an enum.
For example:
enum Command {
CD(FileSystem::cd),
EXIT(Application::exit),
MAKEFILE(FileSystem::createFile),
...
private final Runnable runnable;
Command(Runnable runnable) {
this.runnable = runnable;
}
public void run() {
runnable.run();
}
}
You can still use the name to get the command if you wish (automatically throwing an exception if the value isn't found in the enum - which is presumably what you would want):
Command.valueOf(commandString.toUpperCase()).run();
Or call the commands directly without having to know which method they delegate to:
Command.MAKEFILE.run();
Given you are going to have a list of if statements somewhere, you might as well encapsulate that in an enum which is much more explicit than embedding the method names.
Okay, everyone seems to suggest reflection, there is at least one alternative way to do it, depending on whether you know your class and method names at compile time or not.
So, lets say we have this method in someclass:
public void changeDirectory(String args) {
//do something
}
If they are known, you could easily use method references:
HashMap<String, Consumer<String[]>> commands = new HashMap<>();
commands.put("cd", SomeClass::changeDirectory);
commands.get("cd").accept(args);
The drawback would be, the method signature would have to be the same... on the other hand, if you know the exact method anyway, you could just use a switch statement and call them directly...
If you want to do it dynamically, the alternative to reflection would be MethodHandles. The advantage is that they would only check access once on creation and have some other optimizations that should make them faster than reflection in most cases.
Class<?> dynamicallyFoundClass = Class.forName("some.package.SomeClass");
Class<?> fixedClass = SomeClass.class;
String methodName = "changeDirectory";
MethodType type = MethodType.methodType(void.class, String.class);
handles.put("cd", lookUp.findStatic(dynamicallyFoundClass, methodName, type));
handles.get("cd").invoke(args);
But how complicated an approach you have to use would depend on what you know at compile time and what has to be found at runtime.
# EvenDriver.java
package com.EventDrivenScenario.SystemElements;
import com.EventDrivenScenario.Exceptions.TableFullException;
import java.util.Random;
public class Table {
static final int TABLE_SIZE = 6;
static int tableCurrentSize;
Table(){
}
public static void main(String args[]){
Random eventTrigger = new Random();
while(true){
try {
if(eventTrigger.nextLong()%2 == 0){
new HumanBeing();
}
if (tableCurrentSize == TABLE_SIZE) {
throw new TableFullException();
}
} catch(TableFullException e) {
System.out.println(e.getMessage());
break;
}
}
}
}
class HumanBeing{
HumanBeing(){
new Chairs();
}
}
class Chairs{
Chairs(){
Table.tableCurrentSize++;
}
}
# TableFullException
package com.EventDrivenScenario.Exceptions;
public class TableFullException extends Exception{
TableFullException(){
}
public String getMessage() {
return ("Table Full - No More Visitors");
}
}
In the above code when i try to compile #EventDriver.java, I am getting compile time error indicating that TableFullException is not public and cannot be accessed outside package in spite of declaring it as public.
but if I change the package statement in both files to ##package com.EventDriver;## It works fine. I just want to understand why the above code throwing compile time error in spite of provide public access specifier for TableFullException.
Your TableFullException constructor is not public, so you can't create an instance of that exception from a class that doesn't belong to the same package. Make the constructor public, and your problem will be solved.
This is because you have a package private constructor for the class.
You have defined a constructor as package private by not giving any access modifier to the constructor Example :
public class PackagePrivateClassConstructor{
PackagePrivateClassConstructor(){}
}
You can use this constructor in the same package but outside the package it won't allow you to use it.
This is true for all you class Table,Chairs & HumanBeing.
You need to change it to
public class MyClass{
public MyCLass(){}
}
I have already written AspectJ aspects that perform #Around advice triggered by method annotations. Now I want to do the same, but where fields are annotated instead of methods. So with each method invocation of the class below, it must set the accountSummary field to the correct implementation. Is there a way to accomplish this? I presume using #Before advice would be the best way of going about it. Using CDI is not an option - the solution must use AspectJ.
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacy=LegacyAccountSummary.class,new=NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
// use correct accountSummary impl, decided in #Before code
}
public void bar() {
// use correct accountSummary impl, decided in #Before code
}
}
I am not sure what exactly you want to achieve, so I am presenting two alternative solutions.
First, let us create some application classes in order to have a fully compileable sample:
package de.scrum_master.app;
public interface AccountSummary {
void doSomething();
}
package de.scrum_master.app;
public class LegacyAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
public class NewAccountSummary implements AccountSummary {
#Override
public void doSomething() {
System.out.println("I am " + this);
}
}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectServiceClientAdapter {
Class<?> legacyImpl();
Class<?> newImpl();
}
package de.scrum_master.app;
public class PoolableBusinessLogic {
#InjectServiceClientAdapter(legacyImpl = LegacyAccountSummary.class, newImpl = NewAccountSummary.class)
private AccountSummary accountSummary;
public void foo() {
accountSummary.doSomething();
}
public void bar() {
System.out.println("Account summary is " + accountSummary);
}
}
Now we need an entry point:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
PoolableBusinessLogic businessLogic = new PoolableBusinessLogic();
businessLogic.foo();
businessLogic.bar();
System.out.println();
}
}
}
Obviously this yields an error because the member accountSummary has not been initialised:
Exception in thread "main" java.lang.NullPointerException
at de.scrum_master.app.PoolableBusinessLogic.foo(PoolableBusinessLogic.java:8)
at de.scrum_master.app.Application.main(Application.java:7)
Now we have two options, depending on what you want to achieve:
Option A: dynamic injection
Scenario: For each field access (even in the same PoolableBusinessLogic instance) decide dynamically what type of object instance to return. Here in this example I will just be randomising in order to simulate another if-else criterion.
BTW, I hope it is okay that I use the more expressive native AspectJ syntax. You can easily convert the aspect to annotation style.
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect DynamicInjectionAspect {
private static final Random RANDOM = new Random();
Object around(InjectServiceClientAdapter adapterAnn) :
get(* *) && #annotation(adapterAnn)
{
try {
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
return implClass.newInstance();
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This yields the following output:
I am de.scrum_master.app.LegacyAccountSummary#4d9cfefb
Account summary is de.scrum_master.app.NewAccountSummary#7e28388b
I am de.scrum_master.app.NewAccountSummary#2986e62
Account summary is de.scrum_master.app.LegacyAccountSummary#6576e542
I am de.scrum_master.app.NewAccountSummary#60c58418
Account summary is de.scrum_master.app.LegacyAccountSummary#4763754a
I am de.scrum_master.app.NewAccountSummary#52a971e3
Account summary is de.scrum_master.app.NewAccountSummary#7274187a
I am de.scrum_master.app.LegacyAccountSummary#23f32c4a
Account summary is de.scrum_master.app.LegacyAccountSummary#31e0c0b6
As you can see, within each of the five output groups (i.e. for each PoolableBusinessLogic instance) there are different account summary object IDs and sometimes (not always) even different class names.
Option B: static injection
Scenario: Per PoolableBusinessLogic instance decide dynamically what type of object instance to statically assign to the annotated member if its value is null. After that, do not overwrite the member anymore but return the previously initialised value. Again I will just be randomising in order to simulate another if-else criterion.
Attention: Do not forget to deactivate the first aspect, e.g. by prepending if(false) && to its pointcut. Otherwise the two aspects will be conflicting with each other.
package de.scrum_master.aspect;
import java.lang.reflect.Field;
import java.util.Random;
import org.aspectj.lang.SoftException;
import de.scrum_master.app.InjectServiceClientAdapter;
public aspect StaticInjectionAspect {
private static final Random RANDOM = new Random();
before(InjectServiceClientAdapter adapterAnn, Object targetObj) :
get(* *) && #annotation(adapterAnn) && target(targetObj)
{
try {
Field field = targetObj.getClass().getDeclaredField(thisJoinPoint.getSignature().getName());
field.setAccessible(true);
if (field.get(targetObj) != null)
return;
Class<?> implClass = RANDOM.nextBoolean() ? adapterAnn.legacyImpl() : adapterAnn.newImpl();
field.set(targetObj,implClass.newInstance());
} catch (Exception e) {
throw new SoftException(e);
}
}
}
This is a bit uglier because it involves using reflection for finding the member field. Because it might be (and in our example really is) private we need to make it accessible before doing anything with it.
This yields the following output:
I am de.scrum_master.app.NewAccountSummary#20d1fa4
Account summary is de.scrum_master.app.NewAccountSummary#20d1fa4
I am de.scrum_master.app.NewAccountSummary#2b984909
Account summary is de.scrum_master.app.NewAccountSummary#2b984909
I am de.scrum_master.app.LegacyAccountSummary#1ae3043b
Account summary is de.scrum_master.app.LegacyAccountSummary#1ae3043b
I am de.scrum_master.app.LegacyAccountSummary#2e2acb47
Account summary is de.scrum_master.app.LegacyAccountSummary#2e2acb47
I am de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Account summary is de.scrum_master.app.LegacyAccountSummary#7b87b9fe
Now the output looks different: Within each of the five output groups (i.e. for each PoolableBusinessLogic instance) both output lines show exactly the same object ID.
For Option A: dynamic injection in kriegaex's answer, the annotation-style aspect will look like this:
#Aspect
public class InjectServiceClientAdapterAspect {
#Pointcut("get(* *) && #annotation(injectAnnotation)")
public void getServiceClientAdapter(InjectServiceClientAdapter injectAnnotation) {
}
#Around("getServiceClientAdapter(injectAnnotation)")
public Object injectServiceClientAdapter(final ProceedingJoinPoint joinPoint, final InjectServiceClientAdapter injectAnnotation) {
// injection code goes here
}
Here's an excerpt from my code
package dictionary;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.regex.*;
public class IntelliCwDB extends CwDB {
public IntelliCwDB(String filename) {
super(filename);
}
#Override
public void add(String word, String clue) {
System.out.println("inelli");
}
}
And CwDB...
package dictionary;
import java.util.LinkedList;
import java.io.*;
import java.util.Scanner;
public class CwDB {
protected LinkedList<Entry> dict;
public CwDB(String filename) {
dict = new LinkedList<Entry>();
createDB(filename);
}
public void add(String word, String clue) {
System.out.println("cwdb");
dict.add(new Entry(word, clue));
}
protected void createDB(String filename) {
try {
BufferedReader f = new BufferedReader(new FileReader(filename));
while (f.ready()) {
this.add(f.readLine(), f.readLine());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
In the main() part I create a new IntelliCwDB object, which fires the execution of createDB().
The problem is that I want CwDB.createDB() to use it's own CwDB.add() method, not the one from IntelliCwDB. Is there any other neat solution than creating CwDB separately, then passing it into the constructor of IntelliCwDB just to rewrite the LinkedList<Entry> dict database?
You experienced one of the reasons why one should not call virtual methods from a constructor. For more details on this, see Effective Java 2nd Edition, Item 17: Design and document for inheritance or else prohibit it.
The simplest solution to your problem would be to split the base class method into a nonvirtual (final and/or private) one, and another, virtual, method, which calls the former in the base class implementation.
#aioobe was faster to provide an example to this :-)
You could solve it like this:
Create a private (or final) version of the CwDB.add, lets call it privateAdd.
Let the old add method in CwDB call this method instead.
Whenever you want to be sure that the CwDB-version of add is used, you simply call privateAdd instead.
Sample code
public class CwDB {
// ...
public void add(String word, String clue) {
privateAdd(word, clue);
}
private void privateAdd(String word, String clue) {
System.out.println("cwdb");
dict.add(new Entry(word, clue));
}
protected void createDB(String filename) {
// ...
// "Calling parent method from within the parent class" :-)
this.privateAdd(f.readLine(), f.readLine());
// ...
}
// ...
}
As #Péter Török correctly points out: You should never call a virtual method (directly or indirectly) from within a constructor. The reason is simple: The sub-class will get to run code before its super class (and itself) is initialized properly. (Whether or not it applies in this particular example stands to reason though.)
I would move the add method to addInternal in CwDB, and make a new add which calls addInternal. Then in the createDB method, call addInternal to get the correct method.
Eg.
class CwDB {
..
private void addInternal(String word, String clue) {
..
}
public void add(String word, String clue) {
addInternal(word, clue);
}
public void createDB(String filename) {
..
addInternal(w, c);
..
}
}