Java reflection inconsistent with method declaration

Sorry for the length of this question. I am new to Java and I came across something that really pounded me. I am so new to Java that I don’t yet know the whole terminology, so please bear with me; I have about 3 years of experience working with PHP (mostly procedural, not OO), but very little Java. I also know that debugging with System.out.println is the wrong way to do this, but it works, and that's what I'm used to (insert jokes about PHP programmers here). I'm still trying to figure out how to use the NetBeans debugger.

I am working on adding a feature to a web application using Struts (1.x). The problem I am facing is that the method is declared when you want to pass a String, but the Reflection on this method says that it wants String [] (an array of strings). I am limited in that I cannot make significant structural changes to the application, and, of course, I have to make sure that I am not breaking anything in the application that is currently working, so I am trying to make my changes in the context of what already exists. So, to the problem ...

A method is declared here (many of them are cut from them to show only what I hope are the corresponding bits):

AEReportBean.java:

public class AEReportBean { private String selectedDownloadFields = null; public String getSelectedDownloadFields() { return selectedDownloadFields; } // Note that there is no overloading of this function anywhere, this is the only declaration. public void setSelectedDownloadFields(String selectedDownloadFields) { this.selectedDownloadFields = selectedDownloadFields; } } 

When the user clicks the submit button on the form, he is processed by AEReportSubmitAction.java:

 public class AEReportSubmitAction extends BaseAction { public ActionForward doExecute( ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response ) throws Exception { // This works fine, the paramater is getting passed in the request: System.out.println("URL parameter: " + request.getParameter("selectedDownloadFields"); AEReportBean bean = new AEReportBean(request.getLocale(), 0); PropertyUtil.setAllFromRequest(request, bean); // This prints "Null", meaning the setAllFromRequest line above is failing to set this property. System.out.println("AEReportSubmitAction.java - bean.getSelectedDownloadFields() after setAllFromRequest: " + bean.getSelectedDownloadFields()); } } 

PropertyUtil.setAllFromRequest () is where the magic and the real problem are:

 public class PropertyUtil { /** * Takes all the parameters from the request object and if there a matching * mutator method in the bean, sets it */ static public void setAllFromRequest(ServletRequest request, Object out) { // Iterate through all the request parameter names and try to set each one. for (Enumeration parameterNames = request.getParameterNames(); parameterNames.hasMoreElements();) { String name = (String) parameterNames.nextElement(); try { PropertyUtil.setSimpleProperty(out, name, request.getParameter(name)); } catch (Exception e) { log.info("Exception while setting properties from the Request. parameterName=" + name, e); } } } /** * Sets the property from an object using the object mutator method. * Assumes naming conventions for accessor methods * @param bean the object to get the property from * @param property the name of the property to obtain * @param newProperty the object to set */ // NOTE: This just seems to be a wrapper for the method below it... static public void setSimpleProperty(Object bean, String property, Object newProperty) throws Exception { PropertyUtil.setSimpleProperty(bean, property, newProperty, null); } /** * Sets the property from an object using the object mutator method. * Assumes naming conventions for accessor methods * @param bean the object to get the property from * @param property the name of the property to obtain * @param newProperty the object to set */ static public void setSimpleProperty(Object bean, String property, Object newProperty, Class type) throws Exception { // Capitalize the first letter in the property and append "set" to the front String methodName = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); Method method; Class[] parameters; // If the Type was passed in when this method was called, simply add it to the Class array. if (type != null) { parameters = new Class[]{type}; } // If the Type was not specified, determine the Type class by calling getClass() on it; that class will be used below to call the appropriate setter method. else { parameters = new Class[]{newProperty.getClass()}; } // Here the reflection problem... // Iterate through all the methods in the bean. If the method is named "setSelectedDownloadFields", print out some info about it. for (Method m : bean.getClass().getMethods()) { if (m.getName().equals("setSelectedDownloadFields")) { // newProperty is the incoming data that ultimately comes from the HTML form field. System.out.println("newProperty.getClass(): " + newProperty.getClass()); // Prints "class java.lang.String" // Added for Cameron Skinner in comments. System.out.println("m.toGenericString: " + m.toGenericString()); // Prints "public void com.[company deleted].bean.AEReportBean.setSelectedDownloadFields(java.lang.String[])" System.out.println("m.getName(): " + m.getName()); // Prints "setSelectedDownloadFields" System.out.println("parameters:"); for (Class c : m.getParameterTypes()) { System.out.println("--c.getCanonicalName(): " + c.getCanonicalName()); // Prints "java.lang.String[]" System.out.println("--c.getName(): " + c.getName()); // Prints "[Ljava.lang.String;" } } } // And here where it fails... try { System.out.println("bean.getClass(): " + bean.getClass()); // Prints "class com.[company deleted].bean.AEReportBean" System.out.println("methodName: " + methodName); // Prints "setSelectedDownloadFields" System.out.println("for (Class p : parameters):"); for (Class p : parameters) { System.out.println("--p.getCanonicalName(): " + p.getCanonicalName()); // Prints "java.lang.String" } // Here it looks for a method called, effectively, AEReportBean.setSelectedDownloadFields(String s), but above we see that reflection is showing it as AEReportBean.setSelectedDownloadFields(String[] s), so the try block fails. method = bean.getClass().getMethod(methodName, parameters); } catch (NoSuchMethodException e) { // All lines below here also fail until it bombs out with the exception at the bottom... // If no method can be found, then see if it a primitive type that // has been wrapped Class valueClass = newProperty.getClass(); //System.out.println("valueClass.toString() = " + valueClass.toString()); try { if (valueClass.equals(Integer.class)) { method = bean.getClass().getMethod(methodName, new Class[]{int.class}); } else if (valueClass.equals(Double.class)) { method = bean.getClass().getMethod(methodName, new Class[]{double.class}); } else if (valueClass.equals(Long.class)) { method = bean.getClass().getMethod(methodName, new Class[]{long.class}); } else if (valueClass.equals(Float.class)) { method = bean.getClass().getMethod(methodName, new Class[]{float.class}); } else { throw new Exception(e.getMessage()); } } catch (NoSuchMethodException ex) { throw new Exception(ex.getMessage()); } } // If it had gotten to this point, it would call the method with the appropriate parameters, and the property would be set. try { // Now execute the method method.invoke(bean, new Object[]{newProperty}); } catch (Exception ex) { throw new Exception(ex.getMessage()); } } } 

I really don't know what I'm missing here, but there must be something. Other HTML form elements on the same page work fine. Please let me know if you need more information. Thanks!

+4
source share
1 answer

Code results do not lie. Basically, this suggests that the class is not the one you expect from it. You have several AEReportBean classes of different versions in the classpath of your project, possibly in different packages, and the wrong one was imported or got priority when loading classes. Do a type / class search in Netbeans to find all classes by the specified name in the class path (I do not do Netbeans, but in Eclispe it is Ctrl + Shift + T, the equivalent of Netbeans is probably Alt + Shift + O).

Refresh : Another possible reason is that Netbeans did not automatically create the project when the source file was saved (the IDE must create / update .class files at build time). Look somewhere in the settings.

+4
source

Source: https://habr.com/ru/post/1332840/


All Articles