View Javadoc

    1   package org.apache.turbine.services.intake;
    2
    3
    4   /*
    5    * Licensed to the Apache Software Foundation (ASF) under one
    6    * or more contributor license agreements.  See the NOTICE file
    7    * distributed with this work for additional information
    8    * regarding copyright ownership.  The ASF licenses this file
    9    * to you under the Apache License, Version 2.0 (the
    10   * "License"); you may not use this file except in compliance
    11   * with the License.  You may obtain a copy of the License at
    12   *
    13   *   http://www.apache.org/licenses/LICENSE-2.0
    14   *
    15   * Unless required by applicable law or agreed to in writing,
    16   * software distributed under the License is distributed on an
    17   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    18   * KIND, either express or implied.  See the License for the
    19   * specific language governing permissions and limitations
    20   * under the License.
    21   */
    22
    23
    24  import java.util.HashMap;
    25  import java.util.List;
    26  import java.util.Map;
    27
    28  import org.apache.commons.logging.Log;
    29  import org.apache.commons.logging.LogFactory;
    30  import org.apache.fulcrum.intake.IntakeException;
    31  import org.apache.fulcrum.intake.IntakeService;
    32  import org.apache.fulcrum.intake.Retrievable;
    33  import org.apache.fulcrum.intake.model.Group;
    34  import org.apache.fulcrum.parser.ValueParser;
    35  import org.apache.fulcrum.pool.Recyclable;
    36  import org.apache.turbine.annotation.TurbineService;
    37  import org.apache.turbine.services.pull.ApplicationTool;
    38  import org.apache.turbine.util.RunData;
    39
    40
    41  /**
    42   * The main class through which Intake is accessed.  Provides easy access
    43   * to the Fulcrum Intake component.
    44   *
    45   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
    46   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    47   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
    48   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    49   * @version $Id: IntakeTool.java 1773378 2016-12-09 13:19:59Z tv $
    50   */
    51  public class IntakeTool
    52          implements ApplicationTool, Recyclable
    53  {
    54      /** Used for logging */
    55      protected static final Log log = LogFactory.getLog(IntakeTool.class);
    56
    57      /** Constant for default key */
    58      public static final String DEFAULT_KEY = "_0";
    59
    60      /** Constant for the hidden fieldname */
    61      public static final String INTAKE_GRP = "intake-grp";
    62
    63      /** Groups from intake.xml */
    64      protected HashMap<String, Group> groups = null;
    65
    66      /** ValueParser instance */
    67      protected ValueParser pp;
    68
    69      private final HashMap<String, Group> declaredGroups = new HashMap<String, Group>();
    70      private final StringBuilder allGroupsSB = new StringBuilder(256);
    71      private final StringBuilder groupSB = new StringBuilder(128);
    72
    73      /** The cache of PullHelpers. **/
    74      private Map<String, IntakeTool.PullHelper> pullMap = null;
    75
    76      /**
    77       * The Intake service.
    78       */
    79      @TurbineService
    80      protected IntakeService intakeService;
    81
    82      /**
    83       * Constructor
    84       */
    85      public IntakeTool()
    86      {
    87      }
    88
    89      /**
    90       * Prepares intake for a single request
    91       */
    92      @Override
    93      public void init(Object runData)
    94      {
    95          if (groups == null) // Initialize only once
    96          {
    97              String[] groupNames = intakeService.getGroupNames();
    98              int groupCount = 0;
    99              if (groupNames != null)
    100             {
    101                 groupCount = groupNames.length;
    102             }
    103             groups = new HashMap<String, Group>((int) (1.25 * groupCount + 1));
    104             pullMap = new HashMap<String, IntakeTool.PullHelper>((int) (1.25 * groupCount + 1));
    105
    106             for (int i = groupCount - 1; i >= 0; i--)
    107             {
    108                 pullMap.put(groupNames[i], new PullHelper(groupNames[i]));
    109             }
    110         }
    111
    112         this.pp = ((RunData) runData).getParameters();
    113
    114         String[] groupKeys = pp.getStrings(INTAKE_GRP);
    115         String[] groupNames = null;
    116         if (groupKeys == null || groupKeys.length == 0)
    117         {
    118             groupNames = intakeService.getGroupNames();
    119         }
    120         else
    121         {
    122             groupNames = new String[groupKeys.length];
    123             for (int i = groupKeys.length - 1; i >= 0; i--)
    124             {
    125                 groupNames[i] = intakeService.getGroupName(groupKeys[i]);
    126             }
    127
    128         }
    129
    130         for (int i = groupNames.length - 1; i >= 0; i--)
    131         {
    132             try
    133             {
    134                 List<Group> foundGroups = intakeService.getGroup(groupNames[i])
    135                     .getObjects(pp);
    136
    137                 if (foundGroups != null)
    138                 {
    139                     for (Group group : foundGroups)
    140                     {
    141                         groups.put(group.getObjectKey(), group);
    142                     }
    143                 }
    144             }
    145             catch (IntakeException e)
    146             {
    147                 log.error(e);
    148             }
    149         }
    150     }
    151
    152     /**
    153      * Add all registered group ids to the value parser
    154      *
    155      * @param vp the value parser
    156      */
    157     public void addGroupsToParameters(ValueParser vp)
    158     {
    159         for (Group group : groups.values())
    160         {
    161             if (!declaredGroups.containsKey(group.getIntakeGroupName()))
    162             {
    163                 declaredGroups.put(group.getIntakeGroupName(), null);
    164                 vp.add("intake-grp", group.getGID());
    165             }
    166             vp.add(group.getGID(), group.getOID());
    167         }
    168         declaredGroups.clear();
    169     }
    170
    171     /**
    172      * A convenience method to write out the hidden form fields
    173      * that notify intake of the relevant groups.  It should be used
    174      * only in templates with 1 form.  In multiform templates, the groups
    175      * that are relevant for each form need to be declared using
    176      * $intake.newForm() and $intake.declareGroup($group) for the relevant
    177      * groups in the form.
    178      *
    179      * @return the HTML that declares all groups to Intake in hidden input fields
    180      *
    181      */
    182     public String declareGroups()
    183     {
    184         allGroupsSB.setLength(0);
    185         for (Group group : groups.values())
    186         {
    187             declareGroup(group, allGroupsSB);
    188         }
    189         return allGroupsSB.toString();
    190     }
    191
    192     /**
    193      * A convenience method to write out the hidden form fields
    194      * that notify intake of the group.
    195      *
    196      * @param group the group to declare
    197      * @return the HTML that declares the group to Intake in a hidden input field
    198      */
    199     public String declareGroup(Group group)
    200     {
    201         groupSB.setLength(0);
    202         declareGroup(group, groupSB);
    203         return groupSB.toString();
    204     }
    205
    206     /**
    207      * xhtml valid hidden input field(s) that notifies intake of the
    208      * group's presence.
    209      * @param group the group to declare
    210      * @param sb a String Builder where the hidden field HTML will be appended
    211      */
    212     public void declareGroup(Group group, StringBuilder sb)
    213     {
    214         if (!declaredGroups.containsKey(group.getIntakeGroupName()))
    215         {
    216             declaredGroups.put(group.getIntakeGroupName(), null);
    217             sb.append("<input type=\"hidden\" name=\"")
    218                     .append(INTAKE_GRP)
    219                     .append("\" value=\"")
    220                     .append(group.getGID())
    221                     .append("\"/>\n");
    222         }
    223         group.appendHtmlFormInput(sb);
    224     }
    225
    226     /**
    227      * Declare that a new form starts
    228      */
    229     public void newForm()
    230     {
    231         declaredGroups.clear();
    232         for (Group group : groups.values())
    233         {
    234             group.resetDeclared();
    235         }
    236     }
    237
    238     /**
    239      * Implementation of ApplicationTool interface is not needed for this
    240      * tool as it is request scoped
    241      */
    242     @Override
    243     public void refresh()
    244     {
    245         // empty
    246     }
    247
    248     /**
    249      * Inner class to present a nice interface to the template designer
    250      */
    251     public class PullHelper
    252     {
    253         /** Name of the group used by the pull helper */
    254         String groupName;
    255
    256         /**
    257          * Protected constructor to force use of factory method.
    258          *
    259          * @param groupName the group name
    260          */
    261         protected PullHelper(String groupName)
    262         {
    263             this.groupName = groupName;
    264         }
    265
    266         /**
    267          * Populates the object with the default values from the XML File
    268          *
    269          * @return a Group object with the default values
    270          * @throws IntakeException if getting the group fails
    271          */
    272         public Group getDefault()
    273                 throws IntakeException
    274         {
    275             return setKey(DEFAULT_KEY);
    276         }
    277
    278         /**
    279          * Calls setKey(key,true)
    280          *
    281          * @param key the group key
    282          * @return an Intake Group
    283          * @throws IntakeException if getting the group fails
    284          */
    285         public Group setKey(String key)
    286                 throws IntakeException
    287         {
    288             return setKey(key, true);
    289         }
    290
    291         /**
    292          * Return the group identified by its key
    293          *
    294          * @param key the group key
    295          * @param create true if a non-existing group should be created
    296          * @return an Intake Group
    297          * @throws IntakeException if getting the group fails
    298          */
    299         public Group setKey(String key, boolean create)
    300                 throws IntakeException
    301         {
    302             Group g = null;
    303
    304             String inputKey = intakeService.getGroupKey(groupName) + key;
    305             if (groups.containsKey(inputKey))
    306             {
    307                 g = groups.get(inputKey);
    308             }
    309             else if (create)
    310             {
    311                 g = intakeService.getGroup(groupName);
    312                 groups.put(inputKey, g);
    313                 g.init(key, pp);
    314             }
    315
    316             return g;
    317         }
    318
    319         /**
    320          * maps an Intake Group to the values from a Retrievable object.
    321          *
    322          * @param obj A retrievable object
    323          * @return an Intake Group
    324          */
    325         public Group mapTo(Retrievable obj)
    326         {
    327             Group g = null;
    328
    329             try
    330             {
    331                 String inputKey = intakeService.getGroupKey(groupName)
    332                         + obj.getQueryKey();
    333                 if (groups.containsKey(inputKey))
    334                 {
    335                     g = groups.get(inputKey);
    336                 }
    337                 else
    338                 {
    339                     g = intakeService.getGroup(groupName);
    340                     groups.put(inputKey, g);
    341                 }
    342
    343                 return g.init(obj);
    344             }
    345             catch (IntakeException e)
    346             {
    347                 log.error(e);
    348             }
    349
    350             return null;
    351         }
    352     }
    353
    354     /**
    355      * get a specific group
    356      * @param groupName the name of the group
    357      * @return a {@link PullHelper} wrapper around the group
    358      */
    359     public PullHelper get(String groupName)
    360     {
    361         return pullMap.get(groupName);
    362     }
    363
    364     /**
    365      * Get a specific group
    366      *
    367      * @param groupName the name of the group
    368      * @param throwExceptions if false, exceptions will be suppressed.
    369      * @return a {@link PullHelper} wrapper around the group
    370      * @throws IntakeException could not retrieve group
    371      */
    372     public PullHelper get(String groupName, boolean throwExceptions)
    373             throws IntakeException
    374     {
    375         return pullMap.get(groupName);
    376     }
    377
    378     /**
    379      * Loops through all of the Groups and checks to see if
    380      * the data within the Group is valid.
    381      * @return true if all groups are valid
    382      */
    383     public boolean isAllValid()
    384     {
    385         boolean allValid = true;
    386         for (Group group : groups.values())
    387         {
    388             allValid &= group.isAllValid();
    389         }
    390         return allValid;
    391     }
    392
    393     /**
    394      * Get a specific group by name and key.
    395      * @param groupName the name of the group
    396      * @param key the key for the group
    397      * @return the {@link Group}
    398      * @throws IntakeException if the group could not be retrieved
    399      */
    400     public Group get(String groupName, String key)
    401             throws IntakeException
    402     {
    403         return get(groupName, key, true);
    404     }
    405
    406     /**
    407      * Get a specific group by name and key. Also specify
    408      * whether or not you want to create a new group.
    409      * @param groupName the name of the group
    410      * @param key the key for the group
    411      * @param create true if a new group should be created
    412      * @return the {@link Group}
    413      * @throws IntakeException if the group could not be retrieved
    414      */
    415     public Group get(String groupName, String key, boolean create)
    416             throws IntakeException
    417     {
    418         if (groupName == null)
    419         {
    420             throw new IntakeException("intakeService.get: groupName == null");
    421         }
    422         if (key == null)
    423         {
    424             throw new IntakeException("intakeService.get: key == null");
    425         }
    426
    427         PullHelper ph = get(groupName);
    428         return (ph == null) ? null : ph.setKey(key, create);
    429     }
    430
    431     /**
    432      * Removes group.  Primary use is to remove a group that has
    433      * been processed by an action and is no longer appropriate
    434      * in the view (screen).
    435      * @param group the group instance to remove
    436      */
    437     public void remove(Group group)
    438     {
    439         if (group != null)
    440         {
    441             groups.remove(group.getObjectKey());
    442             group.removeFromRequest();
    443
    444             String[] groupKeys = pp.getStrings(INTAKE_GRP);
    445
    446             pp.remove(INTAKE_GRP);
    447
    448 			if (groupKeys != null)
    449 			{
    450 		        for (int i = 0; i < groupKeys.length; i++)
    451 		        {
    452 		            if (!groupKeys[i].equals(group.getGID()))
    453 		            {
    454 		                 pp.add(INTAKE_GRP, groupKeys[i]);
    455 		            }
    456                 }
    457 		    }
    458
    459             try
    460             {
    461                 intakeService.releaseGroup(group);
    462             }
    463             catch (IntakeException ie)
    464             {
    465                 log.error("Tried to release unknown group "
    466                         + group.getIntakeGroupName());
    467             }
    468         }
    469     }
    470
    471     /**
    472      * Removes all groups.  Primary use is to remove groups that have
    473      * been processed by an action and are no longer appropriate
    474      * in the view (screen).
    475      */
    476     public void removeAll()
    477     {
    478         Object[] allGroups = groups.values().toArray();
    479         for (int i = allGroups.length - 1; i >= 0; i--)
    480         {
    481             Group group = (Group) allGroups[i];
    482             remove(group);
    483         }
    484     }
    485
    486     /**
    487      * Get a Map containing all the groups.
    488      *
    489      * @return the Group Map
    490      */
    491     public Map<String, Group> getGroups()
    492     {
    493         return groups;
    494     }
    495
    496     // ****************** Recyclable implementation ************************
    497
    498     private boolean disposed;
    499
    500     /**
    501      * Recycles the object for a new client. Recycle methods with
    502      * parameters must be added to implementing object and they will be
    503      * automatically called by pool implementations when the object is
    504      * taken from the pool for a new client. The parameters must
    505      * correspond to the parameters of the constructors of the object.
    506      * For new objects, constructors can call their corresponding recycle
    507      * methods whenever applicable.
    508      * The recycle methods must call their super.
    509      */
    510     @Override
    511     public void recycle()
    512     {
    513         disposed = false;
    514     }
    515
    516     /**
    517      * Disposes the object after use. The method is called
    518      * when the object is returned to its pool.
    519      * The dispose method must call its super.
    520      */
    521     @Override
    522     public void dispose()
    523     {
    524         for (Group group : groups.values())
    525         {
    526             try
    527             {
    528                 intakeService.releaseGroup(group);
    529             }
    530             catch (IntakeException ie)
    531             {
    532                 log.error("Tried to release unknown group "
    533                         + group.getIntakeGroupName());
    534             }
    535         }
    536
    537         groups.clear();
    538         declaredGroups.clear();
    539         pp = null;
    540
    541         disposed = true;
    542     }
    543
    544     /**
    545      * Checks whether the recyclable has been disposed.
    546      *
    547      * @return true, if the recyclable is disposed.
    548      */
    549     @Override
    550     public boolean isDisposed()
    551     {
    552         return disposed;
    553     }
    554 }