View Javadoc
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