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.IntakeServiceFacade;
    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.services.pull.ApplicationTool;
    37  import org.apache.turbine.util.RunData;
    38
    39
    40  /**
    41   * The main class through which Intake is accessed.  Provides easy access
    42   * to the Fulcrum Intake component.
    43   *
    44   * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
    45   * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
    46   * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
    47   * @author <a href="mailto:epugh@upstate.com">Eric Pugh</a>
    48   * @version $Id: IntakeTool.java 1706239 2015-10-01 13:18:35Z tv $
    49   */
    50  public class IntakeTool
    51          implements ApplicationTool, Recyclable
    52  {
    53      /** Used for logging */
    54      protected static final Log log = LogFactory.getLog(IntakeTool.class);
    55
    56      /** Constant for default key */
    57      public static final String DEFAULT_KEY = "_0";
    58
    59      /** Constant for the hidden fieldname */
    60      public static final String INTAKE_GRP = "intake-grp";
    61
    62      /** Groups from intake.xml */
    63      protected HashMap<String, Group> groups;
    64
    65      /** ValueParser instance */
    66      protected ValueParser pp;
    67
    68      private final HashMap<String, Group> declaredGroups = new HashMap<String, Group>();
    69      private final StringBuilder allGroupsSB = new StringBuilder(256);
    70      private final StringBuilder groupSB = new StringBuilder(128);
    71
    72      /** The cache of PullHelpers. **/
    73      private final Map<String, IntakeTool.PullHelper> pullMap;
    74
    75      /**
    76       * Constructor
    77       */
    78      public IntakeTool()
    79      {
    80          String[] groupNames = IntakeServiceFacade.getGroupNames();
    81          int groupCount = 0;
    82          if (groupNames != null)
    83          {
    84              groupCount = groupNames.length;
    85          }
    86          groups = new HashMap<String, Group>((int) (1.25 * groupCount + 1));
    87          pullMap = new HashMap<String, IntakeTool.PullHelper>((int) (1.25 * groupCount + 1));
    88
    89          for (int i = groupCount - 1; i >= 0; i--)
    90          {
    91              pullMap.put(groupNames[i], new PullHelper(groupNames[i]));
    92          }
    93      }
    94
    95      /**
    96       * Prepares intake for a single request
    97       */
    98      @Override
    99      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 }