GAE HTTPS on local DevAppServer in Eclipse using appengine-maven-plugin

I'm a Java/Web newbie and I was hoping someone on here could help me.

I'm building a GAE application using Spring in Eclipse (Juno Windows 7). The project was generated using the Maven plugin for Eclipse (using Maven 3.1.0) and the appengine-maven-plugin for Maven (using the GAE SDK 1.8.3).

The majority of my messages must be sent over HTTPS and I enforce this in my Spring configuration. However, I'm at a loss for how to configure the local DevAppServer to support HTTPS.

I started by creating a certificate

keytool -keystore myKeystore -genkey -keyalg RSA

which I would hope to supply to the server. However, GAE seems to be running inside its own version of Jetty and it's not clear if I can modify it.

I attempted to push it via command line arguments

<plugin>
    <groupId>com.google.appengine</groupId>
    <artifactId>appengine-maven-plugin</artifactId>
    <version>${appengine.target.version}</version>
    <configuration>
        <useJava7>true</useJava7>
        <address>127.0.0.1</address>
        <port>8080</port>
        <compileEncoding>utf-8</compileEncoding>
        <jvmFlags>
            <jvmFlag>-Xdebug</jvmFlag>
            <jvmFlag>-Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=y</jvmFlag>
            <jvmFlag>-Djavax.net.ssl.keyStore=C:\STEVEN-DEV\projects\apps\testserver\src\main\webapp\WEB-INF\myKeystore</jvmFlag>
            <jvmFlag>-Djavax.net.ssl.keyStorePassword=testpass</jvmFlag>
        </jvmFlags>
    </configuration>
</plugin>

I know the keystore is being loaded because I see exceptions if the path is wrong. However, I'm not really sure what it's being loaded into and if that's even what I want to do.

When I debug the app from Eclipse, this appears in my logs after the app is packaged:

Running C:\Program Files\Java\jdk1.7.0_10\jre\bin\java -javaagent:C:\Users\Steven\.m2\repository\com\google\appengine\appengine-java-sdk\1.8.3\appengine-java-sdk\appengine-java-sdk-1.8.3\lib\agent\appengine-agent.jar -Xbootclasspath/p:C:\Users\Steven\.m2\repository\com\google\appengine\appengine-java-sdk\1.8.3\appengine-java-sdk\appengine-java-sdk-1.8.3\lib\override\appengine-dev-jdk-overrides.jar -Dappengine.fullscan.seconds=5 -classpath C:\Users\Steven\.m2\repository\com\google\appengine\appengine-java-sdk\1.8.3\appengine-java-sdk\appengine-java-sdk-1.8.3\lib\appengine-tools-api.jar -Xdebug -Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=y -Djavax.net.ssl.keyStore=C:\STEVEN-DEV\projects\apps\testserver\src\main\webapp\WEB-INF\myKeystore -Djavax.net.ssl.keyStorePassword=testpass com.google.appengine.tools.development.DevAppServerMain --allow_remote_shutdown -a 127.0.0.1 -p 8080 C:\STEVEN-DEV\projects\apps\testserver\target/testserver-0.0.1-SNAPSHOT

If I open chrome and attempt to access

https://localhost:8080/test/retval

I see "SSL Connection Error" and "Error code: ERR_SSL_PROTOCOL_ERROR"

If I try to browse to

http://localhost:8080/test/retval

I will be forwarded to

https://localhost:8443/test/retval

and be told that "This webpage is not available".

Update 2: After taking another look at the docs I found this

The development web server does not support HTTPS connections. It ignores the transport guarantee, so paths intended for use with HTTPS can be tested using regular HTTP connections to the development web server.

So it seems that my problem is that my Spring configuration forces HTTPS. I can take this out and allow any connection type. However, I'm not keen on doing that and would prefer a way to configure/hack the dev server to support SSL.

(Return to old question: What I have been attempting)

I don't know where my error lies: GAE, Maven or Spring.

The following is my complete pom.xml

I've been reading documentation and searching the web for hours. Any help would be much appreciated.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>

    <groupId>com.steven</groupId>
    <artifactId>testserver</artifactId>

    <properties>
        <java.version>1.7</java.version>
        <maven.enforcer.plugin-version>1.3.1</maven.enforcer.plugin-version>
        <maven.version.range>[2.2.1,3.1.0]</maven.version.range>
        <appengine.app.version>1</appengine.app.version>
        <appengine.target.version>1.8.3</appengine.target.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <org.springframework-version>3.2.3.RELEASE</org.springframework-version>
        <org.springframework.security-version>3.1.4.RELEASE</org.springframework.security-version>
        <org.slf4j-version>1.7.5</org.slf4j-version>
        <ch.qos.logback-version>1.0.13</ch.qos.logback-version>
        <org.codehaus.jackson-version>1.9.12</org.codehaus.jackson-version>
    </properties>

    <dependencies>
        <!-- Compile/runtime dependencies -->
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-1.0-sdk</artifactId>
            <version>${appengine.target.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- Test Dependencies -->
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-testing</artifactId>
            <version>${appengine.target.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.google.appengine</groupId>
            <artifactId>appengine-api-stubs</artifactId>
            <version>${appengine.target.version}</version>
            <scope>test</scope>
        </dependency>

        <!-- Spring Core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${org.springframework-version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>

        <!-- Spring Security -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-core</artifactId>
            <version>${org.springframework.security-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${org.springframework.security-version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${org.springframework.security-version}</version>
        </dependency>

        <!-- JSON -->
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>${org.codehaus.jackson-version}</version>
        </dependency>

        <!-- Logging -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${ch.qos.logback-version}</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>${ch.qos.logback-version}</version>
        </dependency>

    </dependencies>

    <repositories>
        <repository>
            <id>springsource-repo</id>
            <name>SpringSource Repository</name>
            <url>http://repo.springsource.org/release</url>
        </repository>
    </repositories>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.eclipse.m2e</groupId>
                    <artifactId>lifecycle-mapping</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <lifecycleMappingMetadata>
                            <pluginExecutions>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>org.apache.maven.plugins</groupId>
                                        <artifactId>maven-enforcer-plugin</artifactId>
                                        <versionRange>[1.0.0,)</versionRange>
                                        <goals>
                                            <goal>enforce</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore />
                                    </action>
                                </pluginExecution>
                            </pluginExecutions>
                        </lifecycleMappingMetadata>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <version>2.5.1</version>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <archiveClasses>true</archiveClasses>
                    <webResources>
                        <!-- in order to interpolate version from pom into appengine-web.xml -->
                        <resource>
                            <directory>${basedir}/src/main/webapp/WEB-INF</directory>
                            <filtering>true</filtering>
                            <targetPath>WEB-INF</targetPath>
                        </resource>
                    </webResources>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>${maven.enforcer.plugin-version}</version>
                <executions>
                    <execution>
                        <id>enforce-banned-dependencies</id>
                        <goals>
                            <goal>enforce</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <bannedDependencies>
                                    <searchTransitive>true</searchTransitive>
                                    <excludes>
                                        <exclude>javassist:javassist</exclude>
                                        <exclude>commons-logging</exclude>
                                        <exclude>aspectj:aspectj*</exclude>
                                        <exclude>hsqldb:hsqldb</exclude>
                                        <exclude>log4j:log4j</exclude>
                                        <exclude>org.slf4j:[1.5, 1.6.5]</exclude>
                                        <exclude>org.springframework:2.*</exclude>
                                        <exclude>org.springframework:3.0.*</exclude>
                                    </excludes>
                                </bannedDependencies>
                                <requireMavenVersion>
                                    <version>${maven.version.range}</version>
                                </requireMavenVersion>
                                <requireJavaVersion>
                                    <version>${java.version}</version>
                                </requireJavaVersion>
                            </rules>
                            <fail>true</fail>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>com.google.appengine</groupId>
                <artifactId>appengine-maven-plugin</artifactId>
                <version>${appengine.target.version}</version>
                <configuration>
                    <useJava7>true</useJava7>
                    <address>127.0.0.1</address>
                    <port>8080</port>
                    <compileEncoding>utf-8</compileEncoding>
                    <jvmFlags>
                        <jvmFlag>-Xdebug</jvmFlag>
                        <jvmFlag>-Xrunjdwp:transport=dt_socket,address=1044,server=y,suspend=y</jvmFlag>
                        <jvmFlag>-Djavax.net.ssl.keyStore=C:\STEVEN-DEV\projects\apps\testserver\src\main\webapp\WEB-INF\myKeystore</jvmFlag>
                        <jvmFlag>-Djavax.net.ssl.keyStorePassword=testpass</jvmFlag>
                    </jvmFlags>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Update: I have tried this with and without security constraints in my web.xml

I can't help but think that I need to find a way to configure the Jetty used by the app engine plugin to work with SSL

<?xml version="1.0" encoding="utf-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <display-name>Test Server</display-name>

    <!-- Spring MVC -->
    <servlet>
        <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/mvc-dispatcher-servlet.xml, /WEB-INF/spring-security.xml</param-value>
    </context-param>

    <!-- Spring Security -->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>test</web-resource-name>
            <url-pattern>/test/*</url-pattern>
        </web-resource-collection>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
</web-app>

Answers


As described in the docs, just add a security-contraint to your src/main/webapp/WEB-INF/web.xml file to force SSL-only on some URLs of your app (e.g. /rest/*):

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
    ...
    <security-constraint>
        <web-resource-collection>
            <url-pattern>/rest/*</url-pattern>
        </web-resource-collection>
        <user-data-constraint>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>
    ...
</web-app>

You should be able to use HTTP and HTTPS locally. I am not sure if the SSL-only constraints are enforced on localhost though. Once you deploy your app to GAE it will be enforced accordingly based on your web.xml settings.


I posted this as a comment in the above answer, but I should put it here as well:

According to the docs

https://developers.google.com/appengine/docs/java/config/webxml#Secure_URLs

"The development web server does not support HTTPS connections. It ignores the transport guarantee, so paths intended for use with HTTPS can be tested using regular HTTP connections to the development web server."


Need Your Help

How do you break a large java application into components?

java spring hibernate service components

I am writing a java web application using spring, hibernate and mysql. The applications is getting larger so I want to break it into smaller parts e.g. smaller projects, components or which ever way

What is the best and fastest technique in .Net for searchin a string into a large file/folder upto 1GB?

c# algorithm file-io

I want to search a string (in every 5 to 10 mints) in text files or folder which might be of 500MB space. I want to know which technique would be fruitful to implement this searching. Please let me...