View Javadoc

1   /*
2      Copyright 2007 Ramon Servadei
3   
4      Licensed under the Apache License, Version 2.0 (the "License");
5      you may not use this file except in compliance with the License.
6      You may obtain a copy of the License at
7   
8          http://www.apache.org/licenses/LICENSE-2.0
9   
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15   */
16  package fulmine.model.container;
17  
18  import static fulmine.util.Utils.logException;
19  import static fulmine.util.Utils.safeToString;
20  
21  import java.util.Collection;
22  import java.util.List;
23  
24  import fulmine.IDomain;
25  import fulmine.IType;
26  import fulmine.Type;
27  import fulmine.context.IFrameworkContext;
28  import fulmine.model.container.events.ContainerFieldAddedEvent;
29  import fulmine.model.container.events.ContainerFieldRemovedEvent;
30  import fulmine.model.field.IField;
31  import fulmine.model.field.containerdefinition.ContainerDefinitionField;
32  import fulmine.model.field.containerdefinition.IContainerDefinitionField;
33  import fulmine.protocol.specification.FieldReader;
34  import fulmine.protocol.specification.FrameReader;
35  import fulmine.protocol.wire.operation.IOperationScope;
36  import fulmine.util.collection.CollectionFactory;
37  
38  /**
39   * Base class for dynamic containers.
40   * <p>
41   * This implementation is thread safe. A dynamic container is a container with a
42   * variable field population; fields can be added and removed arbitrarily during
43   * the container's life-cycle.
44   * <p>
45   * A dynamic container maintains a field that is the
46   * {@link IContainerDefinitionField} to describe the field population for the
47   * container. This definition is sent in the wire frame as the first field. This
48   * allows receiving context instances to correctly construct the field
49   * population of the dynamic container. This is a key difference between a
50   * static and dynamic container structure.
51   * <p>
52   * This raises the following events:
53   * <ul>
54   * <li>{@link ContainerFieldAddedEvent} when a field is added
55   * <li>{@link ContainerFieldRemovedEvent} when a field is removed
56   * </ul>
57   * 
58   * @author Ramon Servadei
59   */
60  public abstract class AbstractDynamicContainer extends
61      AbstractEventProcessingContainer
62  {
63      /**
64       * The reader task executed by the {@link FieldReader} when reading messages
65       */
66      protected final class ReaderTask extends AbstractContainer.ReaderTask
67      {
68          @Override
69          public void read(IOperationScope scope, int fieldId, byte[] dataBuffer,
70              int dataStart, int dataLen)
71          {
72              // dynamic containers send their definition when it changes
73              if (fieldId == Type.CONTAINER_DEFINITION.value())
74              {
75                  getDefinition().readState(scope, dataBuffer, dataStart, dataLen);
76              }
77              else
78              {
79                  super.read(scope, fieldId, dataBuffer, dataStart, dataLen);
80              }
81          }
82      }
83  
84      /** The identity of the definition */
85      private final String definitionId;
86  
87      /**
88       * Standard constructor. A {@link ContainerDefinitionField} is constructed
89       * and added as a field of this. The constructor calls the {@link #start()}
90       * method.
91       * 
92       * @param nativeContextIdentity
93       *            the name of the context this container is native to - the name
94       *            of its local context
95       * @param identity
96       *            the identity of the container
97       * @param type
98       *            the type of the container
99       * @param domain
100      *            the domain for the container
101      * @param hostContext
102      *            the context hosting this container instance
103      * @param local
104      *            <code>true</code> the container is local to this context
105      */
106     public AbstractDynamicContainer(String nativeContextIdentity,
107         String identity, IType type, IDomain domain,
108         IFrameworkContext hostContext, boolean local)
109     {
110         super(nativeContextIdentity, identity, type, domain, hostContext, local);
111         final ContainerDefinitionField containerDefinition =
112             new ContainerDefinitionField(this);
113         this.definitionId = containerDefinition.getIdentity();
114         /*
115          * this is not pretty - we need to masquerade as being in a FrameReader
116          * context to ensure we can bypass the usual checks that prevent remote
117          * containers being mutated - its the action of adding a container
118          * definition that requires this
119          */
120         boolean alreadyInContext = FrameReader.inContext.get().booleanValue();
121         try
122         {
123             if (!alreadyInContext && !isLocal())
124             {
125                 FrameReader.inContext.set(Boolean.TRUE);
126             }
127             add(containerDefinition);
128         }
129         finally
130         {
131             if (!alreadyInContext && !isLocal())
132             {
133                 FrameReader.inContext.remove();
134             }
135         }
136         start();
137     }
138 
139     @Override
140     protected final void afterAdd(IField field)
141     {
142         super.afterAdd(field);
143         final IContainerDefinitionField containerDefinition =
144             ((IContainerDefinitionField) get(this.definitionId));
145         containerDefinition.add(field);
146         // don't raise the event for the ContainerDefinitionField
147         if (!(field instanceof IContainerDefinitionField))
148         {
149             try
150             {
151                 if (getContext() != null && getContext().isActive())
152                 {
153                     getContext().queueEvent(
154                         new ContainerFieldAddedEvent(this,
155                             (IField) field.clone()));
156                 }
157             }
158             catch (CloneNotSupportedException e)
159             {
160                 logException(getLog(), getIdentity()
161                     + " could not raise ContainerFieldAddedEvent for "
162                     + safeToString(field), e);
163             }
164         }
165     }
166 
167     @Override
168     protected final void afterRemove(IField field, IField removed)
169     {
170         super.afterRemove(field, removed);
171         if (removed != null)
172         {
173             final IContainerDefinitionField containerDefinition =
174                 ((IContainerDefinitionField) get(this.definitionId));
175             containerDefinition.remove(removed);
176             try
177             {
178                 if (getContext() != null && getContext().isActive())
179                 {
180 
181                     getContext().queueEvent(
182                         new ContainerFieldRemovedEvent(this,
183                             (IField) removed.clone()));
184                 }
185             }
186             catch (CloneNotSupportedException e)
187             {
188                 logException(getLog(), getIdentity()
189                     + " could not raise ContainerFieldRemovedEvent for "
190                     + safeToString(field), e);
191             }
192         }
193     }
194 
195     public final boolean isDynamic()
196     {
197         return true;
198     }
199 
200     @Override
201     protected final IContainerDefinitionField getDefinition()
202     {
203         return (IContainerDefinitionField) get(this.definitionId);
204     }
205 
206     @Override
207     protected final ReaderTask newReaderTask()
208     {
209         return this.new ReaderTask();
210     }
211 
212     @Override
213     protected final Collection<IField> getFieldsToWrite(boolean completeState)
214     {
215         if (completeState)
216         {
217             return doGetComponentsToWriteWithDefinition(completeState);
218         }
219         /*
220          * If the definition has changed, it must be the first field written
221          * into the frame. The receiving end needs the definition first in order
222          * to make sense of any new fields being sent.
223          */
224         if (super.changedFields.contains(getDefinition()))
225         {
226             return doGetComponentsToWriteWithDefinition(completeState);
227         }
228         return super.getFieldsToWrite(completeState);
229     }
230 
231     private Collection<IField> doGetComponentsToWriteWithDefinition(
232         boolean completeState)
233     {
234         final List<IField> changes =
235             CollectionFactory.newList(completeState ? getFields().size()
236                 : super.changedFields.size());
237         // ensure the definition is at the head of the changes
238         if (completeState)
239         {
240             changes.addAll(getFields().values());
241             changes.remove(getDefinition());
242             changes.add(0, getDefinition());
243         }
244         else
245         {
246             changes.add(getDefinition());
247             super.changedFields.remove(getDefinition());
248             changes.addAll(super.changedFields);
249         }
250         return changes;
251     }
252 }