[Logo] Terracotta Discussion Forums
  [Search] Search   [Recent Topics] Recent Topics   [Members]  Member Listing   [Groups] Back to home page 
[Register] Register / 
[Login] Login 
[Expert]
Problems using DMI  XML
Forum Index -> Terracotta Platform
Author Message
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
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
[WWW]
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
[WWW]
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?
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
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 :-)
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?
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.
[WWW]
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
 
Forum Index -> Terracotta Platform
Go to:   
Powered by JForum 2.1.7 © JForum Team