1 package org.apache.turbine.services.intake;
2
3 /* ====================================================================
4 * The Apache Software License, Version 1.1
5 *
6 * Copyright (c) 2001 The Apache Software Foundation. All rights
7 * reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. The end-user documentation included with the redistribution,
22 * if any, must include the following acknowledgment:
23 * "This product includes software developed by the
24 * Apache Software Foundation (http://www.apache.org/)."
25 * Alternately, this acknowledgment may appear in the software itself,
26 * if and wherever such third-party acknowledgments normally appear.
27 *
28 * 4. The names "Apache" and "Apache Software Foundation" and
29 * "Apache Turbine" must not be used to endorse or promote products
30 * derived from this software without prior written permission. For
31 * written permission, please contact apache@apache.org.
32 *
33 * 5. Products derived from this software may not be called "Apache",
34 * "Apache Turbine", nor may "Apache" appear in their name, without
35 * prior written permission of the Apache Software Foundation.
36 *
37 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48 * SUCH DAMAGE.
49 * ====================================================================
50 *
51 * This software consists of voluntary contributions made by many
52 * individuals on behalf of the Apache Software Foundation. For more
53 * information on the Apache Software Foundation, please see
54 * <http://www.apache.org/>.
55 */
56 import java.beans.PropertyDescriptor;
57 import java.io.File;
58 import java.io.FileInputStream;
59 import java.io.FileOutputStream;
60 import java.io.InputStream;
61 import java.io.ObjectInputStream;
62 import java.io.ObjectOutputStream;
63 import java.io.OutputStream;
64 import java.lang.reflect.Method;
65 import java.util.HashMap;
66 import java.util.Iterator;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Properties;
70 import javax.servlet.ServletConfig;
71 import org.apache.turbine.om.OMTool;
72 import org.apache.turbine.services.InitializationException;
73 import org.apache.turbine.services.TurbineBaseService;
74 import org.apache.turbine.services.intake.model.Group;
75 import org.apache.turbine.services.intake.transform.XmlToAppData;
76 import org.apache.turbine.services.intake.xmlmodel.AppData;
77 import org.apache.turbine.services.intake.xmlmodel.XmlGroup;
78 import org.apache.turbine.util.Log;
79 import org.apache.turbine.util.ServletUtils;
80 import org.apache.turbine.util.TurbineException;
81 import org.apache.turbine.util.pool.BoundedBuffer;
82 import org.apache.turbine.util.pool.Recyclable;
83
84 /***
85 * This service provides access to input processing objects based
86 * on an XML specification.
87 *
88 * @author <a href="mailto:jmcnally@collab.net">John McNally</a>
89 * @version $Id: TurbineIntakeService.java,v 1.5 2002/07/11 16:53:28 mpoeschl Exp $
90 */
91 public class TurbineIntakeService
92 extends TurbineBaseService
93 implements IntakeService
94 {
95 /*** Array of group names. */
96 private String[] groupNames;
97
98 /*** The cache of group names. */
99 private Map groupNameMap;
100
101 /*** The cache of group keys. */
102 private Map groupKeyMap;
103
104 /*** The cache of property getters. */
105 private Map getterMap;
106
107 /*** The cache of property setters. */
108 private Map setterMap;
109
110 /*** Keep a OMTool to be able to retrieve objects */
111 private OMTool omTool;
112
113 /*** The top element of the object tree */
114 private AppData appData;
115
116 /***
117 * The pool repository, one pool for each class.
118 */
119 private HashMap poolRepository = new HashMap();
120
121 // a couple integers for a switch statement
122 private static final int GETTER = 0;
123 private static final int SETTER = 1;
124
125 /***
126 * Constructor.
127 */
128 public TurbineIntakeService()
129 {
130 }
131
132 /***
133 * Called the first time the Service is used.
134 *
135 * @param config A ServletConfig.
136 */
137 public void init(ServletConfig config)
138 throws InitializationException
139 {
140 Properties props = getProperties();
141 String xmlPath = props.getProperty(XML_PATH);
142 if ( xmlPath == null )
143 {
144 String pathError =
145 "Path to intake.xml was not specified. Check that the" +
146 " property exists in TR.props and was loaded.";
147 Log.error(pathError);
148 throw new InitializationException(pathError);
149 }
150 //!! need a constant
151 String appDataPath = "/WEB-INF/appData.ser";
152 try
153 {
154 // If possible, transform paths to be webapp root relative.
155 xmlPath = ServletUtils.expandRelative(config, xmlPath);
156 appDataPath = ServletUtils.expandRelative(config, appDataPath);
157 File serialAppData = new File(appDataPath);
158 File xmlFile = new File(xmlPath);
159 if ( serialAppData.exists()
160 && serialAppData.lastModified() > xmlFile.lastModified() )
161 {
162 InputStream in = null;
163 try
164 {
165 in = new FileInputStream(serialAppData);
166 ObjectInputStream p = new ObjectInputStream(in);
167 appData = (AppData)p.readObject();
168 }
169 catch (Exception e)
170 {
171 // We got a corrupt file for some reason
172 writeAppData(xmlPath, appDataPath, serialAppData);
173 }
174 finally
175 {
176 if (in != null)
177 {
178 in.close();
179 }
180 }
181 }
182 else
183 {
184 writeAppData(xmlPath, appDataPath, serialAppData);
185 }
186
187 groupNames = new String[appData.getGroups().size()];
188 groupKeyMap = new HashMap();
189 groupNameMap = new HashMap();
190 getterMap = new HashMap();
191 setterMap = new HashMap();
192 // omTool = new OMTool();
193 String pkg = appData.getBasePackage();
194
195 List glist = appData.getGroups();
196 for ( int i=glist.size()-1; i>=0; i-- )
197 {
198 XmlGroup g = (XmlGroup)glist.get(i);
199 String groupName = g.getName();
200 groupNames[i] = groupName;
201 groupKeyMap.put(groupName, g.getKey());
202 groupNameMap.put(g.getKey(), groupName);
203
204 List classNames = g.getMapToObjects();
205 Iterator iter2 = classNames.iterator();
206 while (iter2.hasNext())
207 {
208 String className = (String)iter2.next();
209 if ( !getterMap.containsKey(className) )
210 {
211 getterMap.put(className, new HashMap());
212 setterMap.put(className, new HashMap());
213 }
214 }
215 }
216
217 setInit(true);
218 }
219 catch (Exception e)
220 {
221 throw new InitializationException(
222 "TurbineIntakeService failed to initialize", e);
223 }
224 }
225
226
227 /***
228 * This method writes the appData file into Objects and stores
229 * the information into this classes appData property
230 */
231 private void writeAppData(String xmlPath, String appDataPath, File serialAppData)
232 throws Exception
233 {
234 XmlToAppData xmlApp = new XmlToAppData();
235 appData = xmlApp.parseFile(xmlPath);
236 OutputStream out = null;
237 InputStream in = null;
238 try
239 {
240 // write the appData file out
241 out = new FileOutputStream(serialAppData);
242 ObjectOutputStream p = new ObjectOutputStream(out);
243 p.writeObject(appData);
244 p.flush();
245
246 // read the file back in. for some reason on OSX 10.1
247 // this is necessary.
248 in = new FileInputStream(serialAppData);
249 ObjectInputStream pin = new ObjectInputStream(in);
250 appData = (AppData)pin.readObject();
251 }
252 catch (Exception e)
253 {
254 Log.info(
255 "Intake initialization could not be serialized " +
256 "because writing to " + appDataPath + " was not " +
257 "allowed. This will require that the xml file be " +
258 "parsed when restarting the application.");
259 }
260 finally
261 {
262 if (out != null)
263 {
264 out.close();
265 }
266 if (in != null)
267 {
268 in.close();
269 }
270 }
271 }
272
273 /***
274 * An inner class for group specific pools.
275 */
276 private class PoolBuffer
277 {
278 /***
279 * A buffer for class instances.
280 */
281 private BoundedBuffer pool;
282
283 /***
284 * A cache for recycling methods.
285 */
286 private HashMap recyclers;
287
288 /***
289 * Contructs a new pool buffer with a specific capacity.
290 *
291 * @param capacity a capacity.
292 */
293 public PoolBuffer(int capacity)
294 {
295 pool = new BoundedBuffer(capacity);
296 }
297
298 /***
299 * Polls for an instance from the pool.
300 *
301 * @return an instance or null.
302 */
303 public Group poll()
304 throws TurbineException
305 {
306 Group instance = (Group)pool.poll();
307 if ((instance != null) &&
308 (instance instanceof Recyclable))
309 {
310 try
311 {
312 ((Recyclable) instance).recycle();
313 }
314 catch (Exception x)
315 {
316 throw new TurbineException("Recycling failed for " +
317 instance.getClass().getName(),x);
318 }
319 }
320 return instance;
321 }
322
323 /***
324 * Offers an instance to the pool.
325 *
326 * @param instance an instance.
327 */
328 public boolean offer(Group instance)
329 {
330 try
331 {
332 ((Recyclable) instance).dispose();
333 }
334 catch (Exception x)
335 {
336 return false;
337 }
338 return pool.offer(instance);
339 }
340
341 /***
342 * Returns the capacity of the pool.
343 *
344 * @return the capacity.
345 */
346 public int capacity()
347 {
348 return pool.capacity();
349 }
350
351 /***
352 * Returns the size of the pool.
353 *
354 * @return the size.
355 */
356 public int size()
357 {
358 return pool.size();
359 }
360 }
361
362 /***
363 * Gets an instance of a named group either from the pool
364 * or by calling the Factory Service if the pool is empty.
365 *
366 * @param groupName the name of the group.
367 * @return a Group instance.
368 * @throws TurbineException if recycling fails.
369 */
370 public Group getGroup(String groupName)
371 throws TurbineException
372 {
373 Group instance = (Group)pollInstance(groupName);
374 if ( instance == null )
375 {
376 try
377 {
378 instance = new Group(appData.getGroup(groupName));
379 }
380 catch (Exception e)
381 {
382 throw new TurbineException(e);
383 }
384 }
385 return instance;
386 }
387
388
389 /***
390 * Puts a Group back to the pool.
391 *
392 * @param instance the object instance to recycle.
393 * @return true if the instance was accepted.
394 */
395 public boolean releaseGroup(Group instance)
396 {
397 if (instance != null)
398 {
399 HashMap repository = poolRepository;
400 String name = instance.getIntakeGroupName();
401 PoolBuffer pool = (PoolBuffer) repository.get(name);
402 if (pool == null)
403 {
404 pool = new PoolBuffer(instance.getPoolCapacity());
405 repository = (HashMap) repository.clone();
406 repository.put(name,pool);
407 poolRepository = repository;
408 }
409 return pool.offer(instance);
410 }
411 else
412 {
413 return false;
414 }
415 }
416
417 /***
418 * Gets the capacity of the pool for a named group.
419 *
420 * @param name the name of the class.
421 */
422 public int getCapacity(String name)
423 {
424 int capacity = DEFAULT_POOL_CAPACITY;
425 PoolBuffer pool = (PoolBuffer) poolRepository.get(name);
426 if ( pool == null )
427 {
428 try
429 {
430 capacity = Integer
431 .parseInt(appData.getGroup(name).getPoolCapacity());
432 }
433 catch (NumberFormatException nfe) {}
434 }
435 else
436 {
437 capacity = pool.capacity();
438 }
439
440 return capacity;
441 }
442
443 /***
444 * Sets the capacity of the pool for a group.
445 * Note that the pool will be cleared after the change.
446 *
447 * @param name the name of the group.
448 * @param capacity the new capacity.
449 */
450 public void setCapacity(String name,
451 int capacity)
452 {
453 HashMap repository = poolRepository;
454 repository = repository != null ?
455 (HashMap) repository.clone() : new HashMap();
456 repository.put(name,new PoolBuffer(capacity));
457 poolRepository = repository;
458 }
459
460 /***
461 * Gets the current size of the pool for a group.
462 *
463 * @param name the name of the group.
464 */
465 public int getSize(String name)
466 {
467 PoolBuffer pool = (PoolBuffer) poolRepository.get(name);
468 return pool != null ? pool.size() : 0;
469 }
470
471 /***
472 * Clears instances of a group from the pool.
473 *
474 * @param name the name of the group.
475 */
476 public void clearPool(String name)
477 {
478 throw new Error("Not implemented");
479 /* FIXME!! We need to worry about objects that are checked out
480
481 HashMap repository = poolRepository;
482 if (repository.get(name) != null)
483 {
484 repository = (HashMap) repository.clone();
485 repository.remove(name);
486 poolRepository = repository;
487 }
488 */
489 }
490
491 /***
492 * Clears all instances from the pool.
493 */
494 public void clearPool()
495 {
496 throw new Error("Not implemented");
497 /* FIXME!! We need to worry about objects that are checked out
498 poolRepository = new HashMap();
499 */
500 }
501
502 /***
503 * Polls and recycles an object of the named group from the pool.
504 *
505 * @param groupName the name of the group.
506 * @return the object or null.
507 * @throws TurbineException if recycling fails.
508 */
509 private Object pollInstance(String groupName)
510 throws TurbineException
511 {
512 PoolBuffer pool = (PoolBuffer) poolRepository.get(groupName);
513 return pool != null ? pool.poll() : null;
514 }
515
516 /***
517 * Names of all the defined groups.
518 *
519 * @return array of names.
520 */
521 public String[] getGroupNames()
522 {
523 return groupNames;
524 }
525
526 /***
527 * Gets the key (usually a short identifier) for a group.
528 *
529 * @param groupName the name of the group.
530 * @return the the key.
531 */
532 public String getGroupKey(String groupName)
533 {
534 return (String)groupKeyMap.get(groupName);
535 }
536
537 /***
538 * Gets the group name given its key.
539 *
540 * @param the the key.
541 * @return groupName the name of the group.
542 */
543 public String getGroupName(String groupKey)
544 {
545 return (String)groupNameMap.get(groupKey);
546 }
547
548 /***
549 * Gets the Method that can be used to set a property.
550 *
551 * @param className the name of the object.
552 * @param propName the name of the property.
553 * @return the setter.
554 */
555 public Method getFieldSetter(String className, String propName)
556 {
557 Map settersForClassName = (Map)setterMap.get(className);
558 Method setter = (Method)settersForClassName.get(propName);
559
560 if ( setter == null )
561 {
562 PropertyDescriptor pd = null;
563 synchronized(setterMap)
564 {
565 try
566 {
567 pd = new PropertyDescriptor(propName,
568 Class.forName(className));
569 setter = pd.getWriteMethod();
570 ((Map)setterMap.get(className)).put(propName, setter);
571 if ( setter == null )
572 {
573 Log.error("Intake: setter for '" + propName
574 + "' in class '" + className
575 + "' could not be found.");
576 }
577 }
578 catch (Exception e)
579 {
580 Log.error(e);
581 }
582 }
583 // we have already completed the reflection on the getter, so
584 // save it so we do not have to repeat
585 synchronized(getterMap)
586 {
587 try
588 {
589 Method getter = pd.getReadMethod();
590 ((Map)getterMap.get(className)).put(propName, getter);
591 }
592 catch (Exception e)
593 {
594 Log.error(e);
595 }
596 }
597 }
598 return setter;
599 }
600
601 /***
602 * Gets the Method that can be used to get a property value.
603 *
604 * @param className the name of the object.
605 * @param propName the name of the property.
606 * @return the getter.
607 */
608 public Method getFieldGetter(String className, String propName)
609 {
610 Map gettersForClassName = (Map)getterMap.get(className);
611 Method getter = (Method)gettersForClassName.get(propName);
612
613 if ( getter == null )
614 {
615 PropertyDescriptor pd = null;
616 synchronized(getterMap)
617 {
618 try
619 {
620 pd = new PropertyDescriptor(propName,
621 Class.forName(className));
622 getter = pd.getReadMethod();
623 ((Map)getterMap.get(className)).put(propName, getter);
624 if ( getter == null )
625 {
626 Log.error("Intake: getter for '" + propName
627 + "' in class '" + className
628 + "' could not be found.");
629 }
630 }
631 catch (Exception e)
632 {
633 Log.error(e);
634 }
635 }
636 // we have already completed the reflection on the setter, so
637 // save it so we do not have to repeat
638 synchronized(setterMap)
639 {
640 try
641 {
642 Method setter = pd.getWriteMethod();
643 ((Map)setterMap.get(className)).put(propName, setter);
644 }
645 catch (Exception e)
646 {
647 Log.error(e);
648 }
649 }
650 }
651 return getter;
652 }
653 }
This page was automatically generated by Maven