001/* A publisher that transparently tunnels messages to subscribers. 002 003 Copyright (c) 2006-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.actor.lib; 029 030import java.util.Set; 031 032import ptolemy.actor.AtomicActor; 033import ptolemy.actor.PublisherPort; 034import ptolemy.actor.TypedAtomicActor; 035import ptolemy.actor.TypedIOPort; 036import ptolemy.actor.util.ActorDependencies; 037import ptolemy.data.BooleanToken; 038import ptolemy.data.Token; 039import ptolemy.data.expr.Parameter; 040import ptolemy.data.expr.SingletonParameter; 041import ptolemy.data.expr.StringParameter; 042import ptolemy.data.type.BaseType; 043import ptolemy.kernel.CompositeEntity; 044import ptolemy.kernel.util.IllegalActionException; 045import ptolemy.kernel.util.KernelException; 046import ptolemy.kernel.util.NameDuplicationException; 047import ptolemy.kernel.util.Workspace; 048 049/** 050 This actor publishes input tokens on a named channel. The tokens are 051 "tunneled" to any instance of {@link Subscriber} that names the same channel. 052 If {@link #global} is false (the default), then this publisher 053 will only send to instances of Subscriber that are under the 054 control of the same director. That is, it can 055 be at a different level of the hierarchy, or in an entirely different 056 composite actor, as long as the relevant composite actors are 057 transparent (have no director). If {@link #global} is true, 058 then the subscriber may be anywhere in the model, as long as its 059 <i>global</i> parameter is also true. 060 <p> 061 It is an error to have two instances of Publisher using the same 062 channel under the control of the same director. When you create a 063 new Publisher, by default, it has no channel name. You have to 064 specify a channel name to use it. 065 <p> 066 <b>How it works:</b> 067 This actor has a hidden output port. When the channel name 068 is specified, typically during model construction, this actor 069 causes a relation to be created in the least opaque composite 070 actor above it in the hierarchy and links to that relation. 071 In addition, if {@link #global} is set to true, it causes 072 a port to be created in that composite, and also links that 073 port to the relation on the inside. The relation is recorded by the opaque 074 composite. When a Subscriber is preinitialized that refers 075 to the same channel, that Subscriber finds the relation (by 076 finding the least opaque composite actor above it) and links 077 to the relation. Some of these links are "liberal links" in that 078 they cross levels of the hierarchy. 079 <p> 080 Since publishers are linked to subscribers, 081 any data dependencies that the director might assume on a regular 082 "wired" connection will also be assumed across Publisher-Subscriber 083 pairs. Similarly, type constraints will propagate across 084 Publisher-Subscriber pairs. That is, the type of the Subscriber 085 output will match the type of the Publisher input. 086 087 @author Edward A. Lee, Raymond A. Cardillo, Bert Rodiers 088 @version $Id$ 089 @since Ptolemy II 5.2 090 @Pt.ProposedRating Green (cxh) 091 @Pt.AcceptedRating Red (cxh) 092 */ 093public class Publisher extends TypedAtomicActor { 094 095 /** Construct a publisher with the specified container and name. 096 * @param container The container actor. 097 * @param name The name of the actor. 098 * @exception IllegalActionException If the actor is not of an acceptable 099 * class for the container. 100 * @exception NameDuplicationException If the name coincides with 101 * an actor already in the container. 102 */ 103 public Publisher(CompositeEntity container, String name) 104 throws IllegalActionException, NameDuplicationException { 105 super(container, name); 106 107 channel = new StringParameter(this, "channel"); 108 109 input = new TypedIOPort(this, "input", true, false); 110 input.setMultiport(true); 111 112 output = new PublisherPort(this, "output"); 113 output.setMultiport(true); 114 output.setTypeAtLeast(input); 115 116 // We only have constraints from the publisher on the subscriber 117 // and the output of the subscriber and not the other way around 118 // to not break any existing models. 119 output.setWidthEquals(input, false); 120 121 Parameter hide = new SingletonParameter(output, "_hide"); 122 hide.setToken(BooleanToken.TRUE); 123 // hide = new SingletonParameter(this, "_hideName"); 124 // hide.setToken(BooleanToken.TRUE); 125 126 global = new Parameter(this, "global"); 127 global.setExpression("false"); 128 global.setTypeEquals(BaseType.BOOLEAN); 129 130 propagateNameChanges = new Parameter(this, "propagateNameChanges"); 131 propagateNameChanges.setExpression("false"); 132 propagateNameChanges.setTypeEquals(BaseType.BOOLEAN); 133 134 // Refer the parameters of the output port to those of 135 // this actor. 136 output.channel.setExpression("$channel"); 137 output.global.setExpression("global"); 138 output.propagateNameChanges.setExpression("propagateNameChanges"); 139 } 140 141 /////////////////////////////////////////////////////////////////// 142 //// ports and parameters //// 143 144 /** The name of the channel. Subscribers that reference this same 145 * channel will receive any transmissions to this port. 146 * This is a string that defaults to empty, indicating that 147 * no channel is specified. A channel must be set before 148 * the actor executes or an exception will occur. 149 */ 150 public StringParameter channel; 151 152 /** Specification of whether the published data is global. 153 * If this is set to true, then a subscriber anywhere in the model that 154 * references the same channel by name will see values published by 155 * this publisher. If this is set to false (the default), then only 156 * those subscribers that are fired by the same director will see 157 * values published on this channel. 158 */ 159 public Parameter global; 160 161 /** The input port. This is a multiport, allowing multiple 162 * signals to be be transmitted through the publisher channel. 163 * This base class imposes no type constraints except 164 * that the type of the input cannot be greater than the type of the 165 * output. 166 */ 167 public TypedIOPort input; 168 169 /** The output port. This port is hidden and should not be 170 * directly used. By default, the type of this output is constrained 171 * to be at least that of the input. This port is hidden by default 172 * and the actor handles creating connections to it. 173 */ 174 public PublisherPort output; 175 176 /** If true, then propagate channel name changes to any 177 * Subscribers. The default value is a BooleanToken with the 178 * value false, indicating that if the channel name is changed, 179 * then the channel names of the Subscribers are not changed. If 180 * the value is true, then if the channel name is changed, the 181 * channel names of the connected Subscribers are updated. 182 * 183 * <p>If the value is true, then SubscriptionAggregators that 184 * have the same regular expression as the channel name of the 185 * Publisher will be updated. However, SubscriptionAggregators 186 * usually have regular expressions as channel names, so usually 187 * the channel name of the SubscriptionAggregator will <b>not</b> 188 * be updated.</p> 189 * 190 * <p>Note that if a Publisher is within an Actor Oriented Class 191 * definition, then any Subscribers with the same channel name in 192 * Actor Oriented Class definitions will <b>not</b> be updated. 193 * This is because there is no connection between the Publisher 194 * in the Actor Oriented Class definition and the Subscriber. 195 * However, if the channel name in a Publisher in an instance of 196 * an Actor Oriented Class is updated, then the 197 * corresponding Subscribers in instances of Actor Oriented Class 198 * will be updated.</p> 199 */ 200 public Parameter propagateNameChanges; 201 202 /////////////////////////////////////////////////////////////////// 203 //// public methods //// 204 205 /** Clone the actor into the specified workspace. 206 * @param workspace The workspace for the new object. 207 * @return A new actor. 208 * @exception CloneNotSupportedException If a derived class contains 209 * an attribute that cannot be cloned. 210 */ 211 @Override 212 public Object clone(Workspace workspace) throws CloneNotSupportedException { 213 Publisher newObject = (Publisher) super.clone(workspace); 214 215 // We only have constraints from the publisher on the subscriber 216 // and the output of the subscriber and not the other way around 217 // to not break any existing models. 218 newObject.output.setWidthEquals(newObject.input, false); 219 220 newObject.output.setTypeAtLeast(newObject.input); 221 222 return newObject; 223 } 224 225 /** Read at most one input token from each 226 * input channel and send it to the subscribers, 227 * if any. 228 * @exception IllegalActionException If there is no director. 229 */ 230 @Override 231 public void fire() throws IllegalActionException { 232 super.fire(); 233 for (int i = 0; i < input.getWidth(); i++) { 234 if (input.hasToken(i)) { 235 Token token = input.get(i); 236 output.send(i, token); 237 } 238 } 239 } 240 241 /** Override the base class to ensure that links to subscribers 242 * have been updated. 243 * @exception IllegalActionException If there is already a publisher 244 * publishing on the same channel, or if the channel name has not 245 * been specified. 246 */ 247 @Override 248 public void preinitialize() throws IllegalActionException { 249 String channelValue = channel.stringValue(); 250 if (channelValue == null || channelValue.trim().equals("")) { 251 throw new IllegalActionException(this, 252 "No channel name has been specified."); 253 } 254 255 // Call super.preinitialize() after updating links so that 256 // we have connections made before possibly inferring widths. 257 super.preinitialize(); 258 } 259 260 /** Return a Set of Subscribers that are connected to this Publisher. 261 * @return A Set of Subscribers that are connected to this Publisher 262 * @exception KernelException If thrown when a Manager is added to 263 * the top level or if preinitialize() fails. 264 */ 265 public Set<AtomicActor> subscribers() throws KernelException { 266 return ActorDependencies.dependents(this, Subscriber.class); 267 } 268}