CAY-2076 Implement Jetty HTTP/1.1 and HTTP/2 Client support for ROP Client
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/34646fd5 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/34646fd5 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/34646fd5 Branch: refs/heads/master Commit: 34646fd5baf3640208399b2f21afc7afff6def51 Parents: edef6ae Author: Savva Kolbachev <s.kolbac...@gmail.com> Authored: Tue Jun 28 14:59:07 2016 +0300 Committer: Savva Kolbachev <s.kolbac...@gmail.com> Committed: Tue Jun 28 14:59:07 2016 +0300 ---------------------------------------------------------------------- cayenne-client-jetty/pom.xml | 165 ++++++++++++++++ .../rop/client/ClientJettyHttp2Module.java | 45 +++++ .../rop/client/ClientJettyHttpModule.java | 40 ++++ .../rop/JettyHttp2ClientConnectionProvider.java | 59 ++++++ .../rop/JettyHttpClientConnectionProvider.java | 128 +++++++++++++ .../cayenne/rop/http/JettyHttpROPConnector.java | 189 +++++++++++++++++++ .../rop/http/JettyHttpROPConnectorIT.java | 134 +++++++++++++ .../apache/cayenne/util/Http2TestServer.java | 127 +++++++++++++ .../apache/cayenne/configuration/Constants.java | 8 + docs/doc/src/main/resources/RELEASE-NOTES.txt | 1 + pom.xml | 1 + tutorials/pom.xml | 13 ++ tutorials/tutorial-rop-client-http2/pom.xml | 62 ++++++ .../apache/cayenne/tutorial/Http2Client.java | 127 +++++++++++++ .../tutorial/persistent/client/Artist.java | 12 ++ .../tutorial/persistent/client/Gallery.java | 12 ++ .../tutorial/persistent/client/Painting.java | 12 ++ .../persistent/client/auto/_Artist.java | 96 ++++++++++ .../persistent/client/auto/_Gallery.java | 72 +++++++ .../persistent/client/auto/_Painting.java | 98 ++++++++++ .../src/main/resources/keystore | Bin 0 -> 3697 bytes tutorials/tutorial-rop-server-http2/pom.xml | 85 +++++++++ .../cayenne/tutorial/Http2ROPServlet.java | 66 +++++++ .../apache/cayenne/tutorial/Http2Server.java | 107 +++++++++++ .../cayenne/tutorial/persistent/Artist.java | 9 + .../cayenne/tutorial/persistent/Gallery.java | 9 + .../cayenne/tutorial/persistent/Painting.java | 9 + .../tutorial/persistent/auto/_Artist.java | 52 +++++ .../tutorial/persistent/auto/_Gallery.java | 43 +++++ .../tutorial/persistent/auto/_Painting.java | 49 +++++ .../tutorial/persistent/client/Artist.java | 12 ++ .../tutorial/persistent/client/Gallery.java | 12 ++ .../tutorial/persistent/client/Painting.java | 12 ++ .../persistent/client/auto/_Artist.java | 96 ++++++++++ .../persistent/client/auto/_Gallery.java | 72 +++++++ .../persistent/client/auto/_Painting.java | 98 ++++++++++ .../src/main/resources/cayenne-project.xml | 17 ++ .../src/main/resources/datamap.map.xml | 50 +++++ .../src/main/resources/keystore | Bin 0 -> 3697 bytes tutorials/tutorial-rop-server/pom.xml | 4 + 40 files changed, 2203 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/pom.xml ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/pom.xml b/cayenne-client-jetty/pom.xml new file mode 100644 index 0000000..ea9b55b --- /dev/null +++ b/cayenne-client-jetty/pom.xml @@ -0,0 +1,165 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. --> +<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> + + <parent> + <artifactId>cayenne-parent</artifactId> + <groupId>org.apache.cayenne</groupId> + <version>4.0.M4-SNAPSHOT</version> + </parent> + + <artifactId>cayenne-client-jetty</artifactId> + <name>Cayenne ROP Client (Jetty)</name> + <packaging>jar</packaging> + + <properties> + <jetty.version>9.3.10.v20160621</jetty.version> + </properties> + + <dependencyManagement> + <dependencies> + <!-- Jetty client --> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-client</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-client</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-http-client-transport</artifactId> + <version>${jetty.version}</version> + </dependency> + + <!-- Jetty server test dependencies --> + <dependency> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-server</artifactId> + <version>${jetty.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <version>${jetty.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlets</artifactId> + <version>${jetty.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <!-- Compile dependencies --> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-client</artifactId> + <version>${project.version}</version> + </dependency> + + <!-- Jetty client --> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-client</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-client</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-http-client-transport</artifactId> + </dependency> + + <!--Test dependencies--> + <dependency> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-server</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlets</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <artifactId>maven-remote-resources-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>process</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-failsafe-plugin</artifactId> + </plugin> + </plugins> + </build> + + <profiles> + <profile> + <id>code-quality</id> + + <activation> + <property> + <name>!fast-and-dirty</name> + </property> + </activation> + <build> + <plugins> + <plugin> + <artifactId>maven-checkstyle-plugin</artifactId> + <!--<configuration> + <suppressionsLocation>${project.basedir}/cayenne-checkstyle-suppression.xml</suppressionsLocation> + </configuration>--> + </plugin> + <plugin> + <artifactId>maven-pmd-plugin</artifactId> + </plugin> + </plugins> + </build> + </profile> + </profiles> +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttp2Module.java ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttp2Module.java b/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttp2Module.java new file mode 100644 index 0000000..22a8751 --- /dev/null +++ b/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttp2Module.java @@ -0,0 +1,45 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.configuration.rop.client; + +import org.apache.cayenne.configuration.Constants; +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.remote.ClientConnection; +import org.apache.cayenne.rop.JettyHttp2ClientConnectionProvider; +import org.apache.cayenne.rop.http.JettyHttpROPConnector; + +/** + * This is HTTP/2 implementation of ROP Client. + * <p> + * This module uses {@link JettyHttpROPConnector} initialized by {@link JettyHttp2ClientConnectionProvider} + * without ALPN by default. + * <p> + * In order to use it with ALPN you have to set {@link Constants#ROP_SERVICE_USE_ALPN_PROPERTY} to true + * and provide the alpn-boot-XXX.jar into the bootstrap classpath. + */ +public class ClientJettyHttp2Module implements Module { + + @Override + public void configure(Binder binder) { + binder.bind(ClientConnection.class).toProvider(JettyHttp2ClientConnectionProvider.class); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttpModule.java ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttpModule.java b/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttpModule.java new file mode 100644 index 0000000..76b5598 --- /dev/null +++ b/cayenne-client-jetty/src/main/java/org/apache/cayenne/configuration/rop/client/ClientJettyHttpModule.java @@ -0,0 +1,40 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.configuration.rop.client; + +import org.apache.cayenne.di.Binder; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.remote.ClientConnection; +import org.apache.cayenne.rop.JettyHttpClientConnectionProvider; +import org.apache.cayenne.rop.http.JettyHttpROPConnector; + +/** + * This is HTTP/1.1 implementation of ROP Client. + * <p> + * This module uses {@link JettyHttpROPConnector} initialized by {@link JettyHttpClientConnectionProvider}. + */ +public class ClientJettyHttpModule implements Module { + + @Override + public void configure(Binder binder) { + binder.bind(ClientConnection.class).toProvider(JettyHttpClientConnectionProvider.class); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttp2ClientConnectionProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttp2ClientConnectionProvider.java b/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttp2ClientConnectionProvider.java new file mode 100644 index 0000000..b1411e9 --- /dev/null +++ b/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttp2ClientConnectionProvider.java @@ -0,0 +1,59 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.rop; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.configuration.Constants; +import org.apache.cayenne.di.Provider; +import org.apache.cayenne.remote.ClientConnection; +import org.apache.cayenne.rop.http.JettyHttpROPConnector; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http2.client.HTTP2Client; +import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +/** + * This {@link Provider} initializes HTTP/2 {@link ClientConnection} through {@link JettyHttpROPConnector} which uses + * {@link org.eclipse.jetty.client.HttpClient} over {@link org.eclipse.jetty.http2.client.HTTP2Client}. + * It works without ALPN by default. + * <p> + * In order to use it with ALPN you have to set {@link Constants#ROP_SERVICE_USE_ALPN_PROPERTY} to true + * and provide the alpn-boot-XXX.jar into the bootstrap classpath. + */ +public class JettyHttp2ClientConnectionProvider extends JettyHttpClientConnectionProvider { + + @Override + protected HttpClient initJettyHttpClient() { + try { + HttpClientTransportOverHTTP2 http2 = new HttpClientTransportOverHTTP2(new HTTP2Client()); + + boolean useALPN = runtimeProperties.getBoolean(Constants.ROP_SERVICE_USE_ALPN_PROPERTY, false); + http2.setUseALPN(useALPN); + + HttpClient httpClient = new HttpClient(http2, new SslContextFactory()); + httpClient.start(); + + return httpClient; + } catch (Exception e) { + throw new CayenneRuntimeException("Exception while starting Jetty HttpClient over HTTP/2.", e); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttpClientConnectionProvider.java ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttpClientConnectionProvider.java b/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttpClientConnectionProvider.java new file mode 100644 index 0000000..c430c79 --- /dev/null +++ b/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/JettyHttpClientConnectionProvider.java @@ -0,0 +1,128 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.rop; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.ConfigurationException; +import org.apache.cayenne.configuration.Constants; +import org.apache.cayenne.configuration.RuntimeProperties; +import org.apache.cayenne.di.DIRuntimeException; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.di.Provider; +import org.apache.cayenne.remote.ClientConnection; +import org.apache.cayenne.rop.http.JettyHttpROPConnector; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.AuthenticationStore; +import org.eclipse.jetty.client.util.BasicAuthentication; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import java.net.URI; + +/** + * This {@link Provider} initializes HTTP/1.1 {@link ClientConnection} through {@link JettyHttpROPConnector} which uses + * {@link org.eclipse.jetty.client.HttpClient}. + */ +public class JettyHttpClientConnectionProvider implements Provider<ClientConnection> { + + private static Log logger = LogFactory.getLog(JettyHttpROPConnector.class); + + @Inject + protected RuntimeProperties runtimeProperties; + + @Inject + protected ROPSerializationService serializationService; + + @Override + public ClientConnection get() throws DIRuntimeException { + String sharedSession = runtimeProperties + .get(Constants.ROP_SERVICE_SHARED_SESSION_PROPERTY); + + JettyHttpROPConnector ropConnector = createJettyHttpRopConnector(); + ProxyRemoteService remoteService = new ProxyRemoteService(serializationService, ropConnector); + + HttpClientConnection clientConnection = new HttpClientConnection(remoteService, sharedSession); + ropConnector.setClientConnection(clientConnection); + + return clientConnection; + } + + protected JettyHttpROPConnector createJettyHttpRopConnector() { + String url = runtimeProperties.get(Constants.ROP_SERVICE_URL_PROPERTY); + if (url == null) { + throw new ConfigurationException( + "No property defined for '%s', can't initialize connection", + Constants.ROP_SERVICE_URL_PROPERTY); + } + + String username = runtimeProperties.get(Constants.ROP_SERVICE_USERNAME_PROPERTY); + long readTimeout = runtimeProperties.getLong( + Constants.ROP_SERVICE_TIMEOUT_PROPERTY, + -1L); + + HttpClient httpClient = initJettyHttpClient(); + + addBasicAuthentication(httpClient, url, username); + + JettyHttpROPConnector result = new JettyHttpROPConnector(httpClient, url, username); + + if (readTimeout > 0) { + result.setReadTimeout(readTimeout); + } + + return result; + } + + protected HttpClient initJettyHttpClient() { + try { + HttpClient httpClient = new HttpClient(new SslContextFactory()); + httpClient.start(); + + return httpClient; + } catch (Exception e) { + throw new CayenneRuntimeException("Exception while starting Jetty HttpClient.", e); + } + } + + protected void addBasicAuthentication(HttpClient httpClient, String url, String username) { + String password = runtimeProperties.get(Constants.ROP_SERVICE_PASSWORD_PROPERTY); + String realm = runtimeProperties.get(Constants.ROP_SERVICE_REALM_PROPERTY); + + if (username != null && password != null) { + if (realm == null && logger.isWarnEnabled()) { + logger.warn("In order to use JettyClient with BASIC Authentication " + + "you should provide Constants.ROP_SERVICE_REALM_PROPERTY."); + return; + } + + if (logger.isInfoEnabled()) { + logger.info( + "Adding authentication" + + "\nUser: " + username + + "\nRealm: " + realm); + } + + AuthenticationStore auth = httpClient.getAuthenticationStore(); + auth.addAuthentication(new BasicAuthentication(URI.create(url), realm, username, password)); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/http/JettyHttpROPConnector.java ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/http/JettyHttpROPConnector.java b/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/http/JettyHttpROPConnector.java new file mode 100644 index 0000000..f2c0005 --- /dev/null +++ b/cayenne-client-jetty/src/main/java/org/apache/cayenne/rop/http/JettyHttpROPConnector.java @@ -0,0 +1,189 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.rop.http; + +import org.apache.cayenne.remote.ClientConnection; +import org.apache.cayenne.remote.RemoteSession; +import org.apache.cayenne.rop.*; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.util.BytesContentProvider; +import org.eclipse.jetty.client.util.InputStreamResponseListener; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; + +/** + * This implementation of ROPConnector uses Jetty HTTP Client. + * Depends on {@link ClientConnection} provider it uses HTTP/1.1 or HTTP/2 protocol. + * <p> + * {@link JettyHttpClientConnectionProvider} for HTTP/1.1 protocol. + * {@link JettyHttp2ClientConnectionProvider} for HTTP/2 protocol. + */ +public class JettyHttpROPConnector implements ROPConnector { + + private static Log logger = LogFactory.getLog(JettyHttpROPConnector.class); + + public static final String SESSION_COOKIE_NAME = "JSESSIONID"; + + protected HttpClient httpClient; + protected HttpClientConnection clientConnection; + + protected String url; + protected String username; + + protected Long readTimeout = 5l; + + public JettyHttpROPConnector(HttpClient httpClient, String url, String username) { + if (httpClient == null) + throw new IllegalArgumentException("org.eclipse.jetty.client.HttpClient should be provided " + + "for this ROPConnector implementation."); + + this.httpClient = httpClient; + this.url = url; + this.username = username; + } + + public void setClientConnection(HttpClientConnection clientConnection) { + this.clientConnection = clientConnection; + } + + public void setReadTimeout(Long readTimeout) { + this.readTimeout = readTimeout; + } + + @Override + public InputStream establishSession() throws IOException { + if (logger.isInfoEnabled()) { + logger.info(ROPUtil.getLogConnect(url, username, true)); + } + + try { + ContentResponse response = httpClient.newRequest(url) + .method(HttpMethod.POST) + .param(ROPConstants.OPERATION_PARAMETER, ROPConstants.ESTABLISH_SESSION_OPERATION) + .timeout(readTimeout, TimeUnit.SECONDS) + .send(); + + return new ByteArrayInputStream(response.getContent()); + } catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + + throw new IOException("Exception while establishing session", e); + } + } + + @Override + public InputStream establishSharedSession(String sharedSessionName) throws IOException { + if (logger.isInfoEnabled()) { + logger.info(ROPUtil.getLogConnect(url, username, true, sharedSessionName)); + } + + try { + ContentResponse response = httpClient.newRequest(url) + .method(HttpMethod.POST) + .param(ROPConstants.OPERATION_PARAMETER, ROPConstants.ESTABLISH_SHARED_SESSION_OPERATION) + .param(ROPConstants.SESSION_NAME_PARAMETER, sharedSessionName) + .timeout(readTimeout, TimeUnit.SECONDS) + .send(); + + return new ByteArrayInputStream(response.getContent()); + } catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + + throw new IOException("Exception while establishing shared session: " + sharedSessionName, e); + } + } + + @Override + public InputStream sendMessage(byte[] message) throws IOException { + try { + Request request = httpClient.newRequest(url) + .method(HttpMethod.POST) + .header(HttpHeader.CONTENT_TYPE, "application/octet-stream") + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .content(new BytesContentProvider(message)); + + addSessionCookie(request); + + InputStreamResponseListener listener = new InputStreamResponseListener(); + request.send(listener); + + /** + * Waits for the given timeout for the response to be available, then returns it. + * The wait ends as soon as all the HTTP headers have been received, without waiting for the content. + */ + Response response = listener.get(readTimeout, TimeUnit.SECONDS); + + if (response.getStatus() >= 300) { + throw new IOException( + "Did not receive successful HTTP response: status code = " + response.getStatus() + + ", status message = [" + response.getReason() + "]"); + } + + return listener.getInputStream(); + } catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + + throw new IOException("Exception while sending message", e); + } + } + + @Override + public void close() throws IOException { + if (httpClient != null) { + if (logger.isInfoEnabled()) { + logger.info(ROPUtil.getLogDisconnect(url, username, true)); + } + + try { + httpClient.stop(); + } catch (Exception e) { + throw new IOException("Exception while stopping Jetty HttpClient", e); + } + } + } + + protected void addSessionCookie(Request request) { + if (clientConnection != null) { + RemoteSession session = clientConnection.getSession(); + + if (session != null && session.getSessionId() != null) { + request.header(HttpHeader.COOKIE, SESSION_COOKIE_NAME + + "=" + + session.getSessionId()); + } + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/src/test/java/org/apache/cayenne/rop/http/JettyHttpROPConnectorIT.java ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/src/test/java/org/apache/cayenne/rop/http/JettyHttpROPConnectorIT.java b/cayenne-client-jetty/src/test/java/org/apache/cayenne/rop/http/JettyHttpROPConnectorIT.java new file mode 100644 index 0000000..163e6b1 --- /dev/null +++ b/cayenne-client-jetty/src/test/java/org/apache/cayenne/rop/http/JettyHttpROPConnectorIT.java @@ -0,0 +1,134 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.rop.http; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.rop.ROPConstants; +import org.apache.cayenne.util.Http2TestServer; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.http2.client.HTTP2Client; +import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.*; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class JettyHttpROPConnectorIT { + + private static final String MESSAGE = "test message"; + private static final String SEND_MESSAGE_SESSION = "send message session"; + + private static JettyHttpROPConnector ropConnector; + private static Http2TestServer server; + + @BeforeClass + public static void setUpClass() throws Exception { + // Start the test server + class TestServlet extends HttpServlet { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String sharedSessionName = req.getParameter(ROPConstants.SESSION_NAME_PARAMETER); + + if (sharedSessionName == null) { + resp.getOutputStream().write(MESSAGE.getBytes()); + } else if (sharedSessionName.equals(SEND_MESSAGE_SESSION)) { + resp.getOutputStream().write(toByteArray(req.getInputStream())); + } else { + resp.getOutputStream().write((MESSAGE + " " + sharedSessionName).getBytes()); + } + } + } + + server = Http2TestServer.servlet(new TestServlet()).start(); + + ropConnector = new JettyHttpROPConnector(initJettyHttp2Client(), server.getBasePath(), null); + } + + protected static HttpClient initJettyHttp2Client() { + try { + HttpClientTransportOverHTTP2 http2 = new HttpClientTransportOverHTTP2(new HTTP2Client()); + http2.setUseALPN(false); + + HttpClient httpClient = new HttpClient(http2, new SslContextFactory()); + httpClient.start(); + + return httpClient; + } catch (Exception e) { + throw new CayenneRuntimeException("Exception while starting Jetty HttpClient over HTTP/2.", e); + } + } + + @AfterClass + public static void tearDownClass() throws Exception { + server.stop(); + ropConnector.close(); + } + + @Test + public void testEstablishSession() throws Exception { + String message = read(ropConnector.establishSession()); + assertEquals(MESSAGE, message); + } + + @Test + public void testEstablishSharedSession() throws Exception { + String sharedSessionName = "test session"; + String message = read(ropConnector.establishSharedSession(sharedSessionName)); + assertEquals(MESSAGE + " " + sharedSessionName, message); + } + + @Test + public void sendMessage() throws Exception { + ropConnector.establishSharedSession(SEND_MESSAGE_SESSION); + + byte[] message = toByteArray(ropConnector.sendMessage(MESSAGE.getBytes())); + assertArrayEquals(MESSAGE.getBytes(), message); + } + + private static String read(InputStream input) throws IOException { + try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) { + return buffer.lines().collect(Collectors.joining("\n")); + } + } + + private static byte[] toByteArray(InputStream inputStream) throws IOException { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + + int reads = inputStream.read(); + while (reads != -1) { + baos.write(reads); + reads = inputStream.read(); + } + + return baos.toByteArray(); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-client-jetty/src/test/java/org/apache/cayenne/util/Http2TestServer.java ---------------------------------------------------------------------- diff --git a/cayenne-client-jetty/src/test/java/org/apache/cayenne/util/Http2TestServer.java b/cayenne-client-jetty/src/test/java/org/apache/cayenne/util/Http2TestServer.java new file mode 100644 index 0000000..5e6bb35 --- /dev/null +++ b/cayenne-client-jetty/src/test/java/org/apache/cayenne/util/Http2TestServer.java @@ -0,0 +1,127 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.util; + +import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.thread.QueuedThreadPool; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.function.BiConsumer; + +public class Http2TestServer { + + private final String path; + private final Server server; + private final ServerConnector connector; + + public static TestServerBuilder servlet(HttpServlet servlet) { + return new TestServerBuilder(servlet, "/", 0); + } + + public static TestServerBuilder handler(BiConsumer<HttpServletRequest, HttpServletResponse> handler) { + + HttpServlet servlet = new HttpServlet() { + private static final long serialVersionUID = -7741340028518626628L; + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) { + handler.accept(req, resp); + } + }; + + return servlet(servlet); + } + + + public Http2TestServer(HttpServlet servlet, String path, int port) { + this.path = path; + + QueuedThreadPool serverExecutor = new QueuedThreadPool(); + serverExecutor.setName("server"); + + server = new Server(serverExecutor); + connector = new ServerConnector(server, 1, 1, new HTTP2ServerConnectionFactory(new HttpConfiguration())); + connector.setPort(port); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(server, "/", true, false); + context.addServlet(new ServletHolder(servlet), path); + } + + void start() { + try { + server.start(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public int getLocalPort() { + return connector.getLocalPort(); + } + + + public String getBasePath() { + return "http://localhost:" + getLocalPort() + path; + } + + + public static class TestServerBuilder { + private final HttpServlet servlet; + private final String path; + private final int port; + + private TestServerBuilder(HttpServlet servlet, String path, int port) { + this.servlet = servlet; + this.path = path; + this.port = port; + } + + public TestServerBuilder path(String path) { + return new TestServerBuilder(this.servlet, path, this.port); + } + + public TestServerBuilder port(int port) { + return new TestServerBuilder(this.servlet, this.path, port); + } + + + public Http2TestServer start() { + Http2TestServer http2Server = new Http2TestServer(servlet, path, port); + http2Server.start(); + return http2Server; + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java index e49f434..6742863 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/Constants.java @@ -152,6 +152,14 @@ public interface Constants { public static final String ROP_SERVICE_PASSWORD_PROPERTY = "cayenne.rop.service_password"; + public static final String ROP_SERVICE_REALM_PROPERTY = "cayenne.rop.service_realm"; + + /** + * A boolean property that defines whether ALPN should be used. + * Possible values are "true" or "false". + */ + public static final String ROP_SERVICE_USE_ALPN_PROPERTY = "cayenne.rop.service_use_alpn"; + public static final String ROP_SERVICE_SHARED_SESSION_PROPERTY = "cayenne.rop.shared_session_name"; public static final String ROP_SERVICE_TIMEOUT_PROPERTY = "cayenne.rop.service_timeout"; http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/docs/doc/src/main/resources/RELEASE-NOTES.txt ---------------------------------------------------------------------- diff --git a/docs/doc/src/main/resources/RELEASE-NOTES.txt b/docs/doc/src/main/resources/RELEASE-NOTES.txt index 6cf687e..88cabdd 100644 --- a/docs/doc/src/main/resources/RELEASE-NOTES.txt +++ b/docs/doc/src/main/resources/RELEASE-NOTES.txt @@ -21,6 +21,7 @@ CAY-2063 ProcedureCall fluent query API CAY-2065 Pluggable serialization and connectivity layers for ROP CAY-2073 Ordering.orderedList methods CAY-2074 Support for catalogs in stored procedures +CAY-2076 Implement Jetty HTTP/1.1 and HTTP/2 Client support for ROP Client CAY-2083 Implement Protostuff as serialization service for Cayenne ROP CAY-2090 Untangle HttpRemoteService from ServiceContext thread local setup http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index a68e6b5..16bd539 100644 --- a/pom.xml +++ b/pom.xml @@ -1457,6 +1457,7 @@ </activation> <modules> <module>cayenne-java8</module> + <module>cayenne-client-jetty</module> <module>cayenne-protostuff</module> <module>assembly</module> </modules> http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/pom.xml ---------------------------------------------------------------------- diff --git a/tutorials/pom.xml b/tutorials/pom.xml index 17b4c06..fc467ab 100644 --- a/tutorials/pom.xml +++ b/tutorials/pom.xml @@ -35,4 +35,17 @@ <module>tutorial-rop-server</module> <module>tutorial-rop-client</module> </modules> + + <profiles> + <profile> + <id>cayenne-java8-module-to-build</id> + <activation> + <jdk>[1.8,)</jdk> + </activation> + <modules> + <module>tutorial-rop-server-http2</module> + <module>tutorial-rop-client-http2</module> + </modules> + </profile> + </profiles> </project> http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/pom.xml ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/pom.xml b/tutorials/tutorial-rop-client-http2/pom.xml new file mode 100644 index 0000000..15e57de --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/pom.xml @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<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/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>cayenne-tutorials-parent</artifactId> + <groupId>org.apache.cayenne.tutorials</groupId> + <version>4.0.M4-SNAPSHOT</version> + </parent> + + <artifactId>tutorial-rop-client-http2</artifactId> + <name>Cayenne ROP HTTP/2 Client Tutorial</name> + <packaging>jar</packaging> + + <properties> + <main.class>org.apache.cayenne.tutorial.Http2Client</main.class> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-client-jetty</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-protostuff</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-java8</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/Http2Client.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/Http2Client.java b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/Http2Client.java new file mode 100644 index 0000000..97b589e --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/Http2Client.java @@ -0,0 +1,127 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.tutorial; + +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.configuration.Constants; +import org.apache.cayenne.configuration.rop.client.ClientJettyHttp2Module; +import org.apache.cayenne.configuration.rop.client.ClientRuntime; +import org.apache.cayenne.configuration.rop.client.ProtostuffModule; +import org.apache.cayenne.java8.CayenneJava8Module; +import org.apache.cayenne.query.ObjectSelect; +import org.apache.cayenne.rop.JettyHttp2ClientConnectionProvider; +import org.apache.cayenne.rop.http.JettyHttpROPConnector; +import org.apache.cayenne.rop.protostuff.ProtostuffROPSerializationService; +import org.apache.cayenne.tutorial.persistent.client.Artist; +import org.apache.cayenne.tutorial.persistent.client.Gallery; +import org.apache.cayenne.tutorial.persistent.client.Painting; + +import javax.net.ssl.HttpsURLConnection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This example uses {@link ProtostuffROPSerializationService} which is bound in {@link ProtostuffModule} + * and {@link JettyHttpROPConnector} initialized by {@link JettyHttp2ClientConnectionProvider}, which is bound + * in {@link ClientJettyHttp2Module}. It works without ALPN by default. + * <p> + * In order to run it with ALPN, you have to set {@link Constants#ROP_SERVICE_USE_ALPN_PROPERTY} to true + * and provide the alpn-boot-XXX.jar into the bootstrap classpath. + */ +public class Http2Client { + + public static void main(String[] args) throws Exception { + HttpsURLConnection.setDefaultHostnameVerifier((hostname, sslSession) -> hostname.equals("localhost")); + System.setProperty("javax.net.ssl.trustStore", Http2Client.class.getResource("/keystore").getPath()); + + // Setting Protostuff properties + System.setProperty("protostuff.runtime.collection_schema_on_repeated_fields", "true"); + System.setProperty("protostuff.runtime.morph_collection_interfaces", "true"); + System.setProperty("protostuff.runtime.morph_map_interfaces", "true"); + System.setProperty("protostuff.runtime.pojo_schema_on_collection_fields", "true"); + System.setProperty("protostuff.runtime.pojo_schema_on_map_fields", "true"); + + Map<String, String> properties = new HashMap<>(); + properties.put(Constants.ROP_SERVICE_URL_PROPERTY, "https://localhost:8443/"); + properties.put(Constants.ROP_SERVICE_USE_ALPN_PROPERTY, "false"); + properties.put(Constants.ROP_SERVICE_USERNAME_PROPERTY, "cayenne-user"); + properties.put(Constants.ROP_SERVICE_PASSWORD_PROPERTY, "secret"); + properties.put(Constants.ROP_SERVICE_REALM_PROPERTY, "Cayenne Realm"); + + ClientRuntime runtime = new ClientRuntime(properties, + new ClientJettyHttp2Module(), + new ProtostuffModule(), + new CayenneJava8Module()); + + ObjectContext context = runtime.newContext(); + + newObjectsTutorial(context); + selectTutorial(context); + deleteTutorial(context); + + runtime.shutdown(); + } + + static void newObjectsTutorial(ObjectContext context) { + + // creating new Artist + Artist picasso = context.newObject(Artist.class); + picasso.setName("Pablo Picasso"); + + // Creating other objects + Gallery metropolitan = context.newObject(Gallery.class); + metropolitan.setName("Metropolitan Museum of Art"); + + Painting girl = context.newObject(Painting.class); + girl.setName("Girl Reading at a Table"); + + Painting stein = context.newObject(Painting.class); + stein.setName("Gertrude Stein"); + + // connecting objects together via relationships + picasso.addToPaintings(girl); + picasso.addToPaintings(stein); + + girl.setGallery(metropolitan); + stein.setGallery(metropolitan); + + // saving all the changes above + context.commitChanges(); + } + + static void selectTutorial(ObjectContext context) { + // ObjectSelect examples + List<Painting> paintings1 = ObjectSelect.query(Painting.class).select(context); + + List<Painting> paintings2 = ObjectSelect.query(Painting.class) + .where(Painting.NAME.likeIgnoreCase("gi%")).select(context); + } + + static void deleteTutorial(ObjectContext context) { + // Delete object example + Artist picasso = ObjectSelect.query(Artist.class).where(Artist.NAME.eq("Pablo Picasso")).selectOne(context); + + if (picasso != null) { + context.deleteObjects(picasso); + context.commitChanges(); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Artist.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Artist.java b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Artist.java new file mode 100644 index 0000000..efe03bd --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Artist.java @@ -0,0 +1,12 @@ +package org.apache.cayenne.tutorial.persistent.client; + +import org.apache.cayenne.tutorial.persistent.client.auto._Artist; + +/** + * A persistent class mapped as "Artist" Cayenne entity. + */ +public class Artist extends _Artist { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Gallery.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Gallery.java b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Gallery.java new file mode 100644 index 0000000..54553d9 --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Gallery.java @@ -0,0 +1,12 @@ +package org.apache.cayenne.tutorial.persistent.client; + +import org.apache.cayenne.tutorial.persistent.client.auto._Gallery; + +/** + * A persistent class mapped as "Gallery" Cayenne entity. + */ +public class Gallery extends _Gallery { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Painting.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Painting.java b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Painting.java new file mode 100644 index 0000000..2084706 --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/Painting.java @@ -0,0 +1,12 @@ +package org.apache.cayenne.tutorial.persistent.client; + +import org.apache.cayenne.tutorial.persistent.client.auto._Painting; + +/** + * A persistent class mapped as "Painting" Cayenne entity. + */ +public class Painting extends _Painting { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Artist.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Artist.java b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Artist.java new file mode 100644 index 0000000..d98d444 --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Artist.java @@ -0,0 +1,96 @@ +package org.apache.cayenne.tutorial.persistent.client.auto; + +import java.time.LocalDate; +import java.util.List; + +import org.apache.cayenne.PersistentObject; +import org.apache.cayenne.exp.Property; +import org.apache.cayenne.tutorial.persistent.client.Painting; +import org.apache.cayenne.util.PersistentObjectList; + +/** + * A generated persistent class mapped as "Artist" Cayenne entity. It is a good idea to + * avoid changing this class manually, since it will be overwritten next time code is + * regenerated. If you need to make any customizations, put them in a subclass. + */ +public abstract class _Artist extends PersistentObject { + + public static final Property<LocalDate> DATE_OF_BIRTH = new Property<LocalDate>("dateOfBirth"); + public static final Property<String> NAME = new Property<String>("name"); + public static final Property<List<Painting>> PAINTINGS = new Property<List<Painting>>("paintings"); + + protected LocalDate dateOfBirth; + protected String name; + protected List<Painting> paintings; + + public LocalDate getDateOfBirth() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "dateOfBirth", false); + } + + return dateOfBirth; + } + public void setDateOfBirth(LocalDate dateOfBirth) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "dateOfBirth", false); + } + + Object oldValue = this.dateOfBirth; + // notify objectContext about simple property change + if(objectContext != null) { + objectContext.propertyChanged(this, "dateOfBirth", oldValue, dateOfBirth); + } + + this.dateOfBirth = dateOfBirth; + } + + public String getName() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "name", false); + } + + return name; + } + public void setName(String name) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "name", false); + } + + Object oldValue = this.name; + // notify objectContext about simple property change + if(objectContext != null) { + objectContext.propertyChanged(this, "name", oldValue, name); + } + + this.name = name; + } + + public List<Painting> getPaintings() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "paintings", true); + } else if (this.paintings == null) { + this.paintings = new PersistentObjectList(this, "paintings"); + } + + return paintings; + } + public void addToPaintings(Painting object) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "paintings", true); + } else if (this.paintings == null) { + this.paintings = new PersistentObjectList(this, "paintings"); + } + + this.paintings.add(object); + } + public void removeFromPaintings(Painting object) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "paintings", true); + } else if (this.paintings == null) { + this.paintings = new PersistentObjectList(this, "paintings"); + } + + this.paintings.remove(object); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Gallery.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Gallery.java b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Gallery.java new file mode 100644 index 0000000..6189902 --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Gallery.java @@ -0,0 +1,72 @@ +package org.apache.cayenne.tutorial.persistent.client.auto; + +import java.util.List; + +import org.apache.cayenne.PersistentObject; +import org.apache.cayenne.exp.Property; +import org.apache.cayenne.tutorial.persistent.client.Painting; +import org.apache.cayenne.util.PersistentObjectList; + +/** + * A generated persistent class mapped as "Gallery" Cayenne entity. It is a good idea to + * avoid changing this class manually, since it will be overwritten next time code is + * regenerated. If you need to make any customizations, put them in a subclass. + */ +public abstract class _Gallery extends PersistentObject { + + public static final Property<String> NAME = new Property<String>("name"); + public static final Property<List<Painting>> PAINTINGS = new Property<List<Painting>>("paintings"); + + protected String name; + protected List<Painting> paintings; + + public String getName() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "name", false); + } + + return name; + } + public void setName(String name) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "name", false); + } + + Object oldValue = this.name; + // notify objectContext about simple property change + if(objectContext != null) { + objectContext.propertyChanged(this, "name", oldValue, name); + } + + this.name = name; + } + + public List<Painting> getPaintings() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "paintings", true); + } else if (this.paintings == null) { + this.paintings = new PersistentObjectList(this, "paintings"); + } + + return paintings; + } + public void addToPaintings(Painting object) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "paintings", true); + } else if (this.paintings == null) { + this.paintings = new PersistentObjectList(this, "paintings"); + } + + this.paintings.add(object); + } + public void removeFromPaintings(Painting object) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "paintings", true); + } else if (this.paintings == null) { + this.paintings = new PersistentObjectList(this, "paintings"); + } + + this.paintings.remove(object); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Painting.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Painting.java b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Painting.java new file mode 100644 index 0000000..23c14e2 --- /dev/null +++ b/tutorials/tutorial-rop-client-http2/src/main/java/org/apache/cayenne/tutorial/persistent/client/auto/_Painting.java @@ -0,0 +1,98 @@ +package org.apache.cayenne.tutorial.persistent.client.auto; + +import org.apache.cayenne.PersistentObject; +import org.apache.cayenne.ValueHolder; +import org.apache.cayenne.exp.Property; +import org.apache.cayenne.tutorial.persistent.client.Artist; +import org.apache.cayenne.tutorial.persistent.client.Gallery; +import org.apache.cayenne.util.PersistentObjectHolder; + +/** + * A generated persistent class mapped as "Painting" Cayenne entity. It is a good idea to + * avoid changing this class manually, since it will be overwritten next time code is + * regenerated. If you need to make any customizations, put them in a subclass. + */ +public abstract class _Painting extends PersistentObject { + + public static final Property<String> NAME = new Property<String>("name"); + public static final Property<Artist> ARTIST = new Property<Artist>("artist"); + public static final Property<Gallery> GALLERY = new Property<Gallery>("gallery"); + + protected String name; + protected ValueHolder artist; + protected ValueHolder gallery; + + public String getName() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "name", false); + } + + return name; + } + public void setName(String name) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "name", false); + } + + Object oldValue = this.name; + // notify objectContext about simple property change + if(objectContext != null) { + objectContext.propertyChanged(this, "name", oldValue, name); + } + + this.name = name; + } + + public Artist getArtist() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "artist", true); + } else if (this.artist == null) { + this.artist = new PersistentObjectHolder(this, "artist"); + } + + return (Artist) artist.getValue(); + } + public void setArtist(Artist artist) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "artist", true); + } else if (this.artist == null) { + this.artist = new PersistentObjectHolder(this, "artist"); + } + + // note how we notify ObjectContext of change BEFORE the object is actually + // changed... this is needed to take a valid current snapshot + Object oldValue = this.artist.getValueDirectly(); + if (objectContext != null) { + objectContext.propertyChanged(this, "artist", oldValue, artist); + } + + this.artist.setValue(artist); + } + + public Gallery getGallery() { + if(objectContext != null) { + objectContext.prepareForAccess(this, "gallery", true); + } else if (this.gallery == null) { + this.gallery = new PersistentObjectHolder(this, "gallery"); + } + + return (Gallery) gallery.getValue(); + } + public void setGallery(Gallery gallery) { + if(objectContext != null) { + objectContext.prepareForAccess(this, "gallery", true); + } else if (this.gallery == null) { + this.gallery = new PersistentObjectHolder(this, "gallery"); + } + + // note how we notify ObjectContext of change BEFORE the object is actually + // changed... this is needed to take a valid current snapshot + Object oldValue = this.gallery.getValueDirectly(); + if (objectContext != null) { + objectContext.propertyChanged(this, "gallery", oldValue, gallery); + } + + this.gallery.setValue(gallery); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-client-http2/src/main/resources/keystore ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-client-http2/src/main/resources/keystore b/tutorials/tutorial-rop-client-http2/src/main/resources/keystore new file mode 100644 index 0000000..d6592f9 Binary files /dev/null and b/tutorials/tutorial-rop-client-http2/src/main/resources/keystore differ http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-server-http2/pom.xml ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-server-http2/pom.xml b/tutorials/tutorial-rop-server-http2/pom.xml new file mode 100644 index 0000000..0ba2ddd --- /dev/null +++ b/tutorials/tutorial-rop-server-http2/pom.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + 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. +--> +<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/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>cayenne-tutorials-parent</artifactId> + <groupId>org.apache.cayenne.tutorials</groupId> + <version>4.0.M4-SNAPSHOT</version> + </parent> + + <artifactId>tutorial-rop-server-http2</artifactId> + <name>Cayenne ROP HTTP/2 Server Tutorial</name> + <packaging>jar</packaging> + + <properties> + <main.class>org.apache.cayenne.tutorial.Http2Server</main.class> + <jetty.version>9.3.10.v20160621</jetty.version> + </properties> + + <dependencies> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-server</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-protostuff</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.cayenne</groupId> + <artifactId>cayenne-java8</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.derby</groupId> + <artifactId>derby</artifactId> + <scope>compile</scope> + </dependency> + + <!-- Jetty's dependencies--> + <dependency> + <groupId>org.eclipse.jetty.http2</groupId> + <artifactId>http2-server</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <version>${jetty.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlets</artifactId> + <version>${jetty.version}</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + </plugins> + </build> + +</project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2ROPServlet.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2ROPServlet.java b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2ROPServlet.java new file mode 100644 index 0000000..1ae9865 --- /dev/null +++ b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2ROPServlet.java @@ -0,0 +1,66 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.tutorial; + +import org.apache.cayenne.configuration.rop.client.ProtostuffModule; +import org.apache.cayenne.configuration.rop.server.ROPServerModule; +import org.apache.cayenne.configuration.server.ServerRuntime; +import org.apache.cayenne.configuration.web.WebConfiguration; +import org.apache.cayenne.configuration.web.WebUtil; +import org.apache.cayenne.di.Module; +import org.apache.cayenne.java8.CayenneJava8Module; +import org.apache.cayenne.remote.RemoteService; +import org.apache.cayenne.rop.ROPSerializationService; +import org.apache.cayenne.rop.ROPServlet; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import java.util.Collection; +import java.util.Map; + +public class Http2ROPServlet extends ROPServlet { + + @Override + public void init(ServletConfig configuration) throws ServletException { + + checkAlreadyConfigured(configuration.getServletContext()); + + this.servletContext = configuration.getServletContext(); + + WebConfiguration configAdapter = new WebConfiguration(configuration); + + String configurationLocation = configAdapter.getConfigurationLocation(); + Map<String, String> eventBridgeParameters = configAdapter.getOtherParameters(); + + Collection<Module> modules = configAdapter.createModules( + new ROPServerModule(eventBridgeParameters), + new ProtostuffModule(), + new CayenneJava8Module()); + + ServerRuntime runtime = new ServerRuntime(configurationLocation, modules + .toArray(new Module[modules.size()])); + + this.remoteService = runtime.getInjector().getInstance(RemoteService.class); + this.serializationService = runtime.getInjector().getInstance(ROPSerializationService.class); + + WebUtil.setCayenneRuntime(servletContext, runtime); + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2Server.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2Server.java b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2Server.java new file mode 100644 index 0000000..20028fd --- /dev/null +++ b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/Http2Server.java @@ -0,0 +1,107 @@ +/***************************************************************** + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.cayenne.tutorial; + +import org.eclipse.jetty.http2.HTTP2Cipher; +import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.security.HashLoginService; +import org.eclipse.jetty.security.SecurityHandler; +import org.eclipse.jetty.security.authentication.BasicAuthenticator; +import org.eclipse.jetty.server.*; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.security.Credential; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import static org.eclipse.jetty.util.resource.Resource.newClassPathResource; + +/** + * Based on the example org.eclipse.jetty.embedded.Http2Server included in the jetty-project distribution. + * <p> + * This server works without ALPN and could handle only HTTP/2 protocol. + */ +public class Http2Server { + + public static void main(String... args) throws Exception { + // Setting Protostuff properties + System.setProperty("protostuff.runtime.collection_schema_on_repeated_fields", "true"); + System.setProperty("protostuff.runtime.morph_collection_interfaces", "true"); + System.setProperty("protostuff.runtime.morph_map_interfaces", "true"); + System.setProperty("protostuff.runtime.pojo_schema_on_collection_fields", "true"); + System.setProperty("protostuff.runtime.pojo_schema_on_map_fields", "true"); + + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); + context.addServlet(new ServletHolder("cayenne-project", new Http2ROPServlet()), "/"); + context.setSecurityHandler(basicAuth("cayenne-user", "secret", "Cayenne Realm")); + server.setHandler(context); + + // HTTPS Configuration + HttpConfiguration httpsConfig = new HttpConfiguration(); + httpsConfig.setSecureScheme("https"); + httpsConfig.setSecurePort(8443); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + + // SSL Context Factory for HTTPS and HTTP/2 + SslContextFactory sslContextFactory = new SslContextFactory(); + sslContextFactory.setKeyStoreResource(newClassPathResource("keystore")); + sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); + sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); + sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR); + + // SSL Connection Factory + SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, "h2"); + + // HTTP/2 Connector + ServerConnector http2Connector = new ServerConnector(server, ssl, new HTTP2ServerConnectionFactory(httpsConfig)); + http2Connector.setPort(8443); + server.addConnector(http2Connector); + + server.start(); + server.join(); + } + + private static SecurityHandler basicAuth(String username, String password, String realm) { + HashLoginService loginService = new HashLoginService(); + loginService.putUser(username, Credential.getCredential(password), new String[]{"cayenne-service-user"}); + loginService.setName(realm); + + Constraint constraint = new Constraint(); + constraint.setName(Constraint.__BASIC_AUTH); + constraint.setRoles(new String[]{"cayenne-service-user"}); + constraint.setAuthenticate(true); + + ConstraintMapping constraintMapping = new ConstraintMapping(); + constraintMapping.setConstraint(constraint); + constraintMapping.setPathSpec("/*"); + + ConstraintSecurityHandler constraintSecurityHandler = new ConstraintSecurityHandler(); + constraintSecurityHandler.setAuthenticator(new BasicAuthenticator()); + constraintSecurityHandler.setRealmName(realm); + constraintSecurityHandler.addConstraintMapping(constraintMapping); + constraintSecurityHandler.setLoginService(loginService); + + return constraintSecurityHandler; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Artist.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Artist.java b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Artist.java new file mode 100644 index 0000000..9d627d4 --- /dev/null +++ b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Artist.java @@ -0,0 +1,9 @@ +package org.apache.cayenne.tutorial.persistent; + +import org.apache.cayenne.tutorial.persistent.auto._Artist; + +public class Artist extends _Artist { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Gallery.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Gallery.java b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Gallery.java new file mode 100644 index 0000000..21b8349 --- /dev/null +++ b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Gallery.java @@ -0,0 +1,9 @@ +package org.apache.cayenne.tutorial.persistent; + +import org.apache.cayenne.tutorial.persistent.auto._Gallery; + +public class Gallery extends _Gallery { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Painting.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Painting.java b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Painting.java new file mode 100644 index 0000000..d0165f0 --- /dev/null +++ b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/Painting.java @@ -0,0 +1,9 @@ +package org.apache.cayenne.tutorial.persistent; + +import org.apache.cayenne.tutorial.persistent.auto._Painting; + +public class Painting extends _Painting { + + private static final long serialVersionUID = 1L; + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/34646fd5/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/auto/_Artist.java ---------------------------------------------------------------------- diff --git a/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/auto/_Artist.java b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/auto/_Artist.java new file mode 100644 index 0000000..f9ee738 --- /dev/null +++ b/tutorials/tutorial-rop-server-http2/src/main/java/org/apache/cayenne/tutorial/persistent/auto/_Artist.java @@ -0,0 +1,52 @@ +package org.apache.cayenne.tutorial.persistent.auto; + +import java.time.LocalDate; +import java.util.List; + +import org.apache.cayenne.CayenneDataObject; +import org.apache.cayenne.exp.Property; +import org.apache.cayenne.tutorial.persistent.Painting; + +/** + * Class _Artist was generated by Cayenne. + * It is probably a good idea to avoid changing this class manually, + * since it may be overwritten next time code is regenerated. + * If you need to make any customizations, please use subclass. + */ +public abstract class _Artist extends CayenneDataObject { + + private static final long serialVersionUID = 1L; + + public static final String ID_PK_COLUMN = "ID"; + + public static final Property<LocalDate> DATE_OF_BIRTH = new Property<LocalDate>("dateOfBirth"); + public static final Property<String> NAME = new Property<String>("name"); + public static final Property<List<Painting>> PAINTINGS = new Property<List<Painting>>("paintings"); + + public void setDateOfBirth(LocalDate dateOfBirth) { + writeProperty("dateOfBirth", dateOfBirth); + } + public LocalDate getDateOfBirth() { + return (LocalDate)readProperty("dateOfBirth"); + } + + public void setName(String name) { + writeProperty("name", name); + } + public String getName() { + return (String)readProperty("name"); + } + + public void addToPaintings(Painting obj) { + addToManyTarget("paintings", obj, true); + } + public void removeFromPaintings(Painting obj) { + removeToManyTarget("paintings", obj, true); + } + @SuppressWarnings("unchecked") + public List<Painting> getPaintings() { + return (List<Painting>)readProperty("paintings"); + } + + +}