capture
[rrq/gorite.git] / com / intendico / gorite / addon / BellBoy.java
1 /*********************************************************************
2 Copyright 2012, Ralph Ronnquist.
3
4 This file is part of GORITE.
5
6 GORITE is free software: you can redistribute it and/or modify it
7 under the terms of the Lesser GNU General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GORITE is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14 License for more details.
15
16 You should have received a copy of the Lesser GNU General Public
17 License along with GORITE.  If not, see <http://www.gnu.org/licenses/>.
18 **********************************************************************/
19
20 package com.intendico.gorite.addon;
21
22 import com.intendico.gorite.*;
23 import java.util.Hashtable;
24 import java.util.Set;
25
26 /**
27  * This is a {@link Capability} (type) that provides a simple
28  * inter-model messaging service within a single JVM. It uses the
29  * {@link Performer#getName()} method of the containing {@link
30  * Performer} as messaging end point identity. The idea is that all
31  * communicating performers are set up to have their own instance of
32  * this capability type. The following is an example of use, with a
33  * receive plan outline:
34  *
35  * <pre>
36  * new Performer( "morgan" ) {{
37  *     addCapability( new BellBoy() );
38  *     addPlan( new Plan( BellBoy.RECV, new Goal [] {
39  *         .... deal with received message in "percept"
40  *     } );
41  * }};
42  * </pre>
43  *
44  * <p><i>Sending Messages</i>
45  *
46  * <p> The capability handles a {@link #SEND "Send BellBoy message"}
47  * {@link Goal goal}, with {@link #MSG "bellboy message"} holding a
48  * {@link BellBoy.Envelope message envelope}, by delivering that
49  * message to the {@link Envelope#getWhom() indicated recipient}.
50  * 
51  * <p> Sending can also be done directly in Java code by using the
52  * static {@link BellBoy#send(Envelope)} or {@link
53  * BellBoy#send(String,Object)} methods.
54  *
55  * <p><i>Receiving Messages</i>
56  *
57  * <p> Upon receiving a message, the capability triggers a {@link
58  * #RECV "Got BellBoy message"} {@link BDIGoal} goal, on the {@link
59  * #TODO "bellboy"} {@link com.intendico.gorite.Performer.TodoGroup
60  * TodoGroup} and with {@link Perceptor#data_name "percept"} holding
61  * the message,
62  *
63  * <p> Reception can also be triggered in Java code by using the
64  * {@link #receive(Object)} method.
65  *
66  * <p> <b>NOTE</b>
67  *
68  * <p>Messages are delivered through in-core sharing, and they are not
69  * copied! <b>Not copied!</b> This means that the sender and receiver
70  * end up sharing the one message object. In proper use, the sender
71  * should release all its handles to the message object after having
72  * sent it. The capability supports this by {@link
73  * Data#forget(String) forgetting} {@link #MSG "bellboy message"}
74  * after having sent the message.
75  */
76 public class BellBoy extends Capability {
77
78     /**
79      * The interface for standard BellBoy messages.
80      */
81     public interface Envelope {
82         /**
83          * Returns message recipient identity.
84          */
85         public String getWhom();
86
87         /**
88          * Returns the message payload, i.e., the "actual" message.
89          */
90         public Object getPayload();
91     }
92
93     /**
94      * Convenience constant for the send goal.
95      */
96     public final static String SEND = "Send BellBoy message";
97
98     /**
99      * Convenience constant for the receive goal.
100      */
101     public final static String RECV = "Got BellBoy message";
102
103     /**
104      * Convenience constant for the send goal data element.
105      */
106     public final static String MSG = "bellboy message";
107
108     /**
109      * Convenience constant for BellBoy messaging {@link
110      * com.intendico.gorite.Performer.TodoGroup TodoGroup}.
111      */
112     public final static String TODO = "bellboy";
113
114     /**
115      * The table of communicating BellBoy instances within this JVM.
116      */
117     public static Hashtable/*<String,BellBoy>*/ club =
118         new Hashtable/*<String,BellBoy>*/();
119
120     /**
121      * The {@link Perceptor} for receiving BellBoy messages. This is
122      * set up by the {@link #initialize()}, which is invoked
123      * automatically by GORITE. The installed {@link Perceptor}
124      * triggers {@link #RECV "Got BellBoy message"} goals on the
125      * {@link #TODO "bellboy"} {@link
126      * com.intendico.gorite.Performer.TodoGroup TodoGroup} with the
127      * received messages as the {@link Perceptor#data_name "percept"}
128      * data.
129      */
130     public Perceptor recv;
131
132     /**
133      * Binds a BellBoy to be a {@link #club} member under a given
134      * name. This replaces any previous binding, and binding
135      * <tt>null</tt> removes the previous binding.
136      */
137     public static void bind(String name,BellBoy b) {
138         if ( b == null ) {
139             synchronized ( club ) {
140                 club.remove( name );
141             }
142         } else {
143             synchronized ( club ) {
144                 club.put( name, b );
145             }
146         }
147     }
148
149     /**
150      * Returns the BellBoy bound to a given name.
151      */
152     public static BellBoy find(String name) {
153         synchronized ( club ) {
154             return (BellBoy) club.get( name );
155         }
156     }
157
158     /**
159      * Returns the names of the current {@link BellBoy#club} members.
160      */
161     public static Set/*<String>*/ getMembers() {
162         synchronized ( club ) {
163             return club.keySet();
164         }
165     }
166
167     /**
168      * Creates a standard BellBoy {@link Envelope}.
169      */
170     public static Envelope pack(final String whom,final Object what) {
171         return new Envelope() {
172             public String getWhom() {
173                 return whom;
174             }
175             public Object getPayload() {
176                 return what;
177             }
178         };
179     }
180
181     /**
182      * Receives a message. This causes a percept on the {@link #recv}
183      * {@link Perceptor}. The method returns false if {@link #recv} is
184      * unassigned, otherwise it returns true.
185      */
186     public boolean receive(Object m) {
187         if ( recv == null )
188             return false;
189         recv.perceive( m );
190         return true;
191     }
192
193     /**
194      * Sends a named recipient a message.
195      */
196     public static boolean send(String to,Object message) {
197         BellBoy whom = find( to );
198         return ( whom == null )? false : whom.receive( message );
199     }
200
201     /**
202      * Sends an envelope.
203      */
204     public static boolean send(Envelope m) {
205         return ( m == null )? false : send( m.getWhom(), m.getPayload() );
206     }
207
208     /**
209      * Initializes the capability with the abilities to send and
210      * receive messages, and then it joins the BellBoy club.
211      *
212      * <p> The capability is set up to handle a {@link #SEND "Send
213      * BellBoy message"} {@link Goal goal}, with {@link #MSG "bellboy
214      * message"} holding a {@link BellBoy.Envelope message envelope},
215      * by delivering that message to the {@link Envelope#getWhom()
216      * indicated recipient}.
217      *
218      * The goal fails if {@link #MSG "bellboy message"} is null. If
219      * it's non-null but not a {@link BellBoy.Envelope}, then a {@link
220      * ClassCastException} is thrown.
221      *
222      * The goal also fails if the {@link BellBoy#club} doesn't include
223      * the desired recipient.
224      */
225     public void initialize() {
226         // Prepare to receive messages
227         recv = new Perceptor( getPerformer(), RECV, TODO );
228         // Define ability to send message as goal
229         addGoal( new Goal( SEND ) {
230             public States execute(Data data) {
231                 if ( send( (Envelope) data.getValue( MSG ) ) ) {
232                     data.forget( MSG );
233                     return States.PASSED;
234                 }
235                 return States.FAILED;
236             }
237         } );
238         // Join the club
239         bind( getPerformer().getName(), this );
240     }
241
242 }