husted      2004/03/27 19:20:56

  Modified:    chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader
                        ActionHelper.java ActionHelperBase.java
                        CommandAction.java
               
chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/commands
                        MailReader.java MailReaderBase.java
  Added:       chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader
                        ClientContext.java ContextAction.java
                        MailReaderAction.java
  Removed:     chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader
                        ViewContext.java
  Log:
  Refactor MailReader and related classes
  
  Revision  Changes    Path
  1.3       +44 -14    
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/ActionHelper.java
  
  Index: ActionHelper.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/ActionHelper.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ActionHelper.java 27 Mar 2004 18:21:30 -0000      1.2
  +++ ActionHelper.java 28 Mar 2004 03:20:55 -0000      1.3
  @@ -20,16 +20,13 @@
   package org.apache.commons.chain.mailreader;
   
   
  -import javax.sql.DataSource;
  -
  -import org.apache.struts.action.ActionForm;
  -import org.apache.struts.action.ActionFormBean;
  -import org.apache.struts.action.ActionForward;
  -import org.apache.struts.action.ActionMapping;
  -import org.apache.struts.action.ActionMessages;
  +import org.apache.commons.chain.Catalog;
  +import org.apache.commons.chain.Context;
  +import org.apache.struts.action.*;
   import org.apache.struts.upload.MultipartRequestWrapper;
   import org.apache.struts.util.MessageResources;
   
  +import javax.sql.DataSource;
   import java.util.Locale;
   
   
  @@ -52,7 +49,7 @@
    * application so they can easily access the various Struts
    * shared resources. These resources may be stored under
    * attributes in the application, session, or request contexts,
  - * but users of this class do not need to know where.
  + * but users of this class do not need to know what goes where.
    * </p>
    * <p>
    * The ActionHelperBase methods simply return the resources
  @@ -109,11 +106,16 @@
   
       /**
        * <p>
  -     * The Locale associated with this client, or null if none.
  +     * Return the Locale associated with this client, or null if none.
        * </p>
        */
       public Locale getLocale();
   
  +    /**
  +     * <p>
  +     * Assign the Locale to associate with this client.
  +     * </p>
  +     */
       public void setLocale(Locale locale);
   
       /**
  @@ -165,7 +167,7 @@
   
       /**
        * <p>
  -     * Retrieve and return the <code>ActionForm</code> bean associated with
  +     * Retrieve and return the <code>ActionForm</code> instance associated with
        * this mapping. If there is no ActionForm present, return <code>null</code>.
        * </p>
        */
  @@ -238,6 +240,35 @@
        */
       public String getEncodeURL(String url);
   
  +
  +// ----------------------------------------------- Catalog / Context
  +
  +    /**
  +     * <p>Returns the default Command Catalog, if any.</p>
  +     * @return the default Command Catalog.
  +     */
  +    public Catalog getCatalog();
  +
  +
  +    /**
  +     * <p>
  +     * Replace the ActionForm instance with an Input context.
  +     * Useful in JSTL or Velocity environments.
  +     * </p>
  +     * @param input Input Context
  +     */
  +    public void setInputAsForm(Context input);
  +
  +
  +    /**
  +     * <p>
  +     * Copy input context attributes over matching ActionForm properties.
  +     * </p>
  +     * @param input Input Context
  +     */
  +    public void setInputToForm(Context input);
  +
  +
   // ------------------------------------------------ Presentation API
   
       /**
  @@ -290,6 +321,5 @@
        * @param path Name given to local or global forward.
        */
       public String getAction(String path);
  -
   
   }
  
  
  
  1.3       +36 -4     
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/ActionHelperBase.java
  
  Index: ActionHelperBase.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/ActionHelperBase.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ActionHelperBase.java     27 Mar 2004 18:21:30 -0000      1.2
  +++ ActionHelperBase.java     28 Mar 2004 03:20:55 -0000      1.3
  @@ -19,6 +19,9 @@
    */
   package org.apache.commons.chain.mailreader;
   
  +import org.apache.commons.beanutils.BeanUtils;
  +import org.apache.commons.chain.Catalog;
  +import org.apache.commons.chain.Context;
   import org.apache.struts.Globals;
   import org.apache.struts.action.*;
   import org.apache.struts.upload.MultipartRequestWrapper;
  @@ -38,7 +41,7 @@
    * applications.
    * </p>
    * <p>
  - * NOTE -- In the next version, a "ViewContext" interface
  + * NOTE -- In the next version, a "ClientContext" interface
    * and implementation will be added to this class to allow
    * access to operations business Commands might use without
    * exposing Http signatures or other implementation details.
  @@ -221,6 +224,7 @@
   
       }
   
  +    // See ActionHelper interface for JavaDoc
       public void setLocale(Locale locale) {
   
           session.setAttribute(Globals.LOCALE_KEY,locale);
  @@ -484,8 +488,36 @@
           return getEncodeURL(getActionMappingURL(path));
       }
   
  +    // ------------------------------------------------- Catalog / Context
  +
  +    // See ActionHelper interface for JavaDoc
  +    public Catalog getCatalog() {
  +
  +        return (Catalog) application.getAttribute(Catalog.CATALOG_KEY);
  +
  +    }
  +
  +    // See ActionHelper interface for JavaDoc
  +    public void setInputAsForm(Context input) {
  +        ActionMapping mapping = getMapping();
  +        String formScope = mapping.getScope();
  +        String name = mapping.getName();
  +        if ("request".equals(formScope)) request.setAttribute(name,input);
  +        else request.getSession().setAttribute(name,input);
  +    }
  +
  +    // See ActionHelper interface for JavaDoc
  +    public void setInputToForm(Context input) {
  +        ActionForm form = getActionForm();
  +        try {
  +            BeanUtils.copyProperties(form,input);
  +        } catch (Throwable t) {
  +            // FIXME: Now what? Log and Throw?
  +        }
  +    }
   
       // --------------------------------------------- Presentation Wrappers
  +
   
       /**
        * <p>Wrapper for getLink(String)</p>
  
  
  
  1.4       +110 -54   
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/CommandAction.java
  
  Index: CommandAction.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/CommandAction.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- CommandAction.java        27 Mar 2004 18:21:30 -0000      1.3
  +++ CommandAction.java        28 Mar 2004 03:20:55 -0000      1.4
  @@ -1,15 +1,17 @@
   package org.apache.commons.chain.mailreader;
   
  -import org.apache.struts.action.*;
  +import org.apache.commons.beanutils.BeanUtils;
   import org.apache.commons.chain.Catalog;
  +import org.apache.commons.chain.Command;
   import org.apache.commons.chain.Context;
   import org.apache.commons.chain.impl.ContextBase;
  -import org.apache.commons.chain.mailreader.commands.MailReader;
  -import org.apache.commons.chain.mailreader.commands.MailReaderBase;
  +import org.apache.struts.action.ActionForm;
  +import org.apache.struts.action.ActionForward;
  +import org.apache.struts.action.ActionMapping;
  +import org.apache.struts.action.DynaActionForm;
   
  -import javax.servlet.http.HttpServletRequest;
  -import javax.servlet.http.HttpServletResponse;
  -import java.util.Locale;
  +import java.util.HashMap;
  +import java.util.Map;
   
   /**
    * <p>
  @@ -27,84 +29,137 @@
    * when support for Commons Chains is added.
    * </p>
    */
  -public class CommandAction extends Action {
  +public abstract class CommandAction extends ContextAction {
   
       /**
        * <p>
  -     * Return the default [EMAIL PROTECTED] org.apache.commons.chain.Catalog}.
  +     * Return the relevant command from the default
  +     * [EMAIL PROTECTED] org.apache.commons.chain.Catalog}.
        * </p>
  -     * @return Default Catalog
  -     * @throws java.lang.UnsupportedOperationException if ControllerContext is
  -     * null.
  +     * @return Command for this helper
        */
  -    public Catalog getCatalog(HttpServletRequest request){
  +    protected Command getCatalogCommand(ActionHelper helper){
   
  -        return (Catalog) 
request.getSession().getServletContext().getAttribute(Catalog.CATALOG_KEY);
  +        Catalog catalog = helper.getCatalog();
  +        String name = helper.getMapping().getName();
  +        return catalog.getCommand(name);
   
       }
   
  -    public ViewContext getContext(ActionHelper helper, ActionForm form) {
  -
  -        Locale locale = helper.getLocale();
  -        Context input = getInput(form);
  -        return new MailReaderBase(locale,input);
  +    /**
  +     * <p>
  +     * Return the client context for this application.
  +     * Must be implemented by a subclass.
  +     * </p>
  +     * @param helper
  +     * @return
  +     */
  +    protected abstract ClientContext getContext(ActionHelper helper);
   
  +    /**
  +     * <p>
  +     * Operations to perform prior to executing business command.
  +     * </p>
  +     * @param helper Our ActionHelper
  +     * @param context Our ClientContext
  +     * @return ActionForward to follow, or null
  +     */
  +    protected ActionForward preExecute(ActionHelper helper, ClientContext context) {
  +        // override to provide functionality
  +        return null;
       }
   
  +    /**
  +     * <p>Convert ActionForm to Chain Context.</p>
  +     * @param form
  +     * @return
  +     */
  +    protected Context getInput(ActionForm form) {
  +
  +        Map input;
  +        if (form instanceof DynaActionForm) {
  +            DynaActionForm dyna = (DynaActionForm) form;
  +            input = dyna.getMap();
  +        }
  +        else try {
  +            input = BeanUtils.describe(form);
  +        } catch (Throwable t) {
  +            input = new HashMap();
  +        }
  +        return new ContextBase(input);
   
  -    public Context getInput(ActionForm form) {
  -        DynaActionForm dyna = (DynaActionForm) form;
  -        return new ContextBase(dyna.getMap());
       }
   
  -    public void conformInput(ActionHelper helper, ViewContext context) {
  +    /**
  +     * <p>Transfer input properties (back) to ActionForm.</p>
  +     * @param helper Our ActionHelper
  +     * @param context Our ClientContext
  +     */
  +    protected void conformInput(ActionHelper helper, ClientContext context) {
  +
           Context input = context.getInput();
  -        ActionMapping mapping = helper.getMapping();
  -        HttpServletRequest request = helper.getRequest();
  -        String formScope = mapping.getScope();
  -        String name = mapping.getName();
  -        if (helper.isRequestScope()) request.setAttribute(name,input);
  -        else request.getSession().setAttribute(name,input);
  +        helper.setInputToForm(input);
  +
       }
   
  -    public void conformState(ActionHelper helper, ViewContext context) {
  +    /**
  +     * <p>Transfer framework properties (back) to framework objects.</p>
  +     * @param helper Our ActionHelper
  +     * @param context Our ClientContext
  +     */
  +    protected void conformState(ActionHelper helper, ClientContext context) {
  +
           helper.setLocale(context.getLocale());
  -    }
   
  -    public ActionForward findSuccess(ActionMapping mapping) {
  -        return mapping.findForward("success");
       }
   
  -    public ActionForward execute(ActionMapping mapping,
  -                                 ActionForm form,
  -                                 HttpServletRequest request,
  -                                 HttpServletResponse response) throws Exception {
  -
  -        // create ActionHelper
  -        ActionHelper helper = new ActionHelperBase(request,response);
  -        // return execute(helper); // ContextAction
  -
  -        // TODO: obtain database reference
  -        // forward = preCommand(helper); if (forward!=null) return forward;
  -
  -        // create mailreader context, using ActionHelper methods
  -        ViewContext context = getContext(helper,form);
  -
  -        // execute command
  -        String name = mapping.getName();
  -        boolean stop = getCatalog(request).getCommand(name).execute(context);
   
  -        // update state from mailreader context
  +    /**
  +     * <p>
  +     * Operations to perform prior to executing business command.
  +     * </p>
  +     * @param helper Our ActionHelper
  +     * @param context Our ClientContext
  +     * @return ActionForward to follow, or null
  +     */
  +    protected ActionForward postExecute(ActionHelper helper, ClientContext context) 
{
  +
           conformInput(helper,context);
           conformState(helper,context);
   
           // TODO: Expose any output
           // TODO: Expose any status messages,
           // TODO: Expose any error messages and find input
  -        // location = postCommand(helper,context);
   
  -        // find success
  -        return helper.findSuccess();
  +        return null;
  +
  +    }
  +
  +    /**
  +     * <p>Convenience method to return nominal location.</p>
  +     * @param mapping Our ActionMapping
  +     * @return ActionForward named "success" or null
  +     */
  +    protected ActionForward findSuccess(ActionMapping mapping) {
  +        return mapping.findForward("success");
  +    }
  +
  +    // See interface for JavaDoc
  +    public ActionForward execute(ActionHelper helper) throws Exception {
  +
  +        ActionForward location;
  +        ClientContext context = getContext(helper);
  +
  +        location = preExecute(helper,context);
  +        if (location!=null) return location;
  +
  +        boolean stop = getCatalogCommand(helper).execute(context);
  +
  +        location = postExecute(helper,context);
  +        if (location!=null) return location;
  +
  +        return findSuccess(helper);
  +
       }
   
       // ModuleContext -> state for a module (mappings, messages) : ReadOnly
  @@ -113,4 +168,5 @@
   
       // StrutsContext -> state for entire Struts application : ReadWrite
       // StrutsContext.createActionContext(request);
  +
   }
  
  
  
  1.1                  
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/ClientContext.java
  
  Index: ClientContext.java
  ===================================================================
  package org.apache.commons.chain.mailreader;
  
  import org.apache.commons.chain.Context;
  
  import java.util.Locale;
  
  /**
   * <p>
   * A "disconnected" representation of the framework state for the client
   * making a request. An instance of this interface may be passed to
   * other components. An instance may also be used to update the "connected"
   * representation.
   * </p>
   * <p>
   * See [EMAIL PROTECTED] org.apache.commons.chain.mailreader.commands.MailReaderBase}
   * for an implementation.
   * </p>
   */
  public interface ClientContext extends Context {
  
      public static String PN_LOCALE = "locale";
      public boolean isLocale();
      public void setLocale(Locale locale);
      public Locale getLocale();
  
      public static String PN_INPUT = "input";
      public boolean isInput();
      public void setInput(Context context);
      public Context getInput();
  
  }
  
  
  
  1.1                  
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/ContextAction.java
  
  Index: ContextAction.java
  ===================================================================
  /*
   * $Header: 
/home/cvs/jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/ContextAction.java,v
 1.1 2004/03/28 03:20:55 husted Exp $
   * $Revision: 1.1 $
   * $Date: 2004/03/28 03:20:55 $
   *
   * Copyright 1999-2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.commons.chain.mailreader;
  
  import org.apache.struts.action.Action;
  import org.apache.struts.action.ActionForm;
  import org.apache.struts.action.ActionForward;
  import org.apache.struts.action.ActionMapping;
  
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  
  /**
   * <p>
   * Create ActionContext from standard execute call and pass to a context
   * form of execute, to be extended by a subclass.
   * </p>
   */
  public abstract class ContextAction extends Action {
  
      /**
       * <p>
       * Token representing a nominal outcome ["success"].
       * </p>
       */
      private static String SUCCESS = "success";
  
  
      /**
       * <p>
       * Convenience method to find a forward named "success".
       * </p>
       * @param helper Our ActionHelper
       * @return a forward named "success" or null.
       */
      protected ActionForward findSuccess(ActionHelper helper) {
          return helper.getMapping().findForward(SUCCESS);
      }
  
  
      /**
       * <p>
       * Process the request represented by the ActionHelper, and return an
       * ActionForward representing the resource that will create the corresonding
       * response (or create the response directly and reutrn null), with provision
       * for handling exceptions thrown by the business logic.
       * </p>
       * @param helper The ActionHelper we are processing
       * @exception Exception if the application business logic throws
       *  an exception
       */
      public abstract ActionForward execute(ActionHelper helper) throws Exception;
  
  
      /**
       * <p>
       * Create ActionHelper and return result of execute(ActionHelper).
       * Subclasses are expected to implement the execute(ActionHelper)
       * and provide the needed functionality there.
       * </p>
       * @param mapping The ActionMapping used to select this instance
       * @param form The optional ActionForm bean for this request (if any)
       * @param request The HTTP request we are processing
       * @param response The HTTP response we are creating
       * @exception Exception if the application business logic throws
       *  an exception
       */
      public ActionForward execute(ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
                                   HttpServletResponse response) throws Exception {
  
          ActionHelper helper = new ActionHelperBase(request,response);
          return execute(helper);
  
      }
  
  }
  
  
  
  1.1                  
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/MailReaderAction.java
  
  Index: MailReaderAction.java
  ===================================================================
  /*
   * $Header: 
/home/cvs/jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/MailReaderAction.java,v
 1.1 2004/03/28 03:20:55 husted Exp $
   * $Revision: 1.1 $
   * $Date: 2004/03/28 03:20:55 $
   *
   * Copyright 1999-2004 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.commons.chain.mailreader;
  
  import org.apache.commons.chain.Context;
  import org.apache.commons.chain.mailreader.commands.MailReaderBase;
  
  import java.util.Locale;
  
  /**
   * <p>Process Commands using a MailReader ClientContext.</p>
   */
  public class MailReaderAction extends CommandAction {
  
      // See interface for JavaDoc
      public ClientContext getContext(ActionHelper helper) {
  
          Locale locale = helper.getLocale();
          Context input = getInput(helper.getActionForm());
          return new MailReaderBase(locale,input);
  
      }
  
  }
  
  
  
  1.2       +15 -7     
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/commands/MailReader.java
  
  Index: MailReader.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/commands/MailReader.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MailReader.java   27 Mar 2004 03:58:02 -0000      1.1
  +++ MailReader.java   28 Mar 2004 03:20:56 -0000      1.2
  @@ -19,17 +19,25 @@
    */
   package org.apache.commons.chain.mailreader.commands;
   
  -import org.apache.commons.chain.Context;
  -import org.apache.commons.chain.mailreader.ViewContext;
  -
  -import java.util.Locale;
  +import org.apache.commons.chain.mailreader.ClientContext;
   
   /**
    * Application interface for MailReader Commands.
    */
  -public interface MailReader extends ViewContext {
  +public interface MailReader extends ClientContext {
   
  +    /**
  +     * Property name for the country field of a Locale.
  +     */
       static String PN_COUNTRY = "country";
  +
  +    /**
  +     * Property name for the language field of a Locale.
  +     */
       static String PN_LANGUAGE = "language";
  +
  +
  +    // Database
  +    // User
   
   }
  
  
  
  1.2       +33 -24    
jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/commands/MailReaderBase.java
  
  Index: MailReaderBase.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-commons-sandbox/chain/apps/mailreader/src/java/org/apache/commons/chain/mailreader/commands/MailReaderBase.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- MailReaderBase.java       27 Mar 2004 03:58:02 -0000      1.1
  +++ MailReaderBase.java       28 Mar 2004 03:20:56 -0000      1.2
  @@ -19,8 +19,8 @@
    */
   package org.apache.commons.chain.mailreader.commands;
   
  -import org.apache.commons.chain.impl.ContextBase;
   import org.apache.commons.chain.Context;
  +import org.apache.commons.chain.impl.ContextBase;
   
   import java.util.Locale;
   
  @@ -29,54 +29,63 @@
    */
   public class MailReaderBase extends ContextBase implements MailReader {
   
  +    /**
  +     * <p>Default constructor.</p>
  +     */
       public MailReaderBase() {
           super();
       }
   
  +    /**
  +     * <p>Convenience constructor to create and populate instance.</p>
  +     * @param locale
  +     * @param input
  +     */
       public MailReaderBase(Locale locale, Context input) {
           super();
           this.locale = locale;
           this.input = input;
       }
   
  -    private boolean isLocale = false;
  +    /**
  +     * <p>Field for Locale property.</p>
  +     */
       private Locale locale;
   
  +    /**
  +     * <p>Return Locale property</p>
  +     * @return This Locale property
  +     */
       public Locale getLocale() {
           return locale;
       }
   
  +    /**
  +     * <p>Assign Locale property</p>
  +     * @param locale New Locale
  +     */
       public void setLocale(Locale locale) {
           this.locale = locale;
  -        setIsLocale(true);
  -    }
  -
  -    public boolean isLocale() {
  -        return isLocale;
  -    }
  -
  -    public void setIsLocale(boolean locale) {
  -        isLocale = locale;
       }
   
  -    private boolean isInput = false;
  +    /**
  +     * <p>Field for Input property.</p>
  +     */
       private Context input;
   
  +    /**
  +     * <p>Return Input property.</p>
  +     * @return This Input property
  +     */
       public Context getInput() {
           return input;
       }
   
  +    /**
  +     * <p>Assign Input property</p>
  +     * @param input New Input context
  +     */
       public void setInput(Context input) {
           this.input = input;
  -        setIsInput(true);
       }
  -
  -    public boolean isInput() {
  -        return isInput;
  -    }
  -
  -    public void setIsInput(boolean input) {
  -        isInput = input;
  -    }
  -
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to