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 java.util.Map;
19
20 import fulmine.IDomain;
21 import fulmine.IType;
22 import fulmine.Type;
23 import fulmine.context.IFrameworkContext;
24 import fulmine.model.container.impl.Record;
25 import fulmine.model.field.containerdefinition.IContainerDefinitionField;
26 import fulmine.protocol.specification.FrameReader;
27 import fulmine.util.collection.CollectionFactory;
28 import fulmine.util.log.AsyncLog;
29
30 /**
31 * A factory that creates {@link IContainer} implementations. An
32 * {@link IContainerFactory.IContainerBuilder} is registered for each container
33 * type (see {@link IContainer#getType()}). The registered builder creates each
34 * instance. The builder is registered using the
35 * {@link #registerBuilder(IType, fulmine.model.container.ContainerFactory.IContainerBuilder)}
36 * method. By default, if there is no builder registered for a container type, a
37 * {@link Record} is created.
38 * <p>
39 * The factory is thread safe.
40 *
41 * @author Ramon Servadei
42 */
43 public final class ContainerFactory implements IContainerFactory
44 {
45 private final static AsyncLog LOG = new AsyncLog(ContainerFactory.class);
46
47 /**
48 * The static {@link IContainerDefinitionField} objects mapped to their
49 * {@link IType}.
50 */
51 private final Map<IType, IContainerDefinitionField> staticDefinitions;
52
53 /**
54 * The {@link IContainerFactory.IContainerBuilder} objects mapped to their
55 * {@link IType}.
56 */
57 private final Map<IType, IContainerFactory.IContainerBuilder> builders;
58
59 /** The default builder */
60 private final IContainerFactory.IContainerBuilder defaultBuilder;
61
62 /**
63 * Standard constructor
64 */
65 public ContainerFactory()
66 {
67 super();
68 this.defaultBuilder = new RecordBuilder();
69 builders = CollectionFactory.newMap();
70 staticDefinitions = CollectionFactory.newMap();
71 }
72
73 /**
74 * Register an {@link IContainerFactory.IContainerBuilder} against the
75 * {@link IContainer} type. This will overwrite any existing builder
76 * registered against the type.
77 *
78 * @param type
79 * the type of the {@link IContainer} the builder creates
80 * @param builder
81 * the container builder to register
82 * @see #containsType(IType)
83 * @see IContainer#getType()
84 * @throws IllegalArgumentException
85 * if the type {@link IType#value()} is less than
86 * {@link Type#BASE_USER_START}
87 */
88 public synchronized void registerBuilder(IType type,
89 IContainerFactory.IContainerBuilder builder)
90 {
91 if (type.value() < Type.BASE_USER_START)
92 {
93 throw new IllegalArgumentException("Cannot register types below "
94 + Type.BASE_USER_START);
95 }
96 final IContainerFactory.IContainerBuilder previous =
97 builders.put(type, builder);
98 if (previous != null)
99 {
100 LOG.warn("Previous builder " + previous
101 + " has been overwritten with a new builder " + builder
102 + " for type " + type);
103 }
104 final IContainerDefinitionField definition =
105 builder.createContainerDefinition();
106 if (definition != null)
107 {
108 if (!definition.isDynamic())
109 {
110 staticDefinitions.put(type, definition);
111 }
112 }
113 }
114
115 /**
116 * Does the factory contain an {@link IContainerFactory.IContainerBuilder}
117 * (and by association, an {@link IContainerDefinitionField}) registered
118 * against the {@link IContainer} type argument.
119 *
120 * @param type
121 * the type of the {@link IContainer} the builder creates
122 * @return <code>true</code> if there is a
123 * {@link IContainerFactory.IContainerBuilder} registered against
124 * the type
125 * @see IContainer#getType()
126 */
127 public synchronized boolean containsType(IType type)
128 {
129 return builders.containsKey(type);
130 }
131
132 /**
133 * Get the {@link IContainerDefinitionField} registered against the
134 * {@link IContainer} type argument. This should not be called for dynamic
135 * container types.
136 *
137 * @param type
138 * the type of the {@link IContainer} the definition applies to
139 * @return the {@link IContainerDefinitionField} for the type of the
140 * {@link IContainer}
141 * @throws IllegalArgumentException
142 * if the {@link IContainer} is a dynamic type (there will be no
143 * {@link IContainerDefinitionField} found)
144 * @see #containsType(IType)
145 * @see IContainer#getType()
146 */
147 public synchronized IContainerDefinitionField getDefinition(IType type)
148 {
149 if (!containsType(type))
150 {
151 throw new IllegalArgumentException("No definition registered for "
152 + type + ", builders are=" + builders
153 + ", is this a dynamic type?"
154 + " (dynamic types do not register their definition)");
155 }
156 return staticDefinitions.get(type);
157 }
158
159 /**
160 * Create an {@link IContainer} implementation from the type argument. If
161 * there is no application {@link IContainerFactory.IContainerBuilder}
162 * registered for the type, a default builder is used that creates an
163 * {@link Record}.
164 *
165 * @param identity
166 * the identity for the container to create
167 * @param type
168 * the type of the {@link IContainer} implementation to create
169 * @param hostContext
170 * the context the container will be associated with
171 *
172 * @return an {@link IContainer} implementation
173 * @see #containsType(IType)
174 * @see IContainer#getType()
175 */
176 @SuppressWarnings("unchecked")
177 public synchronized <T extends IContainer> T createContainer(
178 String nativeContextIdentity, String identity, IType type,
179 IDomain domain, IFrameworkContext hostContext, boolean local)
180 {
181 /*
182 * This is not pretty - we need to masquerade as being in a FrameReader
183 * context to ensure we can bypass the usual checks that prevent remote
184 * containers being mutated - its the action of adding a container
185 * definition that requires this.
186 */
187 boolean alreadyInContext = FrameReader.inContext();
188 try
189 {
190 if (!local && !alreadyInContext)
191 {
192 FrameReader.inContext.set(Boolean.TRUE);
193 }
194 IContainer container;
195 if (containsType(type))
196 {
197 container =
198 builders.get(type).createContainer(nativeContextIdentity,
199 identity, type, domain, hostContext, local);
200 }
201 else
202 {
203 container =
204 defaultBuilder.createContainer(nativeContextIdentity,
205 identity, type, domain, hostContext, local);
206 }
207 container.start();
208 return (T) container;
209 }
210 finally
211 {
212 if (!local && !alreadyInContext)
213 {
214 FrameReader.inContext.remove();
215 }
216 }
217 }
218
219 /**
220 * Builder that creates {@link Record} instances
221 *
222 * @author Ramon Servadei
223 */
224 private static final class RecordBuilder implements
225 IContainerFactory.IContainerBuilder
226 {
227 public IContainer createContainer(String nativeContextIdentity,
228 String identity, IType type, IDomain domain,
229 IFrameworkContext hostContext, boolean local)
230 {
231 return new Record(nativeContextIdentity, identity, type, domain,
232 hostContext, local);
233 }
234
235 public IContainerDefinitionField createContainerDefinition()
236 {
237 return null;
238 }
239
240 public String toString()
241 {
242 return "Builder<Record>";
243 }
244 }
245
246 public void destroy()
247 {
248 this.builders.clear();
249 this.staticDefinitions.clear();
250 }
251 }