| Author |
Message |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/19/2008 00:38:15
|
titeuf1
neo
Joined: 11/19/2008 00:17:22
Messages: 7
Offline
|
hello guys!
I have some trouble / misunderstanding concerning DMI. What I basically try is to avoid JMS as we use Terracotta 2.6.4 anyway. So in my special case each VM runs some backend functionality but I need a way to "control" all these different instances (one per vm) from a central place without JMS/JMX. This is where I thought DMI could be of help. So regardless on which JVM you call a backend method this call should be transparently propagated to all others "backends" in the cluster.
Please see sample project.
Backendprocess (this will do all the heavy backend work. one instance per vm):
Code:
import java.util.UUID;
public class HeavyNonPortableService
{
final UUID _nonPortableServiceExampleOnlyField;
public HeavyNonPortableService()
{
_nonPortableServiceExampleOnlyField = UUID.randomUUID();
System.out.println("HeavyNonPortableService created [" + _nonPortableServiceExampleOnlyField + "] instance=" + this);
}
public void serviceMethod(final String s)
{
System.out.println("serviceMethod [" + s + "] for [" + _nonPortableServiceExampleOnlyField + "] instance=" + this);
}
}
a simple service wrapper to introduce DMI. This service wrapper is shared but basically should do the DMI magic in each VM and pass the message (method call) to the backend part. Parts of it are stolen from the terracotta samples:
Code:
public class ServiceWrapper
{
public class DMI
{
public void internalBroadcast(final String s)
{
_heavyNonPortableService.serviceMethod(s);
}
}
private final HeavyNonPortableService _heavyNonPortableService;
private final DMI _dmi;
public ServiceWrapper()
{
_heavyNonPortableService = new HeavyNonPortableService();
_dmi = new DMI();
}
public void broadcastMethod(final String s)
{
_dmi.internalBroadcast(s);
}
}
and this is the tc-config
Code:
<?xml version="1.0" encoding="UTF-8"?>
<con:tc-config xmlns:con="http://www.terracotta.org/config">
<system>
<configuration-model>production</configuration-model>
</system>
<servers>
<server host="%i" name="local">
<dso-port>9510</dso-port>
<data>terracotta</data>
<logs>terracotta/server-logs</logs>
<statistics>terracotta/cluster-statistics</statistics>
</server>
</servers>
<clients>
<logs>/data/terracotta/logs/%(node.name)/</logs>
<statistics>/data/terracotta/client-statistics/%D/%(node.name)</statistics>
</clients>
<application>
<dso>
<roots>
<root>
<field-name>ServiceWrapper._dmi</field-name>
<root-name>Schedule_DMI</root-name>
</root>
</roots>
<instrumented-classes>
<include>
<class-expression>ServiceWrapper$DMI</class-expression>
</include>
</instrumented-classes>
<locks>
<autolock auto-synchronized="true">
<method-expression>* ServiceWrapper.__INIT__(..)</method-expression>
<lock-level>write</lock-level>
</autolock>
</locks>
<distributed-methods>
<method-expression>void ServiceWrapper$DMI.internalBroadcast(..)</method-expression>
</distributed-methods>
<transient-fields>
<field-name>ServiceWrapper._heavyNonPortableService</field-name>
</transient-fields>
</dso>
</application>
</con:tc-config>
and finally the "main" to start the service on each machine
Code:
/**
* -Xbootclasspath/p:/terracotta-2.6.4/lib/dso-boot/dso-boot-hotspot_osx_150_16.jar
* -Dtc.install-root="/terracotta-2.6.4"
* -Dtc.config=localhost:9510
*/
public class Sample_TerracottaDMI
{
public static void main(final String[] args)
{
final ServiceWrapper serviceWrapper = new ServiceWrapper();
serviceWrapper.broadcastMethod("hello");
try {
Thread.currentThread().join();
// Thread.sleep(60000); // block, whatever
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
}
The following output is produced in case of two running applications
Code:
VM1
HeavyNonPortableService created [04bfb9a5-6b3b-4585-8836-95a208e7a469] instance=HeavyNonPortableService@f71ad4
serviceMethod [hello] for [04bfb9a5-6b3b-4585-8836-95a208e7a469] instance=HeavyNonPortableService@f71ad4
serviceMethod [hello] for [04bfb9a5-6b3b-4585-8836-95a208e7a469] instance=HeavyNonPortableService@f71ad4
as you can see vm1 recognizes two calls to the service method which is correct (one call from each started vm). But VM2 is in trouble:
Code:
HeavyNonPortableService created [55bc94d1-4761-4b91-a667-ce38440118b3] instance=HeavyNonPortableService@851105
Exception in thread "main" java.lang.NullPointerException
at ServiceWrapper$DMI.__tc_dmi_internalBroadcast(ServiceWrapper.java:7)
at ServiceWrapper$DMI.internalBroadcast(ServiceWrapper.java)
at ServiceWrapper.broadcastMethod(ServiceWrapper.java:24)
at Sample_TerracottaDMI.main(Sample_TerracottaDMI.java:11)
Thanks in advance
Jens
|
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/19/2008 01:18:38
|
tgautier
seraphim
Joined: 06/05/2006 12:19:26
Messages: 1781
Offline
|
you made the field _heavyNonPortableService transient. So when the ServiceWrapper is materialized in the second JVM, it's null.
Don't be fooled by the new DMI() that is called from the two ServiceWrapper instances - you are only ever getting one of these and the second assignment is thrown away. So there is only one _dmi instance, and it's pointing to the instance that was created by the first VM. In the second VM, when the _dmi instance is materialized, it's going to point back to the ServiceWrapper instance from the first VM, and the _heavyNonPortableService field is transient so it's going to be null since yo didn't initialize it.
More details here: http://tech.puredanger.com/2007/08/08/hello-terracotta/
Have you gone through the cookbook examples? http://www.terracotta.org/web/display/orgsite/Tutorials
|
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/19/2008 02:00:25
|
tgautier
seraphim
Joined: 06/05/2006 12:19:26
Messages: 1781
Offline
|
I wrote some code some time ago that I think does what you are trying to do and put it in the tc-lib library - (now somewhat defunct, at least for the master worker part since that is now tim-messaging argh).
Should give you the idea:
http://svn.terracotta.org/svn/forge/projects/labs/tim-tclib/trunk/samples/strategy/readme.html
|
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/19/2008 02:14:31
|
titeuf1
neo
Joined: 11/19/2008 00:17:22
Messages: 7
Offline
|
Wow. Thanks for the answer in near realtime..
yes, i made it "transient" as I try to avoid the instrumentation of tons of classes required by the backend only, which only exist in the space of the VM and some of them are non portable. I am only interested in sharing the callback, not all backend classes. I can try to instrument them all, but I was looking for a solution in case of "non portable classes". All I need is a DMI callback to an instance which itself is *not* shared.
If "transient" is removed immediately the trouble starts with "instrument the HeavyNonPortableService" and add "UUID to the additional-boot-jar-classes" aso.
Any other solution here?
|
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/19/2008 12:02:43
|
gkeim
ophanim
Joined: 12/05/2006 10:22:37
Messages: 685
Location: Terracotta, Inc.
Offline
|
This is in the docs Taylor referred to.
Leave it transient, don't make it final, and add this to your config:
Code:
<include>
<class-expression>ServiceWrapper</class-expression>
<on-load>
<execute>self._heavyNonPortableService = new HeavyNonPortableService();</execute>
</on-load>
</include>
|
Gary Keim (terracotta developer) Want to post to this forum? Join the Terracotta Community |
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/19/2008 15:09:23
|
titeuf1
neo
Joined: 11/19/2008 00:17:22
Messages: 7
Offline
|
after small modifications I got it working, thanks (HeavyNonPortable is now a member of DMI, otherwise the broadcastMethod has no effect when called on the second instance).
Unfortunately there is always a but: due to the TC-Config "on-load" entry and the "redundant" new call in the DMI member initialization the HeavyNonPortable class is instantiated twice, but the first instance is thrown away. I can live with this behaviour but nevertheless this seems to be strange...
To be honest the DMI is more difficult as expected at least from a newbie point of view :-)
|
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/20/2008 06:06:14
|
titeuf1
neo
Joined: 11/19/2008 00:17:22
Messages: 7
Offline
|
Sorry, but journey still continues. "I forgot to mention" that the service is created by Spring using CTOR injection, so the onLoad trick is not working here. Dead end reached?
|
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/20/2008 07:26:48
|
tgautier
seraphim
Joined: 06/05/2006 12:19:26
Messages: 1781
Offline
|
tgautier wrote:
I wrote some code some time ago that I think does what you are trying to do and put it in the tc-lib library - (now somewhat defunct, at least for the master worker part since that is now tim-messaging argh).
Should give you the idea:
http://svn.terracotta.org/svn/forge/projects/labs/tim-tclib/trunk/samples/strategy/readme.html
I think you missed this post. It shows how to use Queues, not DMI which will be a better solution.
|
|
|
 |
![[Post New]](/forums/templates/default/images/icon_minipost_new.gif) 11/21/2008 00:41:27
|
titeuf1
neo
Joined: 11/19/2008 00:17:22
Messages: 7
Offline
|
I read about the master / worker pattern before, but as far I understood it this just guarantees one worker is running on one machine within the cluster to do a job. I need multiple workers doing the same job on *each* VM.
In the meantime I believe DMI is not the right choice here. Hopefully it is allowed for a newbie to make a feature request:
Why can't you support something like a "broadcast-method" via tc-config which means: regardless in which VM the method is called it gets propagated to all others. This would be a really transparent DMI without the need to instrument tons of classes and the need for roots. And this "real" callback should allow an easy decoupling from the application and the terracotta DSO world (I guess it is just a new message to be send from TC-SERVER via the socket to the client). At the moment I have to use libs or to write tools to get the job done, which is odd because now the code depends on terracotta which terracotta DSO assumes to avoid? For sure I can not see the consequences or problems arising here, but I think that's the way how DMI should work from my point of view. Am I wrong?
And yes I switched to a LinkedBlockingQueue impl at the moment...
Again, thanks for the input and help
|
|
|
 |
|
|