001package org.apache.turbine.services.intake; 002 003 004/* 005 * Licensed to the Apache Software Foundation (ASF) under one 006 * or more contributor license agreements. See the NOTICE file 007 * distributed with this work for additional information 008 * regarding copyright ownership. The ASF licenses this file 009 * to you under the Apache License, Version 2.0 (the 010 * "License"); you may not use this file except in compliance 011 * with the License. You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, 016 * software distributed under the License is distributed on an 017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 018 * KIND, either express or implied. See the License for the 019 * specific language governing permissions and limitations 020 * under the License. 021 */ 022 023 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030import org.apache.fulcrum.intake.IntakeException; 031import org.apache.fulcrum.intake.IntakeServiceFacade; 032import org.apache.fulcrum.intake.Retrievable; 033import org.apache.fulcrum.intake.model.Group; 034import org.apache.fulcrum.parser.ValueParser; 035import org.apache.fulcrum.pool.Recyclable; 036import org.apache.turbine.services.pull.ApplicationTool; 037import org.apache.turbine.util.RunData; 038 039 040/** 041 * The main class through which Intake is accessed. Provides easy access 042 * to the Fulcrum Intake component. 043 * 044 * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a> 045 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a> 046 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a> 047 * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a> 048 * @version $Id: IntakeTool.java 1706239 2015-10-01 13:18:35Z tv $ 049 */ 050public class IntakeTool 051 implements ApplicationTool, Recyclable 052{ 053 /** Used for logging */ 054 protected static final Log log = LogFactory.getLog(IntakeTool.class); 055 056 /** Constant for default key */ 057 public static final String DEFAULT_KEY = "_0"; 058 059 /** Constant for the hidden fieldname */ 060 public static final String INTAKE_GRP = "intake-grp"; 061 062 /** Groups from intake.xml */ 063 protected HashMap<String, Group> groups; 064 065 /** ValueParser instance */ 066 protected ValueParser pp; 067 068 private final HashMap<String, Group> declaredGroups = new HashMap<String, Group>(); 069 private final StringBuilder allGroupsSB = new StringBuilder(256); 070 private final StringBuilder groupSB = new StringBuilder(128); 071 072 /** The cache of PullHelpers. **/ 073 private final Map<String, IntakeTool.PullHelper> pullMap; 074 075 /** 076 * Constructor 077 */ 078 public IntakeTool() 079 { 080 String[] groupNames = IntakeServiceFacade.getGroupNames(); 081 int groupCount = 0; 082 if (groupNames != null) 083 { 084 groupCount = groupNames.length; 085 } 086 groups = new HashMap<String, Group>((int) (1.25 * groupCount + 1)); 087 pullMap = new HashMap<String, IntakeTool.PullHelper>((int) (1.25 * groupCount + 1)); 088 089 for (int i = groupCount - 1; i >= 0; i--) 090 { 091 pullMap.put(groupNames[i], new PullHelper(groupNames[i])); 092 } 093 } 094 095 /** 096 * Prepares intake for a single request 097 */ 098 @Override 099 public void init(Object runData) 100 { 101 this.pp = ((RunData) runData).getParameters(); 102 103 String[] groupKeys = pp.getStrings(INTAKE_GRP); 104 String[] groupNames = null; 105 if (groupKeys == null || groupKeys.length == 0) 106 { 107 groupNames = IntakeServiceFacade.getGroupNames(); 108 } 109 else 110 { 111 groupNames = new String[groupKeys.length]; 112 for (int i = groupKeys.length - 1; i >= 0; i--) 113 { 114 groupNames[i] = IntakeServiceFacade.getGroupName(groupKeys[i]); 115 } 116 117 } 118 119 for (int i = groupNames.length - 1; i >= 0; i--) 120 { 121 try 122 { 123 List<Group> foundGroups = IntakeServiceFacade.getGroup(groupNames[i]) 124 .getObjects(pp); 125 126 if (foundGroups != null) 127 { 128 for (Group group : foundGroups) 129 { 130 groups.put(group.getObjectKey(), group); 131 } 132 } 133 } 134 catch (IntakeException e) 135 { 136 log.error(e); 137 } 138 } 139 } 140 141 /** 142 * Add all registered group ids to the value parser 143 * 144 * @param vp the value parser 145 */ 146 public void addGroupsToParameters(ValueParser vp) 147 { 148 for (Group group : groups.values()) 149 { 150 if (!declaredGroups.containsKey(group.getIntakeGroupName())) 151 { 152 declaredGroups.put(group.getIntakeGroupName(), null); 153 vp.add("intake-grp", group.getGID()); 154 } 155 vp.add(group.getGID(), group.getOID()); 156 } 157 declaredGroups.clear(); 158 } 159 160 /** 161 * A convenience method to write out the hidden form fields 162 * that notify intake of the relevant groups. It should be used 163 * only in templates with 1 form. In multiform templates, the groups 164 * that are relevant for each form need to be declared using 165 * $intake.newForm() and $intake.declareGroup($group) for the relevant 166 * groups in the form. 167 * 168 * @return the HTML that declares all groups to Intake in hidden input fields 169 * 170 */ 171 public String declareGroups() 172 { 173 allGroupsSB.setLength(0); 174 for (Group group : groups.values()) 175 { 176 declareGroup(group, allGroupsSB); 177 } 178 return allGroupsSB.toString(); 179 } 180 181 /** 182 * A convenience method to write out the hidden form fields 183 * that notify intake of the group. 184 * 185 * @param group the group to declare 186 * @return the HTML that declares the group to Intake in a hidden input field 187 */ 188 public String declareGroup(Group group) 189 { 190 groupSB.setLength(0); 191 declareGroup(group, groupSB); 192 return groupSB.toString(); 193 } 194 195 /** 196 * xhtml valid hidden input field(s) that notifies intake of the 197 * group's presence. 198 * @param group the group to declare 199 * @param sb a String Builder where the hidden field HTML will be appended 200 */ 201 public void declareGroup(Group group, StringBuilder sb) 202 { 203 if (!declaredGroups.containsKey(group.getIntakeGroupName())) 204 { 205 declaredGroups.put(group.getIntakeGroupName(), null); 206 sb.append("<input type=\"hidden\" name=\"") 207 .append(INTAKE_GRP) 208 .append("\" value=\"") 209 .append(group.getGID()) 210 .append("\"/>\n"); 211 } 212 group.appendHtmlFormInput(sb); 213 } 214 215 /** 216 * Declare that a new form starts 217 */ 218 public void newForm() 219 { 220 declaredGroups.clear(); 221 for (Group group : groups.values()) 222 { 223 group.resetDeclared(); 224 } 225 } 226 227 /** 228 * Implementation of ApplicationTool interface is not needed for this 229 * tool as it is request scoped 230 */ 231 @Override 232 public void refresh() 233 { 234 // empty 235 } 236 237 /** 238 * Inner class to present a nice interface to the template designer 239 */ 240 public class PullHelper 241 { 242 /** Name of the group used by the pull helper */ 243 String groupName; 244 245 /** 246 * Protected constructor to force use of factory method. 247 * 248 * @param groupName 249 */ 250 protected PullHelper(String groupName) 251 { 252 this.groupName = groupName; 253 } 254 255 /** 256 * Populates the object with the default values from the XML File 257 * 258 * @return a Group object with the default values 259 * @throws IntakeException 260 */ 261 public Group getDefault() 262 throws IntakeException 263 { 264 return setKey(DEFAULT_KEY); 265 } 266 267 /** 268 * Calls setKey(key,true) 269 * 270 * @param key 271 * @return an Intake Group 272 * @throws IntakeException 273 */ 274 public Group setKey(String key) 275 throws IntakeException 276 { 277 return setKey(key, true); 278 } 279 280 /** 281 * 282 * @param key 283 * @param create 284 * @return an Intake Group 285 * @throws IntakeException 286 */ 287 public Group setKey(String key, boolean create) 288 throws IntakeException 289 { 290 Group g = null; 291 292 String inputKey = IntakeServiceFacade.getGroupKey(groupName) + key; 293 if (groups.containsKey(inputKey)) 294 { 295 g = groups.get(inputKey); 296 } 297 else if (create) 298 { 299 g = IntakeServiceFacade.getGroup(groupName); 300 groups.put(inputKey, g); 301 g.init(key, pp); 302 } 303 304 return g; 305 } 306 307 /** 308 * maps an Intake Group to the values from a Retrievable object. 309 * 310 * @param obj A retrievable object 311 * @return an Intake Group 312 */ 313 public Group mapTo(Retrievable obj) 314 { 315 Group g = null; 316 317 try 318 { 319 String inputKey = IntakeServiceFacade.getGroupKey(groupName) 320 + obj.getQueryKey(); 321 if (groups.containsKey(inputKey)) 322 { 323 g = groups.get(inputKey); 324 } 325 else 326 { 327 g = IntakeServiceFacade.getGroup(groupName); 328 groups.put(inputKey, g); 329 } 330 331 return g.init(obj); 332 } 333 catch (IntakeException e) 334 { 335 log.error(e); 336 } 337 338 return null; 339 } 340 } 341 342 /** 343 * get a specific group 344 * @param groupName the name of the group 345 * @return a {@link PullHelper} wrapper around the group 346 */ 347 public PullHelper get(String groupName) 348 { 349 return pullMap.get(groupName); 350 } 351 352 /** 353 * Get a specific group 354 * 355 * @param groupName the name of the group 356 * @param throwExceptions if false, exceptions will be suppressed. 357 * @return a {@link PullHelper} wrapper around the group 358 * @throws IntakeException could not retrieve group 359 */ 360 public PullHelper get(String groupName, boolean throwExceptions) 361 throws IntakeException 362 { 363 return pullMap.get(groupName); 364 } 365 366 /** 367 * Loops through all of the Groups and checks to see if 368 * the data within the Group is valid. 369 * @return true if all groups are valid 370 */ 371 public boolean isAllValid() 372 { 373 boolean allValid = true; 374 for (Group group : groups.values()) 375 { 376 allValid &= group.isAllValid(); 377 } 378 return allValid; 379 } 380 381 /** 382 * Get a specific group by name and key. 383 * @param groupName the name of the group 384 * @param key the key for the group 385 * @return the {@link Group} 386 * @throws IntakeException if the group could not be retrieved 387 */ 388 public Group get(String groupName, String key) 389 throws IntakeException 390 { 391 return get(groupName, key, true); 392 } 393 394 /** 395 * Get a specific group by name and key. Also specify 396 * whether or not you want to create a new group. 397 * @param groupName the name of the group 398 * @param key the key for the group 399 * @param create true if a new group should be created 400 * @return the {@link Group} 401 * @throws IntakeException if the group could not be retrieved 402 */ 403 public Group get(String groupName, String key, boolean create) 404 throws IntakeException 405 { 406 if (groupName == null) 407 { 408 throw new IntakeException("IntakeServiceFacade.get: groupName == null"); 409 } 410 if (key == null) 411 { 412 throw new IntakeException("IntakeServiceFacade.get: key == null"); 413 } 414 415 PullHelper ph = get(groupName); 416 return (ph == null) ? null : ph.setKey(key, create); 417 } 418 419 /** 420 * Removes group. Primary use is to remove a group that has 421 * been processed by an action and is no longer appropriate 422 * in the view (screen). 423 * @param group the group instance to remove 424 */ 425 public void remove(Group group) 426 { 427 if (group != null) 428 { 429 groups.remove(group.getObjectKey()); 430 group.removeFromRequest(); 431 432 String[] groupKeys = pp.getStrings(INTAKE_GRP); 433 434 pp.remove(INTAKE_GRP); 435 436 if (groupKeys != null) 437 { 438 for (int i = 0; i < groupKeys.length; i++) 439 { 440 if (!groupKeys[i].equals(group.getGID())) 441 { 442 pp.add(INTAKE_GRP, groupKeys[i]); 443 } 444 } 445 } 446 447 try 448 { 449 IntakeServiceFacade.releaseGroup(group); 450 } 451 catch (IntakeException ie) 452 { 453 log.error("Tried to release unknown group " 454 + group.getIntakeGroupName()); 455 } 456 } 457 } 458 459 /** 460 * Removes all groups. Primary use is to remove groups that have 461 * been processed by an action and are no longer appropriate 462 * in the view (screen). 463 */ 464 public void removeAll() 465 { 466 Object[] allGroups = groups.values().toArray(); 467 for (int i = allGroups.length - 1; i >= 0; i--) 468 { 469 Group group = (Group) allGroups[i]; 470 remove(group); 471 } 472 } 473 474 /** 475 * Get a Map containing all the groups. 476 * 477 * @return the Group Map 478 */ 479 public Map<String, Group> getGroups() 480 { 481 return groups; 482 } 483 484 // ****************** Recyclable implementation ************************ 485 486 private boolean disposed; 487 488 /** 489 * Recycles the object for a new client. Recycle methods with 490 * parameters must be added to implementing object and they will be 491 * automatically called by pool implementations when the object is 492 * taken from the pool for a new client. The parameters must 493 * correspond to the parameters of the constructors of the object. 494 * For new objects, constructors can call their corresponding recycle 495 * methods whenever applicable. 496 * The recycle methods must call their super. 497 */ 498 @Override 499 public void recycle() 500 { 501 disposed = false; 502 } 503 504 /** 505 * Disposes the object after use. The method is called 506 * when the object is returned to its pool. 507 * The dispose method must call its super. 508 */ 509 @Override 510 public void dispose() 511 { 512 for (Group group : groups.values()) 513 { 514 try 515 { 516 IntakeServiceFacade.releaseGroup(group); 517 } 518 catch (IntakeException ie) 519 { 520 log.error("Tried to release unknown group " 521 + group.getIntakeGroupName()); 522 } 523 } 524 525 groups.clear(); 526 declaredGroups.clear(); 527 pp = null; 528 529 disposed = true; 530 } 531 532 /** 533 * Checks whether the recyclable has been disposed. 534 * 535 * @return true, if the recyclable is disposed. 536 */ 537 @Override 538 public boolean isDisposed() 539 { 540 return disposed; 541 } 542}