1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 package com.ivata.mask.web.demo.catalog;
85 import java.beans.PropertyDescriptor;
86 import java.io.Serializable;
87 import java.util.HashMap;
88 import java.util.Iterator;
89 import java.util.List;
90 import java.util.Map;
91 import java.util.Set;
92 import java.util.Vector;
93
94 import org.apache.commons.beanutils.PropertyUtils;
95 import org.apache.log4j.Logger;
96
97 import com.ivata.mask.DefaultMaskFactory;
98 import com.ivata.mask.MaskFactory;
99 import com.ivata.mask.field.DefaultFieldValueConvertorFactory;
100 import com.ivata.mask.persistence.PersistenceException;
101 import com.ivata.mask.persistence.PersistenceManager;
102 import com.ivata.mask.persistence.PersistenceSession;
103 import com.ivata.mask.persistence.right.DefaultPersistenceRights;
104 import com.ivata.mask.persistence.right.PersistenceRights;
105 import com.ivata.mask.valueobject.ValueObject;
106 import com.ivata.mask.web.demo.customer.CustomerDO;
107 import com.ivata.mask.web.demo.order.OrderDO;
108 import com.ivata.mask.web.demo.order.item.OrderItemDO;
109 import com.ivata.mask.web.demo.product.ProductDO;
110 import com.ivata.mask.web.demo.valueobject.DemoValueObject;
111 import com.ivata.mask.web.struts.ValueObjectException;
112 /***
113 * <p>
114 * This is the main class of the demo. It represents a catalog of products.
115 * </p>
116 *
117 * @author Colin MacLeod
118 * <a href='mailto:colin.macleod@ivata.com'>colin.macleod@ivata.com</a>
119 * @since ivata masks 0.1 (2004-05-09)
120 * @version $Revision: 1.13 $
121 */
122 public final class Catalog implements PersistenceManager {
123 /***
124 * Unique id for invalid objects.
125 */
126 public static final int INVALID_ID = -1;
127 /***
128 * Refer to {@link Logger}.
129 */
130 private static Logger log = Logger.getLogger(Catalog.class);
131 /***
132 * <p>
133 * For the demo, this is a singleton. It's better to use dependency
134 * injection or service locator patterns, in real life.
135 * </p>
136 */
137 private static Catalog theInstance = null;
138 /***
139 * <p>
140 * Get the sole instance of this class.
141 * </p>
142 *
143 * <p>
144 * For the demo, this is a singleton. It's better to use dependency
145 * injection or service locator patterns, in real life.
146 * </p>
147 *
148 * @return only instance of this class.
149 */
150 public static synchronized Catalog getInstance() {
151 if (theInstance == null) {
152 theInstance = new Catalog();
153 }
154 return theInstance;
155 }
156 /***
157 * <p>
158 * Map of integer instances - counters for the ids of each class. Referenced
159 * by class name.
160 * </p>
161 */
162 private Map counters = new HashMap();
163 /***
164 * <p>
165 * Stores all the instances, referenced by class, then id string.
166 * </p>
167 */
168 private Map instances = new HashMap();
169 /***
170 * <p>
171 * Stores the mask factory used throughout this application.
172 * </p>
173 * <p>
174 * <b>Note </b> we have overridden the name of the list mask used by default
175 * to just "list" - you could do the same for the default input
176 * mask. (This dictates the name of the <strong>Struts </strong> action
177 * class used).
178 * </p>
179 */
180 private MaskFactory maskFactory = new DefaultMaskFactory("inputMask",
181 "list",
182 new DefaultFieldValueConvertorFactory());
183 /***
184 * <p>
185 * Private default constructor enforces singleton behavior.
186 * </p>
187 */
188 private Catalog() {
189 reset();
190 initializeDO(CustomerDO.class);
191 initializeDO(OrderDO.class);
192 initializeDO(OrderItemDO.class);
193 initializeDO(ProductDO.class);
194 }
195 /***
196 * <p>
197 * Add a new object to be persisted.
198 * </p>
199 *
200 * @param session open persistence session, used to store the object.
201 * @param valueObject
202 * value object to be persisted.
203 * @return new value object with id set appropriately.
204 * @throws PersistenceException thrown by the underlying persistence
205 * mechanism if the object cannot be saved for any reason.
206 * @see com.ivata.mask.persistence.PersistenceManager#add
207 */
208 public synchronized ValueObject add(
209 final PersistenceSession session,
210 final ValueObject valueObject) throws PersistenceException {
211 String className = valueObject.getClass().getName();
212 Integer counter = (Integer) counters.get(className);
213 Map instancesThisClass = (Map) instances.get(className);
214 if (counter == null) {
215 throw new NullPointerException(
216 "ERROR: unknown value object class '"
217 + valueObject.getClass() + "'");
218 }
219 counter = new Integer(counter.intValue() + 1);
220 counters.put(className, counter);
221 DemoValueObject demoValueObject = (DemoValueObject) valueObject;
222 demoValueObject.setId(counter.intValue());
223 instancesThisClass.put(demoValueObject.getIdString(), demoValueObject);
224 return demoValueObject;
225 }
226 /***
227 * <p>
228 * Persist changes to an existing object.
229 * </p>
230 *
231 * @param session open persistence session, used to store the changes to
232 * the object.
233 * @param valueObject value object to be persisted.
234 * @throws PersistenceException thrown by the underlying persistence
235 * mechanism if the object cannot be saved for any reason.
236 * @see com.ivata.mask.persistence.PersistenceManager#amend
237 */
238 public synchronized void amend(final PersistenceSession session,
239 final ValueObject valueObject) throws PersistenceException {
240 DemoValueObject demoValueObject = (DemoValueObject) valueObject;
241 String className = valueObject.getClass().getName();
242 Map instancesThisClass = (Map) instances.get(className);
243 if (instancesThisClass == null) {
244 throw new NullPointerException(
245 "ERROR: unknown value object class '"
246 + valueObject.getClass() + "'");
247 }
248 ValueObject existingObject = (ValueObject) instancesThisClass
249 .get(demoValueObject.getIdString());
250 setPropertyValues(demoValueObject, existingObject);
251 }
252 /***
253 * Retrieve all instances of a particular class. This method is used in
254 * the list pages.
255 *
256 * @param session open persistence session, used to fetch the objects.
257 * @param classParam class of objects to be retrieved. Must be a subclass
258 * of <code>ValueObject</code>.
259 * @return <code>List</code> containing instances of the requested class.
260 * @see com.ivata.mask.PersistenceManager#locateByBaseClass
261 */
262 public List findAll(final PersistenceSession session,
263 final Class classParam) {
264 Map instancesThisClass = getInstanceMap(classParam);
265 return new Vector(instancesThisClass.values());
266 }
267 /***
268 * Retrieve a single instance of a particular class. This method is used in
269 * the input/display pages.
270 *
271 * @param session open persistence session, used to fetch the object.
272 * @param classParam class of object to be retrieved. Must be a subclass
273 * of <code>ValueObject</code>.
274 * @param key unique identifier of the object to retrieve.
275 * @return instance of the requested class which matches the identifier
276 * <code>key</code>.
277 * @throws PersistenceException thrown by the underlying persistence
278 * mechanism if the object cannot be retrieved for any reason.
279 * @see com.ivata.mask.persistence.PersistenceManager#findByPrimaryKey
280 */
281 public ValueObject findByPrimaryKey(final PersistenceSession session,
282 final Class classParam,
283 final Serializable key) throws PersistenceException {
284 Map instancesThisClass = getInstanceMap(classParam);
285 return (ValueObject) instancesThisClass.get(key);
286 }
287 /***
288 * Class instances are stored internally within a map, indexed by the
289 * object's identifier. This private helper retrieves the map for a given
290 * class.
291 *
292 * @param classParam indicates the class of value objects which should be
293 * indexed by the map
294 * @return a map of all value objects matching the requested class.
295 */
296 private Map getInstanceMap(final Class classParam) {
297 Map instancesThisClass = null;
298 Class baseClass = classParam;
299 while (instancesThisClass == null) {
300 if ((baseClass == null)
301 || "java.lang.Object".equals(baseClass.getName())) {
302 throw new RuntimeException("ERROR in value object locator: no "
303 + "instances for class '" + classParam.getName() + "'");
304 }
305 instancesThisClass = (Map) instances.get(baseClass.getName());
306 baseClass = baseClass.getSuperclass();
307 }
308 return instancesThisClass;
309 }
310
311 /***
312 * <p>
313 * Get the mask factory used throughout this application. This is used to
314 * create and retrieve input/list masks.
315 * </p>
316 *
317 * @return mask factory used throughout this application.
318 */
319 public synchronized MaskFactory getMaskFactory() {
320 return maskFactory;
321 }
322
323 /***
324 * Just returns an instance of the default rights implementation. This
325 * lets you do everything :-)
326 *
327 * @return Instance of {@link DefaultPersistenceRights}.
328 */
329 public PersistenceRights getPersistenceRights() {
330 return new DefaultPersistenceRights();
331 }
332 /***
333 * <p>
334 * Initialize the map entries for a particular value object. All value
335 * objects are stored in maps - this creates the map for value objects of a
336 * single class.
337 * </p>
338 *
339 * @param valueObjectClass class of value object whose internal storage
340 * map should be prepared.
341 */
342 private void initializeDO(final Class valueObjectClass) {
343 String className = valueObjectClass.getName();
344 counters.put(className, new Integer(0));
345 instances.put(className, new HashMap());
346 }
347 /***
348 * <p>
349 * Open a catalog session.
350 * </p>
351 *
352 * @see com.ivata.mask.persistence.PersistenceManager#openSession()
353 */
354 public PersistenceSession openSession() throws PersistenceException {
355 return new CatalogSession();
356 }
357 /***
358 * You can't use a system session in this simple demo. This method throws
359 * an exception of class <code>UnsupportedOperationException</code>.
360 *
361 * @param systemSession
362 * @return
363 * @throws PersistenceException
364 * @see com.ivata.mask.persistence.PersistenceManager#openSession
365 */
366 public PersistenceSession openSession(final Object systemSession)
367 throws PersistenceException {
368
369 return openSession();
370 }
371 /***
372 * <p>
373 * Remove a value object from the system.
374 * </p>
375 *
376 * @param session open persistence session, used to remove the object.
377 * @param classParam class of object to be retrieved. Must be a subclass
378 * of <code>ValueObject</code>.
379 * @param key unique identifier of the object to retrieve.
380 * @throws PersistenceException thrown by the underlying persistence
381 * mechanism if the object cannot be removed for any reason.
382 * @see com.ivata.mask.persistence.PersistenceManager#remove
383 */
384 public void remove(final PersistenceSession session,
385 final Class classParam,
386 final Serializable key) throws PersistenceException {
387 assert (classParam != null);
388 assert (key != null);
389
390 Map instancesThisClass = (Map) instances.get(classParam.getName());
391 instancesThisClass.remove(key);
392
393
394
395 if ("com.ivata.mask.web.demo.customer.CustomerDO".equals(classParam
396 .getName())) {
397 Map orderInstances = (Map) instances
398 .get("com.ivata.mask.web.demo.order.OrderDO");
399 Iterator keyIterator = orderInstances.keySet().iterator();
400 while (keyIterator.hasNext()) {
401 String orderId = (String) keyIterator.next();
402 OrderDO order = (OrderDO) orderInstances.get(orderId);
403 if ((order.getCustomer() != null)
404 && key.equals(order.getCustomer().getIdString())) {
405 order.setCustomer(null);
406 }
407 }
408 } else if ("com.ivata.mask.web.demo.product.ProductDO".equals(classParam
409 .getName())) {
410 Map orderItemInstances = (Map) instances
411 .get("com.ivata.mask.web.demo.order.item.OrderItemDO");
412 Iterator keyIterator = orderItemInstances.keySet().iterator();
413 while (keyIterator.hasNext()) {
414 String orderItemId = (String) keyIterator.next();
415 OrderItemDO orderItem = (OrderItemDO) orderItemInstances
416 .get(orderItemId);
417 if ((orderItem.getProduct() != null)
418 && key.equals(orderItem.getProduct().getIdString())) {
419 orderItem.setProduct(null);
420 }
421 }
422 }
423 }
424 /***
425 * <p>
426 * Reset the catalog to its initial state.
427 * </p>
428 */
429 public void reset() {
430
431 Set keySet = instances.keySet();
432 for (Iterator keyIterator = keySet.iterator(); keyIterator.hasNext();) {
433 String className = (String) keyIterator.next();
434 Map instancesThisClass = (Map) instances.get(className);
435 instancesThisClass.clear();
436 counters.put(className, new Integer(0));
437 }
438 }
439 /***
440 * <p>
441 * Set the mask factory used throughout this application.
442 * </p>
443 *
444 * @param factory
445 * mask factory used throughout this application.
446 */
447 public void setMaskFactory(final MaskFactory factory) {
448 maskFactory = factory;
449 }
450
451 /***
452 * <p>
453 * Override to set all property values from one value object to another.
454 * </p>
455 *
456 * @param from
457 * value object to take values from.
458 * @param to
459 * value object to set values in.
460 */
461 protected void setPropertyValues(final ValueObject from,
462 final ValueObject to) {
463
464 PropertyDescriptor[] descriptors = PropertyUtils
465 .getPropertyDescriptors(to.getClass());
466 for (int i = 0; i < descriptors.length; i++) {
467 PropertyDescriptor descriptor = descriptors[i];
468 String propertyName = descriptor.getName();
469 try {
470 PropertyUtils.setProperty(to, propertyName, PropertyUtils
471 .getProperty(from, propertyName));
472 } catch (NoSuchMethodException e) {
473
474 if (!("idString".equals(propertyName) || "class"
475 .equals(propertyName))) {
476 log.warn("Warning (" + e.getClass().getName()
477 + "): no setter method for property '"
478 + propertyName + "' on value object '" + to + "'");
479 }
480 } catch (Exception e) {
481 try {
482 throw new ValueObjectException(e, to.getClass(),
483 "setting property called '" + propertyName + "'");
484 } catch (ValueObjectException e1) {
485
486 e1.printStackTrace();
487 }
488 }
489 }
490 }
491 }
492