This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git

commit 0bc9c861ae34501a865a0269ad49179c13fb5740
Author: Robert Lazarski <[email protected]>
AuthorDate: Wed May 13 11:44:21 2026 -1000

    Add autoconfiguration tests for embedded Tomcat support
    
    8 new tests using Mockito to verify the axis2.repo property logic
    in both Axis2ServletAutoConfiguration and Axis2RepositoryAutoConfiguration:
    
    Axis2ServletAutoConfiguration (6 tests):
    - repoProperty_overridesGetRealPath: axis2.repo takes precedence
      over ServletContext.getRealPath(); both axis2.repository.path
      and axis2.xml.path init-params are set correctly
    - emptyRepoProperty_usesGetRealPath: default behavior unchanged
      when axis2.repo is empty
    - repoProperty_failsFastWhenAxis2XmlMissing: throws
      IllegalStateException when axis2.repo is set but conf/axis2.xml
      is absent
    - noAxis2XmlPath_whenFileAbsentAndRepoNotSet: getRealPath case
      does not fail-fast (backward compatible)
    - servletMappedToConfiguredPath: custom services-path is applied
    - servletMappingConflict_throwsException: validates error on
      servlet mapping conflict
    
    Axis2RepositoryAutoConfiguration (2 tests):
    - repoProperty_usedForAxis2XmlStaging: axis2.repo overrides
      getRealPath for staging
    - nullRealPath_andEmptyRepo_logsWarning: graceful fallback when
      both are unavailable
    
    19 tests total (11 existing + 8 new), all pass.
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---
 .../boot/Axis2ServletAutoConfigurationTest.java    | 246 +++++++++++++++++++++
 src/site/xdoc/docs/spring-boot-starter.xml         | 133 +++++++++--
 2 files changed, 360 insertions(+), 19 deletions(-)

diff --git 
a/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfigurationTest.java
 
b/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfigurationTest.java
new file mode 100644
index 0000000000..ffb47165c1
--- /dev/null
+++ 
b/modules/spring-boot-starter/src/test/java/org/apache/axis2/spring/boot/Axis2ServletAutoConfigurationTest.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.axis2.spring.boot;
+
+import org.apache.axis2.deployment.WarBasedAxisConfigurator;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.ArgumentCaptor;
+
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.ServletRegistration;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * Tests for {@link Axis2ServletAutoConfiguration} and
+ * {@link Axis2RepositoryAutoConfiguration} autoconfiguration logic.
+ *
+ * <p>Verifies that the axis2.repo property correctly overrides
+ * ServletContext.getRealPath() and that the axis2.xml.path init-parameter
+ * is set when the repo contains conf/axis2.xml.
+ */
+class Axis2ServletAutoConfigurationTest {
+
+    // ═══════════════════════════════════════════════════════════════════════
+    // Axis2ServletAutoConfiguration — repository and axis2.xml path
+    // ═══════════════════════════════════════════════════════════════════════
+
+    @Test
+    void repoProperty_overridesGetRealPath(@TempDir Path tempDir) throws 
Exception {
+        // Create the expected directory structure
+        Path confDir = tempDir.resolve("conf");
+        Files.createDirectories(confDir);
+        Files.writeString(confDir.resolve("axis2.xml"), "<axisconfig/>");
+
+        Axis2Properties properties = new Axis2Properties();
+        properties.setRepo(tempDir.toString());
+
+        // Mock ServletContext that returns null for getRealPath (embedded 
mode)
+        ServletContext servletContext = mock(ServletContext.class);
+        when(servletContext.getRealPath("/WEB-INF")).thenReturn(null);
+
+        ServletRegistration.Dynamic axisServlet = 
mock(ServletRegistration.Dynamic.class);
+        when(servletContext.addServlet(eq("AxisServlet"), 
any(org.apache.axis2.transport.http.AxisServlet.class)))
+                .thenReturn(axisServlet);
+        
when(axisServlet.addMapping(anyString())).thenReturn(Collections.emptySet());
+
+        // Execute
+        Axis2ServletAutoConfiguration config = new 
Axis2ServletAutoConfiguration();
+        config.axis2ServletInitializer(properties).onStartup(servletContext);
+
+        // Verify repository path was set from axis2.repo, not getRealPath
+        ArgumentCaptor<String> nameCaptor = 
ArgumentCaptor.forClass(String.class);
+        ArgumentCaptor<String> valueCaptor = 
ArgumentCaptor.forClass(String.class);
+        verify(axisServlet, atLeast(2)).setInitParameter(nameCaptor.capture(), 
valueCaptor.capture());
+
+        Map<String, String> params = new HashMap<>();
+        for (int i = 0; i < nameCaptor.getAllValues().size(); i++) {
+            params.put(nameCaptor.getAllValues().get(i), 
valueCaptor.getAllValues().get(i));
+        }
+
+        assertEquals(tempDir.toString(),
+                
params.get(WarBasedAxisConfigurator.PARAM_AXIS2_REPOSITORY_PATH),
+                "repository path should come from axis2.repo property");
+        
assertNotNull(params.get(WarBasedAxisConfigurator.PARAM_AXIS2_XML_PATH),
+                "axis2.xml.path should be set when conf/axis2.xml exists");
+        assertTrue(params.get(WarBasedAxisConfigurator.PARAM_AXIS2_XML_PATH)
+                        .endsWith("axis2.xml"),
+                "axis2.xml.path should point to conf/axis2.xml");
+    }
+
+    @Test
+    void emptyRepoProperty_usesGetRealPath(@TempDir Path tempDir) throws 
Exception {
+        Axis2Properties properties = new Axis2Properties();
+        // repo is empty — should fall back to getRealPath
+
+        ServletContext servletContext = mock(ServletContext.class);
+        
when(servletContext.getRealPath("/WEB-INF")).thenReturn(tempDir.toString());
+
+        ServletRegistration.Dynamic axisServlet = 
mock(ServletRegistration.Dynamic.class);
+        when(servletContext.addServlet(eq("AxisServlet"), 
any(org.apache.axis2.transport.http.AxisServlet.class)))
+                .thenReturn(axisServlet);
+        
when(axisServlet.addMapping(anyString())).thenReturn(Collections.emptySet());
+
+        Axis2ServletAutoConfiguration config = new 
Axis2ServletAutoConfiguration();
+        config.axis2ServletInitializer(properties).onStartup(servletContext);
+
+        // Verify getRealPath result was used
+        verify(axisServlet).setInitParameter(
+                eq(WarBasedAxisConfigurator.PARAM_AXIS2_REPOSITORY_PATH),
+                eq(tempDir.toString()));
+    }
+
+    @Test
+    void repoProperty_failsFastWhenAxis2XmlMissing(@TempDir Path tempDir) {
+        // Create repo dir WITHOUT conf/axis2.xml
+        Axis2Properties properties = new Axis2Properties();
+        properties.setRepo(tempDir.toString());
+
+        ServletContext servletContext = mock(ServletContext.class);
+        when(servletContext.getRealPath("/WEB-INF")).thenReturn(null);
+
+        ServletRegistration.Dynamic axisServlet = 
mock(ServletRegistration.Dynamic.class);
+        when(servletContext.addServlet(eq("AxisServlet"), 
any(org.apache.axis2.transport.http.AxisServlet.class)))
+                .thenReturn(axisServlet);
+        
when(axisServlet.addMapping(anyString())).thenReturn(Collections.emptySet());
+
+        Axis2ServletAutoConfiguration config = new 
Axis2ServletAutoConfiguration();
+
+        // Should throw because axis2.repo is set but conf/axis2.xml is missing
+        assertThrows(IllegalStateException.class,
+                () -> 
config.axis2ServletInitializer(properties).onStartup(servletContext),
+                "Should fail fast when axis2.repo is set but axis2.xml is 
missing");
+    }
+
+    @Test
+    void repoProperty_noAxis2XmlPath_whenFileAbsentAndRepoNotSet(@TempDir Path 
tempDir) throws Exception {
+        // getRealPath returns a valid path but conf/axis2.xml doesn't exist
+        // AND axis2.repo is NOT set — this is fine, WarBasedAxisConfigurator
+        // will load from classpath as it always has
+        Axis2Properties properties = new Axis2Properties();
+
+        ServletContext servletContext = mock(ServletContext.class);
+        
when(servletContext.getRealPath("/WEB-INF")).thenReturn(tempDir.toString());
+
+        ServletRegistration.Dynamic axisServlet = 
mock(ServletRegistration.Dynamic.class);
+        when(servletContext.addServlet(eq("AxisServlet"), 
any(org.apache.axis2.transport.http.AxisServlet.class)))
+                .thenReturn(axisServlet);
+        
when(axisServlet.addMapping(anyString())).thenReturn(Collections.emptySet());
+
+        Axis2ServletAutoConfiguration config = new 
Axis2ServletAutoConfiguration();
+        config.axis2ServletInitializer(properties).onStartup(servletContext);
+
+        // Should set repository path but NOT axis2.xml.path (file doesn't 
exist)
+        verify(axisServlet).setInitParameter(
+                eq(WarBasedAxisConfigurator.PARAM_AXIS2_REPOSITORY_PATH),
+                eq(tempDir.toString()));
+        verify(axisServlet, never()).setInitParameter(
+                eq(WarBasedAxisConfigurator.PARAM_AXIS2_XML_PATH), 
anyString());
+    }
+
+    @Test
+    void servletMappedToConfiguredPath() throws Exception {
+        Axis2Properties properties = new Axis2Properties();
+        properties.setServicesPath("/api");
+
+        ServletContext servletContext = mock(ServletContext.class);
+        when(servletContext.getRealPath("/WEB-INF")).thenReturn("/tmp/fake");
+
+        ServletRegistration.Dynamic axisServlet = 
mock(ServletRegistration.Dynamic.class);
+        when(servletContext.addServlet(eq("AxisServlet"), 
any(org.apache.axis2.transport.http.AxisServlet.class)))
+                .thenReturn(axisServlet);
+        
when(axisServlet.addMapping(anyString())).thenReturn(Collections.emptySet());
+
+        Axis2ServletAutoConfiguration config = new 
Axis2ServletAutoConfiguration();
+        config.axis2ServletInitializer(properties).onStartup(servletContext);
+
+        verify(axisServlet).addMapping("/api/*");
+    }
+
+    @Test
+    void servletMappingConflict_throwsException() {
+        Axis2Properties properties = new Axis2Properties();
+
+        ServletContext servletContext = mock(ServletContext.class);
+        when(servletContext.getRealPath("/WEB-INF")).thenReturn("/tmp/fake");
+
+        ServletRegistration.Dynamic axisServlet = 
mock(ServletRegistration.Dynamic.class);
+        when(servletContext.addServlet(eq("AxisServlet"), 
any(org.apache.axis2.transport.http.AxisServlet.class)))
+                .thenReturn(axisServlet);
+        when(axisServlet.addMapping(anyString()))
+                .thenReturn(Collections.singleton("/services/*")); // conflict!
+
+        Axis2ServletAutoConfiguration config = new 
Axis2ServletAutoConfiguration();
+
+        assertThrows(IllegalStateException.class,
+                () -> 
config.axis2ServletInitializer(properties).onStartup(servletContext),
+                "Should throw when servlet mapping conflicts");
+    }
+
+    // ═══════════════════════════════════════════════════════════════════════
+    // Axis2RepositoryAutoConfiguration — axis2.xml staging
+    // ═══════════════════════════════════════════════════════════════════════
+
+    @Test
+    void repoProperty_usedForAxis2XmlStaging(@TempDir Path tempDir) throws 
Exception {
+        // Pre-stage axis2.xml so staging is skipped (it checks for existing 
file)
+        Path confDir = tempDir.resolve("conf");
+        Files.createDirectories(confDir);
+        Files.writeString(confDir.resolve("axis2.xml"), "<axisconfig/>");
+
+        Axis2Properties properties = new Axis2Properties();
+        properties.setRepo(tempDir.toString());
+
+        ServletContext servletContext = mock(ServletContext.class);
+        // getRealPath returns null (embedded mode)
+        when(servletContext.getRealPath("/WEB-INF")).thenReturn(null);
+
+        Axis2RepositoryAutoConfiguration config = new 
Axis2RepositoryAutoConfiguration();
+        // Should not throw — axis2.xml already exists, staging is skipped.
+        // The implicit no-exception check is the assertion: if staging
+        // incorrectly tries to overwrite the existing file and fails,
+        // the test fails with an exception.
+        
config.axis2RepositoryInitializer(properties).onStartup(servletContext);
+    }
+
+    @Test
+    void nullRealPath_andEmptyRepo_logsWarning() throws Exception {
+        Axis2Properties properties = new Axis2Properties();
+        // Both repo and getRealPath are empty/null
+
+        ServletContext servletContext = mock(ServletContext.class);
+        when(servletContext.getRealPath("/WEB-INF")).thenReturn(null);
+
+        Axis2RepositoryAutoConfiguration config = new 
Axis2RepositoryAutoConfiguration();
+        // Should not throw — just logs a warning and skips staging
+        
config.axis2RepositoryInitializer(properties).onStartup(servletContext);
+        // If we got here without exception, the graceful fallback works
+    }
+}
diff --git a/src/site/xdoc/docs/spring-boot-starter.xml 
b/src/site/xdoc/docs/spring-boot-starter.xml
index d6666d4c9e..a416271fde 100644
--- a/src/site/xdoc/docs/spring-boot-starter.xml
+++ b/src/site/xdoc/docs/spring-boot-starter.xml
@@ -34,6 +34,44 @@ from a multi-day configuration project to a single Maven 
dependency. It handles
 servlet registration, repository configuration, and OpenAPI/MCP endpoint 
activation
 with sensible defaults — for both SOAP and JSON-RPC services.</p>
 
+<h3>What Axis2 brings to Spring Boot</h3>
+
+<ul>
+<li><strong>Large JSON streaming (10MB–1GB+)</strong> — the
+    <a href="json-streaming-formatter.html">streaming JSON formatter</a>
+    flushes HTTP/2 DATA frames every 64KB, keeping server memory flat
+    regardless of response size. No reactive stack required.</li>
+<li><strong>Native HTTP/2 with backpressure</strong> — the
+    <a href="http2-transport-additions.html">H2TransportSender</a>
+    provides connection multiplexing, flow control, and ALPN negotiation
+    as a drop-in transport layer.</li>
+<li><strong>AI-ready services (MCP)</strong> — Axis2 auto-generates an
+    <a href="json-rpc-mcp-guide.html">MCP tool catalog</a> from deployed
+    services. AI assistants discover and call your services as tools
+    without hand-authored definitions.</li>
+<li><strong>Response field selection</strong> — the
+    <a 
href="json-streaming-formatter.html#Field_Selection"><code>?fields=</code></a>
+    query parameter lets clients request only the fields they need,
+    reducing payload size and AI token consumption at the serialization
+    layer with zero application code.</li>
+<li><strong>OpenAPI schemas from Hibernate mappings</strong> — the
+    <a href="openapi-jpa-schema.html">JPA/Hibernate schema generator</a>
+    produces read and write JSON Schemas directly from
+    <code>@Entity</code> annotations or <code>.hbm.xml</code> files.
+    No hand-authored API contracts — the schema stays in sync with
+    the database model by construction.</li>
+<li><strong>Built-in pagination</strong> — the
+    <a href="json-pagination.html"><code>PaginatedResponse</code></a>
+    wrapper provides offset/limit pagination with server-enforced
+    safety limits, <code>totalCount</code> for page controls, and
+    <code>hasMore</code> for infinite scroll — mapping directly to
+    JPA/Hibernate's 
<code>setFirstResult</code>/<code>setMaxResults</code>.</li>
+<li><strong>SOAP + JSON from the same services</strong> — serve both
+    SOAP/XML and JSON-RPC from the same Spring beans, selected by
+    configuration. Useful for enterprises maintaining both legacy
+    and modern clients.</li>
+</ul>
+
 <p>This starter supports both <strong>WAR deployment</strong> to an external 
container
 (Tomcat 11, WildFly 32/39) and <strong>embedded Tomcat</strong> for local 
development.
 For embedded mode, set <code>axis2.repo</code> in 
<code>application.properties</code>
@@ -164,13 +202,22 @@ are not tested.</p>
 
 <h3>Embedded mode limitations</h3>
 
-<p>The current embedded Tomcat support requires a pre-built exploded WAR
-on the filesystem (via <code>axis2.repo</code>). A future improvement
-would be a <code>ClasspathBasedAxisConfigurator</code> that discovers
-services and modules directly from the classpath, eliminating the need
-for a filesystem layout entirely. Contributions are welcome — see the
-<a 
href="https://github.com/apache/axis-axis2-java-core/blob/master/AXIS2_MODERNIZATION_PLAN.md";>Axis2
 Modernization Plan</a>
-for the full roadmap.</p>
+<p>Embedded Tomcat requires a pre-built exploded WAR on the filesystem
+(pointed to by <code>axis2.repo</code>). This means you must run
+<code>mvn install</code> before <code>mvn spring-boot:run</code> so
+that the <code>.aar</code> service archives and <code>axis2.xml</code>
+are staged in the build output directory. Changes to service metadata
+(operations, message receivers, MCP descriptions) require a rebuild.</p>
+
+<p>Note that the service <em>code</em> itself is a Spring
+<code>@Component</code> bean on the classpath — the <code>.aar</code>
+file contains only metadata (<code>services.xml</code>) that tells
+Axis2 which Spring bean to call, which operations to expose, and
+which message receivers to use. The <code>SpringBeanName</code>
+parameter in <code>services.xml</code> bridges Axis2 service
+dispatch to Spring's application context. A future improvement
+could scan for <code>services.xml</code> resources on the classpath,
+eliminating the filesystem requirement entirely.</p>
 
 <!-- ============================================================ -->
 <a name="quickstart"/>
@@ -199,10 +246,12 @@ for the full roadmap.</p>
 etc.) and eliminates the need for a hand-coded 
<code>Axis2WebAppInitializer</code>
 or <code>OpenApiServlet</code> class.</p>
 
-<p>The starter auto-registers <code>AxisServlet</code> at 
<code>/services/*</code>
-and — when <code>axis2-openapi</code> is on the classpath — registers the 
OpenAPI
-servlet at <code>/openapi.json</code>, <code>/openapi.yaml</code>,
-<code>/swagger-ui</code>, and <code>/openapi-mcp.json</code>.</p>
+<p>The starter auto-registers <code>AxisServlet</code> at 
<code>/services/*</code>.
+If you also add the optional <code>axis2-openapi</code> dependency (shown 
above),
+the starter additionally registers endpoints for OpenAPI spec generation
+(<code>/openapi.json</code>), Swagger UI (<code>/swagger-ui</code>), and MCP
+tool discovery (<code>/openapi-mcp.json</code>). See
+<a href="#openapi_mcp">Section 7</a> for details.</p>
 
 <!-- ============================================================ -->
 <a name="how_axisservlet_works"/>
@@ -374,20 +423,66 @@ does not already exist. If your Maven build pre-stages it 
(e.g., via
 <a name="openapi_mcp"/>
 <h2>7. OpenAPI and MCP Support</h2>
 
-<p>When <code>axis2-openapi</code> is on the classpath, the starter 
automatically
-registers the OpenAPI servlet. No additional code is needed in the consuming 
app.</p>
+<p><code>axis2-openapi</code> is a separate Axis2 module (Maven artifact:
+<code>org.apache.axis2:axis2-openapi</code>) that auto-generates an
+<a href="https://en.wikipedia.org/wiki/OpenAPI_Specification";>OpenAPI</a>
+3.0.1 specification and
+<a href="https://en.wikipedia.org/wiki/Model_Context_Protocol";>MCP</a>
+(Model Context Protocol) tool catalog from your deployed Axis2 services.
+OpenAPI is a standard format for describing REST APIs — tools like
+<a href="https://swagger.io/tools/swagger-ui/";>Swagger UI</a> use it to
+generate interactive documentation. MCP is a protocol that lets AI
+assistants (Claude, ChatGPT, etc.) discover and call your services
+as tools.</p>
+
+<p>The module auto-generates both from your deployed Axis2 services.
+It reads each service's <code>services.xml</code> (inside the <code>.aar</code>
+file) at runtime — specifically the operation names, message receiver types,
+and MCP metadata parameters — and produces the specification automatically.
+No annotations on your service code are needed.</p>
+
+<p><strong>How it works with the starter:</strong> The starter detects
+<code>axis2-openapi</code> on the classpath via Spring Boot's
+<code>@ConditionalOnClass(OpenApiModule.class)</code> and automatically
+registers a servlet that delegates to Axis2's
+<a 
href="https://github.com/apache/axis-axis2-java-core/blob/master/modules/openapi/src/main/java/org/apache/axis2/openapi/SwaggerUIHandler.java";>SwaggerUIHandler</a>.
+No additional code is needed in your application.</p>
+
+<p>To enable it, add the dependency to your <code>pom.xml</code>:</p>
+
+<pre>
+&lt;dependency&gt;
+    &lt;groupId&gt;org.apache.axis2&lt;/groupId&gt;
+    &lt;artifactId&gt;axis2-openapi&lt;/artifactId&gt;
+    &lt;version&gt;2.0.1&lt;/version&gt;
+&lt;/dependency&gt;
+</pre>
+
+<p>Your Maven build must also copy the <code>axis2-openapi</code> JAR as a
+<code>.mar</code> (Module Archive) into <code>WEB-INF/modules/</code> so the
+Axis2 engine can load it:</p>
+
+<pre>
+&lt;!-- In maven-antrun-plugin, prepare-package phase --&gt;
+&lt;copy 
file="${settings.localRepository}/org/apache/axis2/axis2-openapi/${axis2.version}/axis2-openapi-${axis2.version}.jar"
+      
tofile="${project.build.directory}/${project.build.finalName}/WEB-INF/modules/openapi-${axis2.version}.mar"
+      overwrite="true"/&gt;
+</pre>
 
-<p>The servlet provides:</p>
+<p>Once deployed, the following endpoints are available (no auth required):</p>
 <ul>
-<li><code>GET /openapi.json</code> — OpenAPI 3.0.1 specification</li>
+<li><code>GET /openapi.json</code> — OpenAPI 3.0.1 specification (JSON)</li>
 <li><code>GET /openapi.yaml</code> — same in YAML format</li>
 <li><code>GET /swagger-ui</code> — interactive Swagger UI</li>
-<li><code>GET /openapi-mcp.json</code> — MCP tool catalog for AI 
assistants</li>
+<li><code>GET /openapi-mcp.json</code> — MCP tool catalog for AI assistants
+    (includes operation names, input schemas, and payload templates)</li>
 </ul>
 
-<p>See the <a href="json-rpc-mcp-guide.html">JSON-RPC MCP Integration Guide</a>
-for details on the MCP catalog format, the Axis2 JSON-RPC envelope, and how
-AI assistants discover and call Axis2 services.</p>
+<p>The MCP catalog is generated from <code>services.xml</code> parameters
+like <code>mcpDescription</code> and <code>mcpInputSchema</code>.
+See the <a href="json-rpc-mcp-guide.html">JSON-RPC MCP Integration Guide</a>
+for the full catalog format and how AI assistants discover and call Axis2
+services.</p>
 
 <!-- ============================================================ -->
 <a name="migration"/>

Reply via email to