Asked  7 Months ago    Answers:  5   Viewed   51 times

I have 2 different Java projects, one has 2 classes: dynamicbeans.DynamicBean2 and dynamic.Validator.

On the other project, I load both of these classes dynamically and store them on an Object

class Form {
    Class beanClass;
    Class validatorClass;
    Validator validator;
}

I then go ahead and create a Validator object using validatorClass.newInstance() and store it on validator then I create a bean object as well using beanClass.newInstance() and add it to the session.

portletRequest.setAttribute("DynamicBean2", bean);

During the lifecycle of the Form project, I call validator.validate() which loads the previously created bean object from the session (I'm running Websphere Portal Server). When I try to cast this object back into a DynamicBean2 it fails with a ClassCastException.

When I pull the object back out of the session using

faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces);

and check the class of it using .getClass() I get dynamicbeans.DynamicBean2. This is the class I want to cast it to however when I try I get the ClassCastException.

Any reason why I'm getting this?

 Answers

77

I am not quite following your description of the program flow, but usually when you get ClassCastExceptions you cannot explain you have loaded the class with one classloader then try to cast it to the same class loaded by another classloader. This will not work - they are represented by two different Class objects inside the JVM and the cast will fail.

There is an article about classloading in WebSphere. I cannot say how it applies to your application, but there are a number of possible solutions. I can think of at least:

  1. Change the context class loader manually. Requires that you can actually get a reference to an appropriate class loader, which may not be possible in your case.

    Thread.currentThread().setContextClassLoader(...);
    
  2. Make sure the class is loaded by a class loader higher in the hierarchy.

  3. Serialize and deserialize the object. (Yuck!)

There is probably a more appropriate way for your particular situation though.

Tuesday, June 1, 2021
 
Isky
answered 7 Months ago
20

It is instructive to consider what the class looks like after removing type parameters (type erasure):

public class Test {
    Map map = new HashMap();
    public static void main (String ... args) {
        Test test = new Test();
        test.put("test", "value");
        System.out.println("Test: " + test.get("test", Double.class));
    }

    public Object get(String key, Class clazz) {
        return map.get(key);
    }

    public void put(String key, Object value) {
        map.put(key, value);
    }
}

This compiles and produces the same result that you see.

The tricky part is this line:

System.out.println("Test: " + test.get("test", Double.class));

If you had done this:

Double foo = test.get("test", Double.class);

then after type erasure the compiler would have inserted a cast (because after type erasure test.get() returns Object):

Double foo = (Double)test.get("test", Double.class);

So analogously, the compiler could have inserted a cast in the above line too, like this:

System.out.println("Test: " + (Double)test.get("test", Double.class));

However, it doesn't insert a cast, because the cast is not necessary for it to compile and behave correctly, since string concatenation (+) works on all objects the same way; it only needs to know the type is Object, not a specific subclass. Therefore, the compiler can omit an unnecessary cast and it does in this case.

Wednesday, August 11, 2021
 
Zizoo
answered 4 Months ago
12

Because I can answer my own questions, here's what the problem was:

At server-side, portlets and JSPs in WebSphere Portal use request.getServerHost() and request.getServerPort(). To make these methods work behind reverse proxy (which sends Host: header). I had to add:

ProxyPreserveHost On

to apache configuration and:

com.ibm.ws.webcontainer.extractHostHeaderPort = true
trusthostheaderport = true

additional properties to Web Container in IBM Console: WebSphere application servers -> WebSphere_Portal -> Web Container Settings -> Web Container -> Custom properties.

These properties are described here.

Tuesday, October 5, 2021
 
KrisztiƔn Balla
answered 2 Months ago
62

I think the problem would become from the parent ClassLoader you are using. You are not overloading loadClass method, so you are delegating in parent class, concretely in ClassLoader.getSystemClassLoader().

As javadoc says at https://docs.oracle.com/javase/7/docs/api/java/lang/ClassLoader.html#getSystemClassLoader()

This method is first invoked early in the runtime's startup sequence, at which point it creates the system class loader and sets it as the context class loader of the invoking Thread.

You want to load the changed classes but you are delegating to the Thread ClassLoader the operation, is a bit confusing.

You can do something like this, using your own Class ClassLoader

package a;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

public class ReloadTest {

    public static void main(String[] args) throws ClassNotFoundException, IOException {

        final Class<?> clazz = ReloadTest.class;

        System.out.println("Class: " +  clazz.hashCode());

        final URL[] urls = new URL[1];

        urls[0] =  clazz.getProtectionDomain().getCodeSource().getLocation();
        final ClassLoader delegateParent = clazz.getClassLoader().getParent();

        try (final URLClassLoader cl = new URLClassLoader(urls, delegateParent)) {

            final Class<?> reloadedClazz = cl.loadClass(clazz.getName());
            System.out.println("Class reloaded: " + reloadedClazz.hashCode());
            System.out.println("Are the same: " + (clazz != reloadedClazz) );
        }
    }
}

Hope helps!

P.D: This link is related to the same problems, may it help too Reload used classes at runtime Java

Sunday, November 7, 2021
 
Michael
answered 3 Weeks ago
62

WebSphere Application Server returns principal and remote user only if you configure it to use the JavaEE security context for your web application. Edit your web.xml to contain something like

<security-constraint>
 <display-name>userConstraint</display-name>
 <web-resource-collection>
  <web-resource-name>secure</web-resource-name>
  <url-pattern>/*</url-pattern>
  <http-method>GET</http-method>
  <http-method>POST</http-method>
 </web-resource-collection>
 <auth-constraint>
  <description>user</description>
  <role-name>user</role-name>
 </auth-constraint>
</security-constraint>
<security-role>
 <description>secrole</description>
 <role-name>user</role-name>
</security-role>

and redeploy your application. After deploying your application take a look at the application's settings in the Administrative Console. You will notice "User/role mapping". Add "all authenticated users from trusted realms" to the newly added role. Restart the application.

After that anonymous users can not access your application anymore. Also, the getRemoteUser and other APIs will return the user properly.

Saturday, November 20, 2021
 
Charles
answered 2 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share