Author: tmortagne
Date: 2007-11-20 17:02:51 +0100 (Tue, 20 Nov 2007)
New Revision: 6034

Added:
   
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/AbstractXarMojo.java
   
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XWikiDocument.java
Modified:
   xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/pom.xml
   
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/UnXarMojo.java
   
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XarMojo.java
Log:
* XTOOLS-19: The Maven xar builder use file name in place of real document name
* improve xar plugin to follow XWiki checkstyle rules.

Modified: xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/pom.xml
===================================================================
--- xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/pom.xml       
2007-11-20 15:24:56 UTC (rev 6033)
+++ xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/pom.xml       
2007-11-20 16:02:51 UTC (rev 6034)
@@ -54,5 +54,26 @@
       <artifactId>maven-artifact</artifactId>
       <version>2.0</version>
     </dependency>
+    <dependency>
+      <groupId>dom4j</groupId>
+      <artifactId>dom4j</artifactId>
+      <version>1.6.1</version>
+    </dependency>
   </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <!-- Apply the Checkstyle configurations defined in the top level 
pom.xml file -->
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <dependencies>
+          <dependency>
+            <groupId>com.xpn.xwiki.platform</groupId>
+            <artifactId>xwiki-build-verifications</artifactId>
+            <version>${pom.version}</version>
+          </dependency>
+        </dependencies>
+      </plugin>
+    </plugins>
+  </build>
 </project>

Added: 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/AbstractXarMojo.java
===================================================================
--- 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/AbstractXarMojo.java
                             (rev 0)
+++ 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/AbstractXarMojo.java
     2007-11-20 16:02:51 UTC (rev 6034)
@@ -0,0 +1,79 @@
+package com.xpn.xwiki.tool.xar;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.codehaus.plexus.archiver.ArchiveFileFilter;
+import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
+import org.codehaus.plexus.logging.Logger;
+import org.codehaus.plexus.logging.console.ConsoleLogger;
+
+/**
+ * Base class for xar and unxar mojos.
+ * 
+ * @version $Id: $
+ */
+abstract class AbstractXarMojo extends AbstractMojo
+{
+    /**
+     * Open hook.
+     */
+    protected static final String HOOK_OPEN = "[";
+
+    /**
+     * Close hook.
+     */
+    protected static final String HOOK_CLOSE = "]";
+
+    /**
+     * The name of the file in the package when to find general informations.
+     */
+    protected static final String PACKAGE_XML = "package.xml";
+
+    /**
+     * Unpacks the XAR file (exclude the package.xml file if it exists).
+     * 
+     * @param file the file to be unpacked.
+     * @param location the location where to put the unpacket files.
+     * @param logName the name use with [EMAIL PROTECTED] ConsoleLogger}.
+     * @param overwrite indicate if extracted files has to overwrite existing 
ones.
+     * @throws MojoExecutionException error when unpacking the file.
+     */
+    protected void unpack(File file, File location, String logName, boolean 
overwrite)
+        throws MojoExecutionException
+    {
+        try {
+            ZipUnArchiver unArchiver = new ZipUnArchiver();
+            unArchiver.enableLogging(new ConsoleLogger(Logger.LEVEL_ERROR, 
logName));
+            unArchiver.setSourceFile(file);
+            unArchiver.setDestDirectory(location);
+
+            // Ensure that we don't overwrite XML document files present in 
this project since
+            // we want those to be used and not the ones in the dependent XAR.
+            unArchiver.setOverwrite(overwrite);
+
+            if (!overwrite) {
+                // Do not unpack any package.xml file in dependant XARs. We'll 
generate a complete
+                // one automatically.
+                List filters = new ArrayList();
+                filters.add(new ArchiveFileFilter()
+                {
+                    public boolean include(InputStream dataStream, String 
entryName)
+                    {
+                        return (!entryName.equals(PACKAGE_XML));
+                    }
+                });
+                unArchiver.setArchiveFilters(filters);
+            }
+
+            unArchiver.extract();
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error unpacking file " + 
HOOK_OPEN + file
+                + HOOK_CLOSE + " to " + HOOK_OPEN + location + HOOK_CLOSE, e);
+        }
+    }
+}


Property changes on: 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/AbstractXarMojo.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/UnXarMojo.java
===================================================================
--- 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/UnXarMojo.java
   2007-11-20 15:24:56 UTC (rev 6033)
+++ 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/UnXarMojo.java
   2007-11-20 16:02:51 UTC (rev 6034)
@@ -21,13 +21,9 @@
 
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
-import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.artifact.Artifact;
-import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
 import org.codehaus.plexus.archiver.ArchiverException;
-import org.codehaus.plexus.logging.console.ConsoleLogger;
-import org.codehaus.plexus.logging.Logger;
 
 import java.io.File;
 import java.io.IOException;
@@ -35,16 +31,26 @@
 
 /**
  * Expand a XAR file.
- *
+ * 
  * @version $Id: $
  * @goal unxar
  * @requiresDependencyResolution runtime
  */
-public class UnXarMojo extends AbstractMojo
+public class UnXarMojo extends AbstractXarMojo
 {
     /**
+     * ":".
+     */
+    private static final String TWO_POINTS = ":";
+
+    /**
+     * "...".
+     */
+    private static final String DOTDOTDOT = "...";
+
+    /**
      * The maven project.
-     *
+     * 
      * @parameter expression="${project}"
      * @required
      * @readonly
@@ -52,86 +58,95 @@
     private MavenProject project;
 
     /**
-     * The groupId of the XAR dependency to expand
+     * The groupId of the XAR dependency to expand.
+     * 
      * @parameter
      * @required
      */
     private String groupId;
 
     /**
-     * The artifactId of the XAR dependency to expand
+     * The artifactId of the XAR dependency to expand.
+     * 
      * @parameter
      * @required
      */
     private String artifactId;
 
     /**
-     * The location where to put the expanded XAR
+     * The location where to put the expanded XAR.
+     * 
      * @parameter
      * @required
      */
     private File outputDirectory;
 
     /**
-     * @see org.apache.maven.plugin.Mojo#execute()
+     * [EMAIL PROTECTED]
+     * 
+     * @see org.apache.maven.plugin.AbstractMojo#execute()
      */
     public void execute() throws MojoExecutionException, MojoFailureException
     {
         this.outputDirectory.mkdirs();
 
-        try
-        {
+        try {
             performUnArchive();
+        } catch (Exception e) {
+            throw new MojoExecutionException("Error while expanding the XAR 
file " + HOOK_OPEN
+                + this.groupId + TWO_POINTS + this.artifactId + HOOK_CLOSE, e);
         }
-        catch (Exception e)
-        {
-            throw new MojoExecutionException("Error while expanding the XAR 
file ["
-                + this.groupId + ":" + this.artifactId + "]", e );
-        }
     }
 
+    /**
+     * @return the maven artifact.
+     * @throws MojoExecutionException error when seraching for the mavebn 
artifact.
+     */
     private Artifact findArtifact() throws MojoExecutionException
     {
         Artifact resolvedArtifact = null;
 
-        getLog().debug("Searching for an artifact that matches [" + 
this.groupId + ":"
-            + this.artifactId + "]...");
+        getLog().debug(
+            "Searching for an artifact that matches " + HOOK_OPEN + 
this.groupId + TWO_POINTS
+                + this.artifactId + HOOK_CLOSE + DOTDOTDOT);
 
         Iterator it = this.project.getArtifacts().iterator();
-        while (it.hasNext())
-        {
+        while (it.hasNext()) {
             Artifact artifact = (Artifact) it.next();
 
-            getLog().debug("Checking artifact [" + artifact.getGroupId() + ":"
-                + artifact.getArtifactId() + ":" + artifact.getType() + 
"]...");
+            getLog().debug(
+                "Checking artifact " + HOOK_OPEN + artifact.getGroupId() + 
TWO_POINTS
+                    + artifact.getArtifactId() + TWO_POINTS + 
artifact.getType() + HOOK_CLOSE
+                    + DOTDOTDOT);
 
             if (artifact.getGroupId().equals(this.groupId)
-                && artifact.getArtifactId().equals(this.artifactId))
-            {
+                && artifact.getArtifactId().equals(this.artifactId)) {
                 resolvedArtifact = artifact;
                 break;
             }
         }
 
-        if (resolvedArtifact == null)
-        {
-            throw new MojoExecutionException( "Artifact [" + this.groupId + 
":" + this.artifactId
-                + "] is not a dependency of the project.");
+        if (resolvedArtifact == null) {
+            throw new MojoExecutionException("Artifact " + HOOK_OPEN + 
this.groupId + TWO_POINTS
+                + this.artifactId + HOOK_CLOSE + " is not a dependency of the 
project.");
         }
 
         return resolvedArtifact;
     }
 
+    /**
+     * Unzip maven artifact.
+     * 
+     * @throws ArchiverException error when unzip package.
+     * @throws IOException error when unzip package.
+     * @throws MojoExecutionException error when unzip package.
+     */
     private void performUnArchive() throws ArchiverException, IOException, 
MojoExecutionException
     {
         Artifact artifact = findArtifact();
 
-        getLog().debug("Source XAR = [" + artifact.getFile() + "]");
+        getLog().debug("Source XAR = " + HOOK_OPEN + artifact.getFile() + 
HOOK_CLOSE);
 
-        ZipUnArchiver unArchiver = new ZipUnArchiver();
-        unArchiver.setSourceFile(artifact.getFile());
-        unArchiver.setDestFile(this.outputDirectory);
-        unArchiver.enableLogging(new ConsoleLogger(Logger.LEVEL_ERROR, 
"UnXarMojo"));
-        unArchiver.extract();
+        unpack(artifact.getFile(), this.outputDirectory, "XarMojo", false);
     }
 }

Added: 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XWikiDocument.java
===================================================================
--- 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XWikiDocument.java
                               (rev 0)
+++ 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XWikiDocument.java
       2007-11-20 16:02:51 UTC (rev 6034)
@@ -0,0 +1,98 @@
+package com.xpn.xwiki.tool.xar;
+
+import java.io.File;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+
+/**
+ * Parse XWiki document from xml.
+ * 
+ * @version $Id: $
+ */
+public class XWikiDocument
+{
+    /**
+     * The name of the document.
+     */
+    private String name;
+
+    /**
+     * The space of the document.
+     */
+    private String space;
+
+    /**
+     * Create [EMAIL PROTECTED] XWikiDocument} instance.
+     */
+    public XWikiDocument()
+    {
+
+    }
+
+    /**
+     * Parse xml file to extract documents informations.
+     * 
+     * @param file the xml file.
+     * @throws DocumentException error when parsing xml file.
+     */
+    public void fromXML(File file) throws DocumentException
+    {
+        SAXReader reader = new SAXReader();
+        Document domdoc = reader.read(file);
+
+        Element docel = domdoc.getRootElement();
+
+        Element name = docel.element("name");
+        if (name != null) {
+            this.name = name.getText();
+        }
+
+        Element space = docel.element("web");
+        if (space != null) {
+            this.space = space.getText();
+        }
+    }
+
+    /**
+     * @return the name of the document.
+     */
+    public String getName()
+    {
+        return this.name;
+    }
+
+    /**
+     * @param name the name of the document.
+     */
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    /**
+     * @return the space of the document.
+     */
+    public String getSpace()
+    {
+        return this.space;
+    }
+
+    /**
+     * @param space the space of the document.
+     */
+    public void setSpace(String space)
+    {
+        this.space = space;
+    }
+    
+    /**
+     * @return the full name of the document.
+     */
+    public String getFullName()
+    {
+        return this.space == null ? this.name : this.space + "." + this.name;
+    }
+}


Property changes on: 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XWikiDocument.java
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XarMojo.java
===================================================================
--- 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XarMojo.java
     2007-11-20 15:24:56 UTC (rev 6033)
+++ 
xwiki-platform/xwiki-tools/trunk/build-tools/build-xar-plugin/src/main/java/com/xpn/xwiki/tool/xar/XarMojo.java
     2007-11-20 16:02:51 UTC (rev 6034)
@@ -21,45 +21,45 @@
 
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
-import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.project.MavenProject;
-import org.codehaus.plexus.archiver.ArchiveFileFilter;
-import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
+import org.codehaus.plexus.archiver.ArchiveEntry;
 import org.codehaus.plexus.archiver.manager.ArchiverManager;
 import org.codehaus.plexus.archiver.zip.ZipArchiver;
-import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
-import org.codehaus.plexus.logging.console.ConsoleLogger;
-import org.codehaus.plexus.logging.Logger;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.FileWriter;
-import java.io.InputStream;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.Set;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.StringTokenizer;
 
 /**
  * Gather all resources in a XAR file (which is actually a ZIP file). Also 
generates a XAR
  * descriptor if none is provided.
- *
- * <p>Note that the generated descriptor currently doesn't handle
- * translations.</p>
- *
+ * <p>
+ * Note that the generated descriptor currently doesn't handle translations.
+ * </p>
+ * 
  * @version $Id: $
  * @goal xar
  * @phase package
  * @requiresDependencyResolution runtime
  */
-public class XarMojo extends AbstractMojo
+public class XarMojo extends AbstractXarMojo
 {
     /**
+     * To look up Archiver/UnArchiver implementations.
+     * 
+     * @parameter 
expression="${component.org.codehaus.plexus.archiver.manager.ArchiverManager}"
+     * @required
+     */
+    protected ArchiverManager archiverManager;
+
+    /**
      * The maven project.
-     *
+     * 
      * @parameter expression="${project}"
      * @required
      * @readonly
@@ -67,16 +67,10 @@
     private MavenProject project;
 
     /**
-     * To look up Archiver/UnArchiver implementations
-     *
-     * @parameter 
expression="${component.org.codehaus.plexus.archiver.manager.ArchiverManager}"
-     * @required
+     * [EMAIL PROTECTED]
+     * 
+     * @see org.apache.maven.plugin.AbstractMojo#execute()
      */
-    protected ArchiverManager archiverManager;
-    
-    /**
-     * @see org.apache.maven.plugin.Mojo#execute()
-     */
     public void execute() throws MojoExecutionException, MojoFailureException
     {
         if (this.project.getResources().size() < 1) {
@@ -86,21 +80,21 @@
 
         try {
             performArchive();
-        }
-        catch (Exception e) {
+        } catch (Exception e) {
             throw new MojoExecutionException("Error while creating XAR file", 
e);
         }
     }
 
     /**
      * Create the XAR by zipping the resource files.
-     *
+     * 
      * @throws Exception if the zipping failed for some reason
      */
     private void performArchive() throws Exception
     {
-        File xarFile = new File(this.project.getBuild().getDirectory(),
-            this.project.getArtifactId() + ".xar");
+        File xarFile =
+            new File(this.project.getBuild().getDirectory(), 
this.project.getArtifactId()
+                + ".xar");
 
         // The source dir points to the target/classes directory where the 
Maven resources plugin
         // has copied the XAR files during the process-resources phase.
@@ -118,10 +112,10 @@
         archiver.addDirectory(sourceDir);
 
         // If no package.xml can be found at the top level of the current 
project, generate one
-        if (archiver.getFiles().get("package.xml") == null) {
-            File generatedPackageFile = new File(sourceDir, "package.xml");
-            generatePackageXml(generatedPackageFile, 
archiver.getFiles().keySet());
-            archiver.addFile(generatedPackageFile, "package.xml");
+        if (archiver.getFiles().get(PACKAGE_XML) == null) {
+            File generatedPackageFile = new File(sourceDir, PACKAGE_XML);
+            generatePackageXml(generatedPackageFile, 
archiver.getFiles().values());
+            archiver.addFile(generatedPackageFile, PACKAGE_XML);
         }
 
         archiver.createArchive();
@@ -129,9 +123,17 @@
         this.project.getArtifact().setFile(xarFile);
     }
 
-    private void generatePackageXml(File packageFile, Set files) throws 
IOException
+    /**
+     * Create and add package configuration file to the package.
+     * 
+     * @param packageFile the package when to add configuration file.
+     * @param files the files in the package.
+     * @throws IOException error when writing the configuration file.
+     */
+    private void generatePackageXml(File packageFile, Collection files) throws 
IOException
     {
-        this.getLog().info("Generating package.xml descriptor at [" + 
packageFile.getPath() + "]"); 
+        this.getLog()
+            .info("Generating package.xml descriptor at [" + 
packageFile.getPath() + "]");
 
         FileWriter fw = new FileWriter(packageFile);
         fw.write("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
@@ -148,15 +150,13 @@
 
         // First element before the "/" is the space name, rest is the 
document name. Warn if there
         // are more than 1 "/".
-        for (Iterator it = files.iterator(); it.hasNext();)
-        {
-            String fileName = (String) it.next();
-            StringTokenizer st = new StringTokenizer(fileName, "/");
-            if (st.countTokens() != 2) {
-                this.getLog().warn("Invalid file location [" + fileName + "], 
skipping it.");
-            } else {
-                fw.write("    <file defaultAction=\"0\" language=\"\">" + 
st.nextToken() + "."
-                    + st.nextToken() + "</file>\n");
+        for (Iterator it = files.iterator(); it.hasNext();) {
+            ArchiveEntry entry = (ArchiveEntry) it.next();
+
+            String fullName = getFullNameFromXML(entry.getFile());
+
+            if (fullName != null) {
+                fw.write(" <file defaultAction=\"0\" language=\"\">" + 
fullName + "</file>\n");
             }
         }
 
@@ -165,6 +165,32 @@
         fw.close();
     }
 
+    /**
+     * Get wiki document full name found in xml.
+     * 
+     * @param file the file to parse.
+     * @return the full name of the document.
+     */
+    private String getFullNameFromXML(File file)
+    {
+        String fullname = null;
+
+        try {
+            XWikiDocument doc = new XWikiDocument();
+            doc.fromXML(file);
+            fullname = doc.getFullName();
+        } catch (Exception e) {
+            this.getLog().warn("Failed to parse " + file.getAbsolutePath(), e);
+        }
+
+        return fullname;
+    }
+
+    /**
+     * Unpack xar dependencies before pack then into it.
+     * 
+     * @throws MojoExecutionException error when unpack dependencies.
+     */
     private void unpackDependentXars() throws MojoExecutionException
     {
         Set artifacts = this.project.getArtifacts();
@@ -181,9 +207,8 @@
     }
 
     /**
-     * Unpacks A XAR artifacts into the build output directory, along with the 
project's
-     * XAR files.
-     *
+     * Unpacks A XAR artifacts into the build output directory, along with the 
project's XAR files.
+     * 
      * @param artifact the XAR artifact to unpack.
      * @throws MojoExecutionException in case of unpack error
      */
@@ -196,46 +221,6 @@
         }
 
         File file = artifact.getFile();
-        try {
-            unpack(file, outputLocation);
-        } catch (NoSuchArchiverException e) {
-            this.getLog().info("Skip unpacking dependency file with unknown 
extension ["
-                + file.getPath() + "]"); 
-        }
+        unpack(file, outputLocation, "XarMojo", false);
     }
-
-    /**
-     * Unpacks the XAR file (exclude the package.xml file if it exists)
-     *
-     * @param file File to be unpacked.
-     * @param location Location where to put the unpacked files.
-     */
-    private void unpack(File file, File location)
-        throws MojoExecutionException, NoSuchArchiverException
-    {
-        try {
-            ZipUnArchiver unArchiver = new ZipUnArchiver();
-            unArchiver.enableLogging(new ConsoleLogger(Logger.LEVEL_ERROR, 
"XarMojo"));
-            unArchiver.setSourceFile(file);
-            unArchiver.setDestDirectory(location);
-
-            // Ensure that we don't overwrite XML document files present in 
this project since
-            // we want those to be used and not the ones in the dependent XAR.
-            unArchiver.setOverwrite(false);
-
-            // Do not unpack any package.xml file in dependant XARs. We'll 
generate a complete one
-            // automatically.
-            List filters = new ArrayList();
-            filters.add(new ArchiveFileFilter() {
-                public boolean include(InputStream dataStream, String 
entryName ) {
-                    return (!entryName.equals("package.xml"));
-                }});
-
-            unArchiver.setArchiveFilters(filters);
-            unArchiver.extract();
-        } catch (Exception e) {
-            throw new MojoExecutionException("Error unpacking file [" + file + 
"] to [" + location
-                + "]", e);
-        }
-    }
 }

_______________________________________________
notifications mailing list
[email protected]
http://lists.xwiki.org/mailman/listinfo/notifications

Reply via email to