0

I am trying to load methods Customer.cypher and Customer.cypherCBC method from my class Configuration. Customer class is rendering from different environments so few environmets are having cypherCBC() and cypher() method and few are having only cypher() method.

Now i want to check if cypherCBC if not there in Customer class then load cypher() method. My function is so far;

   try {
        Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");

        Object  obj = customerClass.newInstance();

        //here getting "NoSuchMethodException" exception
        Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7


         if(methodCBC.getName().equals("cypherCBC")){
            methodCBC.invoke(obj, new String(dbshPass));
            System.out.println("CYPHER_CBC: "
               + methodCBC.invoke(obj, new String(dbshPass)));
        }else{

            Method method = customerClass.getDeclaredMethod("cypher", String.class);
            method.invoke(obj, new String(dbshPass));
            System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
        }

    }catch (Exception e){
        e.printStackTrace();
    }

Getting an error at line 7.

NoSuchMethodException: com.myapp.impl.service.Customer.cypherCBC(java.lang.String)

that means for particular environment class Customer doesn't having cypherCBC() method, but ideally it should come in else part and execute cypher() method.

Class<?> client = null;
    Object  obj = null;
        try{
            client = Class.forName("com.myapp.impl.service.Client");
            obj = client.newInstance();

        }catch (InstantiationException ex) {
            System.err.println("Not able to create Instance of Class");
        } catch (IllegalAccessException ex) {
            System.err.println("Not able to access Class");
        } catch (ClassNotFoundException ex) {
            System.err.println("Not able to find Class");
        }

        try {

            Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
 System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));   
        }catch (NoSuchMethodException ex) {
            System.err.println("Not able to find Method on class");
            ex.printStackTrace();
        }  catch (Exception e){
            e.printStackTrace();
        }
0

3 Answers 3

2

That is exactly what is to be expected: getDeclaredMethod() throws that exception when no method exists that meets your specification. And you are wondering that it throws an exception if the required method is missing? Hint: better read the javadoc next time. Don't assume that something does something, but verify your assumptions!

Besides: read your code again. What is it doing? You are asking "give me the method named 'foo'". And then, your next step is to ask that method "is your name 'foo'". So even without reading javadoc, it should become clear that your logic is flawed.

As solution, you can implement a non-throwing lookup yourself, like

private Method lookupCypher(Class<?> client, String methodName) {
  for (Method declaredMethod : client.getDeclardMethods()) {
   if (declaredMethod.getName().equals(methodName))  {
     Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
     if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
        // so declaredMethod has the given name, and takes one string as argument!
        return declaredMethod;
     } 
   }
 // our search didn't reveal any matching method!
 return null;
 }

Using that helper method, you can rewrite your code to:

Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
  toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...

Or, with the idea from hunter in mind; a much more "OO like" version:

interface CustomerCypherWrapper {
   void cypher(String phrase);
}

class NewCustomerWrapper() implements CustomerCypherWrapper {
   @Override
   void cypher(String phrase) {
     new Customer.cypherCBC(phrase);
  }
}

class oldCustomerWrapper() implements CustomerCypherWrapper {
   @Override
   void cypher(String phrase) {
     new Customer.cypher(phrase);
  }
}

And your client code boils down to:

CustomerCypherWrapper wrapper = 
 (lookupCypher(..., "cypherCBC") == null) 
 ? new NewCustomerWrapper() 
 : new OldCustomerWrapper();

wrapper.cypher();

[ I hope you notice that my version A) is easier to read and B) doesn't contain any duplicated code any more. ]

And yes, an alternative implementation of the lookup method could just go like

private Method lookupCyper(Client<?>, String methodName) {
   try {
     return client.getDeclaredMethod(methodName, String.class);
   } catch ....
     and return null;
}
     ... return your public cypherCBC method

But that is an "uncommon practice" in Java. In Java, we ask for permission; instead of forgiveness. Unlike other languages

Sign up to request clarification or add additional context in comments.

3 Comments

Would you please edit my latest code correctly. I still getting exception. I didn't understand lookup method.
I spent 30 minutes explaining to you that this exception is to expected and giving you code that does the lookup without throwing an exception. The only thing you would need to do is to implement a comparison that calls "declaredMethod.getParameterTypes()" (which returns an array) and this check needs to make sure that the returned array contains one element String.class. Seriously: if this is "beyond" the things that you understand; I can't help you.
You see, this is really basic stuff: calling well documented methods and dealing with the results of those calls. If this is already overburdening you, maybe you step back and learn some more Java before you go for such "advanced" topics. But just for the sake of it, I will update my rework my lookup code so that it just works.
1

if you compile the application with a Customer class which has both method,you can use reflection once to check whether the cypherCBC method available or not at runtime, then you can keep that status, you can call the method without using reflection

if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}

But to write a better application,you should use two version baselines. even though this is a small code fragment you should setup a another module to hide this Customer class and its interactions,that module should have two versions. but your main module has only single version.Now when you you deliver the application , product should be packaged with right version baseline based on compatibility for the target environment.

4 Comments

@GhostCast No need a cast, it is the same Class, if i understood OP correctly, if we call cypherCBC method in some client environments, it will throw NosuchMethodError (note that this is Error not Exception), so my answer is a sort of extension to your answer, as u explained in your answer, once we found that cypherCBC is available we flag newVersion=true,after that no reflection call. i believe at compile time application has a dependency to Customer class and it is the new version (both method available)
No , if we can execute customerClass.newInstance(), that means it has a default cunstructor, if we can write a code like Customer customer, that means we have dependency at compile time, so Customer customer = new Customer() can be executed without any problem.
You can just say new Customer(). The only valid reason to use reflection in this case is if the class itself may not exist altogether.
As said; thanks for your comments; inspired me to further enhance my answer.
0

Although reflection works (as explained in the other answers). if you have control over the Customer class, you can try a non-reflection approach.

interface CBCCypherable {
    public String cypherCBC(String pass);
}

You can now have two versions of Customer class, one that implements CBCCypherable and one that doesn't. And when you call it, it looks like this:

Customer c = new Customer();
if (c instanceof CBCCypherable) {
   ((CBCCypherable)c).cypherCBC(pass);
} else {
   c.cypher(pass);
}

What you get with this solution is much simpler code, and as a bonus the compiler will check that you use the correct method name and parameter types. Unlike with reflection, where that's all your job, and you have to run the code to find out if something's wrong.

P.s.: I don't know if this is just sample code or you are really encrypting/hashing passwords here, but it's generally considered a bad idea to roll your own security code.

2 Comments

No. I do not have Customer class control, indeed i can only use reflection.
@DevSingh That's a shame.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.