Classloader is not propagated to XmlConfigBuilder

298 views
Skip to first unread message

rensvan...@gmail.com

unread,
Mar 5, 2014, 5:07:17 PM3/5/14
to haze...@googlegroups.com
Hi,

First my disclaimer: I'm new to most of this and really am not familiar with the way in which Hazelcast should behave ..

I'm working on a project that is broken down into several parts that are eventually packaged into the following structure (oversimplified, but that doesn't really matter at this point):

application.ear/application.war/WEB-INF/lib/application.jar/com/company/foo/HazelcastInstanceFactory.class
application.ear/application.war/WEB-INF/lib/hazelcast-3.1.6.jar

Whenever the application is deployed to the application server, things tend to be OK-ish when the configuration of the Hazelcast instance is done by code (and so relying on a lot of defaults of Hazelcast itself).

However, since the network configuration is tighter on production environments our system administrators insist that we've got ways to configure explicit configuration instead of the config being hardcoded in the EAR they are deploying.

I tried to take the following approach:

1. Try to load a configuration file (application-hazelcast.xml) from the server classpath. If this leads to a valid configuration, work with that config, else proceed to 2.
2. Use the configuration that borrows most from Hazelcast defaults and makes a few adjustments that our application needs.

So, whenever the system administrator puts that configuration file on the classpath, it will override the defaults, but it doesn't burden developers with one more configuration file they have to think about.

Now, the following problem happens (sorry for my lengthy introduction). It seems that the classloader used by invoking the constructor ClasspathXmlConfig("application-hazelcast.xml") is NOT made available to the XmlConfigBuilder that will eventually construct the Config instance. By not doing so, I get all kinds of warnings that have to do with the fact that no resources from 

application.ear/application.war/WEB-INF/lib/hazelcast-3.1.6.jar/META-INF/services/

can be loaded. It looks like this:

Mar 05, 2014 10:57:01 PM com.hazelcast.config.ClasspathXmlConfig
INFO: Configuring Hazelcast from 'application-hazelcast.xml'.
Mar 05, 2014 10:57:01 PM com.hazelcast.util.ServiceLoader
WARNING: Service loader could not load 'META-INF/services/com.hazelcast.PortableHook' It may be empty or does not exist.
Mar 05, 2014 10:57:01 PM com.hazelcast.util.ServiceLoader
WARNING: Service loader could not load 'META-INF/services/com.hazelcast.DataSerializerHook' It may be empty or does not exist.
Mar 05, 2014 10:57:01 PM com.hazelcast.instance.DefaultAddressPicker
INFO: Resolving domain name 'machine-name' to address(es): [list here]
Mar 05, 2014 10:57:01 PM com.hazelcast.instance.DefaultAddressPicker
INFO: Resolving domain name 'machine-name-2' to address(es): [ip address]
Mar 05, 2014 10:57:01 PM com.hazelcast.instance.DefaultAddressPicker
INFO: Interfaces is disabled, trying to pick one address from TCP-IP config addresses: [list here]
Mar 05, 2014 10:57:01 PM com.hazelcast.instance.DefaultAddressPicker
INFO: Prefer IPv4 stack is true.
Mar 05, 2014 10:57:01 PM com.hazelcast.instance.DefaultAddressPicker
INFO: Picked Address[machine-name]:5678, using socket ServerSocket[addr=/0:0:0:0:0:0:0:0,localport=5678], bind any local is true
Mar 05, 2014 10:57:01 PM com.hazelcast.util.ServiceLoader
WARNING: Service loader could not load 'META-INF/services/com.hazelcast.NodeInitializer' It may be empty or does not exist.
2014.03.05 22:57:02 CET INFO  [com.hazelcast.system] - [machine-name]:5678 [Group Goes Here] Hazelcast Community Edition 3.1.6 (20140221) starting at Address[machine-name]:5678
2014.03.05 22:57:02 CET INFO  [com.hazelcast.system] - [machine-name]:5678 [Group Goes Here] Copyright (C) 2008-2013 Hazelcast.com
2014.03.05 22:57:02 CET INFO  [com.hazelcast.instance.Node] - [machine-name]:5678 [Group Goes Here] Creating TcpIpJoiner
2014.03.05 22:57:02 CET INFO  [com.hazelcast.core.LifecycleService] - [machine-name]:5678 [Group Goes Here] Address[machine-name]:5678 is STARTING
2014.03.05 22:57:02 CET FATAL [com.hazelcast.spi.impl.ServiceManager] - [machine-name]:5678 [Group Goes Here] Error while initializing service: Could not find PortableFactory for factoryId: -3
com.hazelcast.nio.serialization.HazelcastSerializationException: Could not find PortableFactory for factoryId: -3
        at com.hazelcast.nio.serialization.SerializationContextImpl.getPortableContext(SerializationContextImpl.java:90)
        at com.hazelcast.nio.serialization.SerializationContextImpl.registerClassDefinition(SerializationContextImpl.java:65)
        at com.hazelcast.client.ClientEngineImpl.init(ClientEngineImpl.java:453)
        at com.hazelcast.spi.impl.ServiceManager.start(ServiceManager.java:137)
        at com.hazelcast.spi.impl.NodeEngineImpl.start(NodeEngineImpl.java:78)
        at com.hazelcast.instance.Node.start(Node.java:321)
        at com.hazelcast.instance.HazelcastInstanceImpl.<init>(HazelcastInstanceImpl.java:95)
        at com.hazelcast.instance.HazelcastInstanceFactory.newHazelcastInstance(HazelcastInstanceFactory.java:92)
        at com.hazelcast.instance.HazelcastInstanceFactory.newHazelcastInstance(HazelcastInstanceFactory.java:65)
        at com.hazelcast.core.Hazelcast.newHazelcastInstance(Hazelcast.java:58)
        at com.application.foo.ApplicationCacheHazelcastInstanceFactory.create(ApplicationCacheHazelcastInstanceFactory.java:51)
        at com.application.foo.ApplicationCache.<clinit>(ApplicationCache.java:30)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:270)
        at com.oracle.injection.provider.weld.BasicResourceLoader.classForName(BasicResourceLoader.java:27)
        at org.jboss.weld.bootstrap.BeanDeployer.addClass(BeanDeployer.java:77)
        at org.jboss.weld.bootstrap.BeanDeployer.addClasses(BeanDeployer.java:115)
        at org.jboss.weld.bootstrap.BeanDeployment.createBeans(BeanDeployment.java:168)
        at org.jboss.weld.bootstrap.WeldBootstrap.deployBeans(WeldBootstrap.java:331)
        at com.oracle.injection.provider.weld.WeldInjectionContainer.deploy(WeldInjectionContainer.java:98)
        at com.oracle.injection.integration.CDIAppDeploymentExtension.prepare(CDIAppDeploymentExtension.java:56)
        at weblogic.application.internal.flow.AppDeploymentExtensionFlow.prepare(AppDeploymentExtensionFlow.java:23)
        at weblogic.application.internal.BaseDeployment$1.next(BaseDeployment.java:706)
        at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:35)
        at weblogic.application.internal.BaseDeployment.prepare(BaseDeployment.java:237)
        at weblogic.application.internal.EarDeployment.prepare(EarDeployment.java:61)
        at weblogic.application.internal.DeploymentStateChecker.prepare(DeploymentStateChecker.java:158)
        at weblogic.deploy.internal.targetserver.AppContainerInvoker.prepare(AppContainerInvoker.java:60)
        at weblogic.deploy.internal.targetserver.operations.ActivateOperation.createAndPrepareContainer(ActivateOperation.java:207)
        at weblogic.deploy.internal.targetserver.operations.ActivateOperation.doPrepare(ActivateOperation.java:96)
        at weblogic.deploy.internal.targetserver.operations.AbstractOperation.prepare(AbstractOperation.java:229)
        at weblogic.deploy.internal.targetserver.DeploymentManager.handleDeploymentPrepare(DeploymentManager.java:747)
        at weblogic.deploy.internal.targetserver.DeploymentManager.prepareDeploymentList(DeploymentManager.java:1216)
        at weblogic.deploy.internal.targetserver.DeploymentManager.handlePrepare(DeploymentManager.java:250)
        at weblogic.deploy.internal.targetserver.DeploymentServiceDispatcher.prepare(DeploymentServiceDispatcher.java:159)
        at weblogic.deploy.service.internal.targetserver.DeploymentReceiverCallbackDeliverer.doPrepareCallback(DeploymentReceiverCallbackDeliverer.java:171)
        at weblogic.deploy.service.internal.targetserver.DeploymentReceiverCallbackDeliverer.access$000(DeploymentReceiverCallbackDeliverer.java:13)
        at weblogic.deploy.service.internal.targetserver.DeploymentReceiverCallbackDeliverer$1.run(DeploymentReceiverCallbackDeliverer.java:46)
        at weblogic.work.SelfTuningWorkManagerImpl$WorkAdapterImpl.run(SelfTuningWorkManagerImpl.java:545)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:256)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:221)
2014.03.05 22:57:02 CET INFO  [com.hazelcast.cluster.TcpIpJoiner] - [machine-name]:5678 [Group Goes Here] Connecting to possible member: Address[machine-name-2]:5678
2014.03.05 22:57:02 CET INFO  [com.hazelcast.nio.SocketConnector] - [machine-name]:5678 [Group Goes Here] Connecting to machine-name-2/[ip address]:5678, timeout: 0, bind-any: true
2014.03.05 22:57:03 CET INFO  [com.hazelcast.nio.SocketConnector] - [machine-name]:5678 [Group Goes Here] Could not connect to: machine-name-2/[ip address]:5678. Reason: SocketException[Connection refused: connect to address machine-name-2/[ip address]:5678]
2014.03.05 22:57:04 CET INFO  [com.hazelcast.cluster.TcpIpJoiner] - [machine-name]:5678 [Group Goes Here]

Members [1] {
        Member [machine-name]:5678 this
}

2014.03.05 22:57:04 CET INFO  [com.hazelcast.core.LifecycleService] - [machine-name]:5678 [Group Goes Here] Address[machine-name]:5678 is STARTED
2014.03.05 22:57:33 CET INFO  [com.hazelcast.partition.PartitionService] - [machine-name]:5678 [Group Goes Here] Initializing cluster partition table first arrangement...

I was browsing through the codebase, and think that this could be solved by reusing the classloader that has been set (either explictly or implicitly) on the ClasspathXmlConfig instance?

I ended up making the following (extremely nasty!) hack, and that seems to work:

// Yes, I removed some exception handling ..
private static Config tryToLoadHazelcastConfigFromClassPath() {
    final ClassLoader classLoader = HazelcastInstanceFactory.class.getClassLoader();
    final InputStream in = classLoader.getResourceAsStream("application-hazelcast.xml");

    final Config config = new Config();
    config.setClassLoader(classLoader);

    final XmlConfigBuilder xmlConfigBuilder = new XmlConfigBuilder(in).setProperties(System.getProperties());

    final Method buildMethod = xmlConfigBuilder.getClass().getDeclaredMethod("build", Config.class);
    buildMethod.setAccessible(true);

    return (Config) buildMethod.invoke(xmlConfigBuilder, config);
}

This basically is the implementation of calling the constructor ClasspathXmlConfig(ClassLoader, String), except that the new instance is not handed to the build(Config) method, but a new instance that has a classloader set. I need reflection to get around the package private restriction of the build(Config) method.

Does anybody know of another way to get this working without the hack?

Regards,

Rens van Leeuwen

Noctarius

unread,
Mar 6, 2014, 12:12:23 AM3/6/14
to haze...@googlegroups.com
Hi Rens,

I guess we just need a XmlConfigBuilder::build(ClassLoader) method where you can explicitly pass in a different class loader you want to use, isn’t it? :)

Chris

--
You received this message because you are subscribed to the Google Groups "Hazelcast" group.
To unsubscribe from this group and stop receiving emails from it, send an email to hazelcast+...@googlegroups.com.
To post to this group, send email to haze...@googlegroups.com.
Visit this group at http://groups.google.com/group/hazelcast.
To view this discussion on the web visit https://groups.google.com/d/msgid/hazelcast/064814ff-1731-4800-896c-246b8f20e446%40googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Rens van Leeuwen

unread,
Mar 6, 2014, 5:04:18 AM3/6/14
to haze...@googlegroups.com, noctar...@googlemail.com
Hi Chris,

Yes, but I would say that that should only be done if it doesn't break an API, or a design philosophy? There must be a reason why no such method is available in the ConfigBuilder interface.

If this is considered a bug (as opposed to a feature), then the classloader that is used for the various concrete Config types need to propagate their classloader to the XmlConfigBuilder somehow. That could be done 'behind the curtains' and not by means of using a new method in a public interface. In the way it is done at this point too.

Could somebody give me advice how to go on about this? The hack will work for the short term.

Regards,

Rens

Noctarius

unread,
Mar 6, 2014, 5:18:35 AM3/6/14
to Rens van Leeuwen, haze...@googlegroups.com
Let me have another look on that issue for 3.3. I guess we’re going to either introduce a setClassLoader method on XmlConfigBuilder or a new build method overload taking the ClassLoader to be set inside the Config object.

I more see it as a “missing fact”. We just missed to introduce it but that shouldn’t be a problem, so see it as a bug. Even if bugs are still fixed for final 3.2 I would like to move this to 3.2.1 or 3.3.

Can you create a github issue for that?

Thanks,
Chris

Rens van Leeuwen

unread,
Mar 6, 2014, 3:33:07 PM3/6/14
to haze...@googlegroups.com, Rens van Leeuwen, noctar...@googlemail.com
Hi Chris,

I signed up on GitHub (it was about time ;)) and created https://github.com/hazelcast/hazelcast/issues/1936 for it.

Thanks for your feedback.

Regards,

Rens

Noctarius

unread,
Mar 6, 2014, 3:33:55 PM3/6/14
to Rens van Leeuwen, haze...@googlegroups.com
Thanks Rens :)
Reply all
Reply to author
Forward
0 new messages