Repository: cxf Updated Branches: refs/heads/master c17955382 -> abe5b35ec
add a jaxrs websocket browser demo in the sample collection Project: http://git-wip-us.apache.org/repos/asf/cxf/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/abe5b35e Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/abe5b35e Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/abe5b35e Branch: refs/heads/master Commit: abe5b35ec859a2bae12c44bb4a7a8f1a118c6cf6 Parents: c179553 Author: Akitoshi Yoshida <[email protected]> Authored: Mon Mar 24 10:56:48 2014 +0100 Committer: Akitoshi Yoshida <[email protected]> Committed: Mon Mar 24 10:56:48 2014 +0100 ---------------------------------------------------------------------- .../release/samples/jax_rs/websocket/README.txt | 132 ++++++++ .../release/samples/jax_rs/websocket/pom.xml | 145 +++++++++ .../src/main/java/demo/jaxrs/client/Client.java | 116 ++++++++ .../demo/jaxrs/client/WebSocketTestClient.java | 298 +++++++++++++++++++ .../main/java/demo/jaxrs/server/Customer.java | 43 +++ .../java/demo/jaxrs/server/CustomerService.java | 176 +++++++++++ .../src/main/java/demo/jaxrs/server/Order.java | 69 +++++ .../main/java/demo/jaxrs/server/Product.java | 43 +++ .../src/main/java/demo/jaxrs/server/Server.java | 45 +++ .../src/main/resources/add_customer.xml | 4 + .../websocket/src/main/resources/index.html | 172 +++++++++++ .../src/main/resources/update_customer.xml | 5 + 12 files changed, 1248 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/README.txt ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/README.txt b/distribution/src/main/release/samples/jax_rs/websocket/README.txt new file mode 100644 index 0000000..19d8ecb --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/README.txt @@ -0,0 +1,132 @@ +JAX-RS WebSocket Demo +================= + +This is a websocket transport version of JAX-RS Basic Demo. + +A RESTful customer service is provided on URL ws://localhost:9000/customers. +Users access this URI to operate on customer. + +Open a websocket to ws://localhost:9000/ and send requests over the websocket. + +A GET request to path /customerservice/customers/123 + +------------------------------------------------------------------------ +GET /customerservice/customers/123 +------------------------------------------------------------------------ + +returns a customer instance whose id is 123. The XML document returned: + +------------------------------------------------------------------------ +<Customer> + <id>123</id> + <name>John</name> +</Customer> +------------------------------------------------------------------------ + +A GET request to path /customerservice/orders/223/products/323 + +------------------------------------------------------------------------ +GET /customerservice/orders/223/products/323 +------------------------------------------------------------------------ + +returns product 323 that belongs to order 223. The XML document returned: + +------------------------------------------------------------------------ +<Product> + <description>product 323</description> + <id>323</id> +</Product> +------------------------------------------------------------------------ + +A POST request to path /customerservice/customers + +------------------------------------------------------------------------ +POST /customerservice/customers +Content-Type: text/xml; charset="utf-8" +------------------------------------------------------------------------ + +with the data: + +------------------------------------------------------------------------ +<Customer> + <name>Jack</name> +</Customer> +------------------------------------------------------------------------ + +adds a customer whose name is Jack + + +A PUT request to path /customerservice/customers + +------------------------------------------------------------------------ +PUT /customerservice/customers +Content-Type: text/xml; charset="utf-8" +------------------------------------------------------------------------ + +with the data: + +------------------------------------------------------------------------ +<Customer> + <id>123</id> + <name>John</name> +</Customer> +------------------------------------------------------------------------ + +updates the customer instance whose id is 123 + + +A GET request to path /monitor + +------------------------------------------------------------------------ +GET /customerservice/monitor +------------------------------------------------------------------------ + +returns a continuous event stream on the customer +activities. + +The client code demonstrates how to send GET/POST/PUT/DELETE requests over +a websocket. + + +Please review the README in the samples directory before +continuing. + + + +Building and running the demo using maven +--------------------------------------- + +From the base directory of this sample (i.e., where this README file is +located), the maven pom.xml file can be used to build and run the demo. + + +Using either UNIX or Windows: + + mvn install + mvn -Pserver (from one command line window) + mvn -Pclient (from a second command line window) + +To remove the target dir, run mvn clean". + +Using a web browser that natively supports WebSocket (Safari, Chrome, Firefox): +After starting the server (see above), open the index.html page located at + + samples/jax_rs/websocket/src/main/resources/index.html + +Click on the "Connect" button to establish the websocket connection. +Fill in the Request and click on the "Send" button. The sent and +received data are displayed in the Log area. + +Try out the above sample requests. When using POST or PUT with content, +make sure to have one empty line between the request header and +the content. For example, the above POST example should use the Request +value: + +------------------------------------------------------------------------ +POST /customerservice/customers +Content-Type: text/xml; charset="utf-8" + +<Customer> + <name>Jack</name> +</Customer> +------------------------------------------------------------------------ http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/pom.xml ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/pom.xml b/distribution/src/main/release/samples/jax_rs/websocket/pom.xml new file mode 100644 index 0000000..4cd9fd6 --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/pom.xml @@ -0,0 +1,145 @@ +<?xml version="1.0"?> +<!-- + 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> + <artifactId>jax_rs_websocket</artifactId> + <name>JAX-RS WebSocket Demo</name> + <description>JAX-RS WebSocket Demo</description> + <parent> + <groupId>org.apache.cxf.samples</groupId> + <artifactId>cxf-samples</artifactId> + <version>3.0.0-SNAPSHOT</version> + <relativePath>../..</relativePath> + </parent> + <properties> + <cxf.version>${project.version}</cxf.version> + <cxf.ahc.version>1.8.1</cxf.ahc.version> + <cxf.jetty.version>8.1.14.v20131031</cxf.jetty.version> + <cxf.netty3.version>3.8.0.Final</cxf.netty3.version> + </properties> + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + </configuration> + </plugin> + </plugins> + </build> + <profiles> + <profile> + <id>server</id> + <build> + <defaultGoal>test</defaultGoal> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <executions> + <execution> + <phase>test</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>demo.jaxrs.server.Server</mainClass> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + <profile> + <id>client</id> + <build> + <defaultGoal>test</defaultGoal> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>exec-maven-plugin</artifactId> + <executions> + <execution> + <phase>test</phase> + <goals> + <goal>java</goal> + </goals> + <configuration> + <mainClass>demo.jaxrs.client.Client</mainClass> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + </profiles> + <dependencies> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-transports-http</artifactId> + <version>3.0.0-SNAPSHOT</version> + </dependency> + <!-- This dependency is needed if you're using the Jetty container --> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-transports-websocket</artifactId> + <version>3.0.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-frontend-jaxrs</artifactId> + <version>3.0.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-websocket</artifactId> + <version>${cxf.jetty.version}</version> + </dependency> + <dependency> + <groupId>com.ning</groupId> + <artifactId>async-http-client</artifactId> + <version>${cxf.ahc.version}</version> + <exclusions> + <exclusion> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + <version>${cxf.netty3.version}</version> + </dependency> + <!-- JettyHttpDestination is referring to org.springframework.util.ClassUtils --> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-core</artifactId> + </dependency> + + </dependencies> +</project> http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/Client.java ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/Client.java b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/Client.java new file mode 100644 index 0000000..4004e84 --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/Client.java @@ -0,0 +1,116 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package demo.jaxrs.client; + +import java.io.InputStream; +import java.util.List; + +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.io.CachedOutputStream; + +public final class Client { + + private Client() { + } + + public static void main(String args[]) throws Exception { + // Sent HTTP GET request to query all customer info + /* + * URL url = new URL("http://localhost:9000/customers"); + * System.out.println("Invoking server through HTTP GET to query all + * customer info"); InputStream in = url.openStream(); StreamSource + * source = new StreamSource(in); printSource(source); + */ + + // Create a websocket client and connect to the target service + WebSocketTestClient client = new WebSocketTestClient("ws://localhost:9000/"); + client.connect(); + + // Sent GET request to query customer info + System.out.println("Sent GET request to query customer info"); + client.sendTextMessage("GET /customerservice/customers/123"); + client.await(5); + List<WebSocketTestClient.Response> responses = client.getReceivedResponses(); + System.out.println(responses.get(0)); + + // Sent GET request to query sub resource product info + client.reset(1); + System.out.println("Sent GET request to query sub resource product info"); + client.sendTextMessage("GET /customerservice/orders/223/products/323"); + client.await(5); + responses = client.getReceivedResponses(); + System.out.println(responses.get(0)); + + // Sent PUT request to update customer info + client.reset(1); + System.out.println("Sent PUT request to update customer info"); + String inputData = getStringFromInputStream(Client.class.getResourceAsStream("/update_customer.xml")); + client.sendTextMessage("PUT /customerservice/customers\r\nContent-Type: text/xml; charset=ISO-8859-1\r\n\r\n" + inputData); + client.await(5); + responses = client.getReceivedResponses(); + System.out.println(responses.get(0)); + + // Sent POST request to add customer + client.reset(1); + System.out.println("Sent POST request to add customer"); + inputData = getStringFromInputStream(Client.class.getResourceAsStream("/add_customer.xml")); + client.sendTextMessage("POST /customerservice/customers\r\nContent-Type: text/xml; charset=ISO-8859-1\r\nAccept: text/xml\r\n\r\n" + inputData); + client.await(5); + responses = client.getReceivedResponses(); + System.out.println(responses.get(0)); + + // Create another websocket client and connect to the target service + WebSocketTestClient client2 = new WebSocketTestClient("ws://localhost:9000/"); + client2.connect(); + + // Sent GET request to monitor the customer activities + client2.reset(1); + System.out.println("Sent GET request to monitor activities"); + client2.sendTextMessage("GET /customerservice/monitor"); + client2.await(5); + responses = client2.getReceivedResponses(); + System.out.println(responses.get(0)); + + // one retrieval, one delete + client2.reset(2); + client.reset(2); + client.sendTextMessage("GET /customerservice/customers/123"); + client.sendTextMessage("DELETE /customerservice/customers/235"); + + client2.await(5); + responses = client2.getReceivedResponses(); + for (Object o : responses) { + System.out.println(o); + } + + client.close(); + client2.close(); + System.exit(0); + } + + private static String getStringFromInputStream(InputStream in) throws Exception { + CachedOutputStream bos = new CachedOutputStream(); + IOUtils.copy(in, bos); + in.close(); + bos.close(); + return bos.getOut().toString(); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/WebSocketTestClient.java ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/WebSocketTestClient.java b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/WebSocketTestClient.java new file mode 100644 index 0000000..1d9ad1f --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/client/WebSocketTestClient.java @@ -0,0 +1,298 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package demo.jaxrs.client; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +import com.ning.http.client.AsyncHttpClient; +import com.ning.http.client.websocket.WebSocket; +import com.ning.http.client.websocket.WebSocketByteListener; +import com.ning.http.client.websocket.WebSocketTextListener; +import com.ning.http.client.websocket.WebSocketUpgradeHandler; + +import org.apache.cxf.common.logging.LogUtils; + + + +/** + * Test client to do websocket calls. + * @see JAXRSClientServerWebSocketTest + * + * we may put this in test-tools so that other systests can use this code. + * for now keep it here to experiment jaxrs websocket scenarios. + */ +class WebSocketTestClient { + private static final Logger LOG = LogUtils.getL7dLogger(WebSocketTestClient.class); + + private List<Object> received; + private List<Object> fragments; + private CountDownLatch latch; + private AsyncHttpClient client; + private WebSocket websocket; + private String url; + + public WebSocketTestClient(String url) { + this.received = new ArrayList<Object>(); + this.fragments = new ArrayList<Object>(); + this.latch = new CountDownLatch(1); + this.client = new AsyncHttpClient(); + this.url = url; + } + + public void connect() throws InterruptedException, ExecutionException, IOException { + websocket = client.prepareGet(url).execute( + new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WsSocketListener()).build()).get(); + } + + public void sendTextMessage(String message) { + websocket.sendTextMessage(message); + } + + public void sendMessage(byte[] message) { + websocket.sendMessage(message); + } + + public boolean await(int secs) throws InterruptedException { + return latch.await(secs, TimeUnit.SECONDS); + } + + public void reset(int count) { + latch = new CountDownLatch(count); + received.clear(); + } + + public List<Object> getReceived() { + return received; + } + + public List<Response> getReceivedResponses() { + Object[] objs = received.toArray(); + List<Response> responses = new ArrayList<Response>(objs.length); + for (Object o : objs) { + responses.add(new Response(o)); + } + return responses; + } + + public void close() { + websocket.close(); + client.close(); + } + + class WsSocketListener implements WebSocketTextListener, WebSocketByteListener { + + public void onOpen(WebSocket ws) { + LOG.info("[ws] opened"); + } + + public void onClose(WebSocket ws) { + LOG.info("[ws] closed"); + } + + public void onError(Throwable t) { + LOG.info("[ws] error: " + t); + } + + public void onMessage(byte[] message) { + received.add(message); + LOG.info("[ws] received bytes --> " + makeString(message)); + latch.countDown(); + } + + public void onFragment(byte[] fragment, boolean last) { + processFragments(fragment, last); + } + + public void onMessage(String message) { + received.add(message); + LOG.info("[ws] received --> " + message); + latch.countDown(); + } + + public void onFragment(String fragment, boolean last) { + processFragments(fragment, last); + } + + private void processFragments(Object f, boolean last) { + synchronized (fragments) { + fragments.add(f); + if (last) { + if (f instanceof String) { + // string + StringBuilder sb = new StringBuilder(); + for (Iterator<Object> it = fragments.iterator(); it.hasNext();) { + Object o = it.next(); + if (o instanceof String) { + sb.append((String)o); + it.remove(); + } + } + received.add(sb.toString()); + } else { + // byte[] + ByteArrayOutputStream bao = new ByteArrayOutputStream(); + for (Iterator<Object> it = fragments.iterator(); it.hasNext();) { + Object o = it.next(); + if (o instanceof byte[]) { + bao.write((byte[])o, 0, ((byte[])o).length); + it.remove(); + } + } + received.add(bao.toByteArray()); + } + } + } + } + } + + private static String makeString(byte[] data) { + return data == null ? null : makeString(data, 0, data.length).toString(); + } + + private static StringBuilder makeString(byte[] data, int offset, int length) { + if (data .length > 256) { + return makeString(data, offset, 256).append("..."); + } + StringBuilder xbuf = new StringBuilder().append("\nHEX: "); + StringBuilder cbuf = new StringBuilder().append("\nASC: "); + for (byte b : data) { + writeHex(xbuf, 0xff & b); + writePrintable(cbuf, 0xff & b); + } + return xbuf.append(cbuf); + } + + private static void writeHex(StringBuilder buf, int b) { + buf.append(Integer.toHexString(0x100 | (0xff & b)).substring(1)).append(' '); + } + + private static void writePrintable(StringBuilder buf, int b) { + if (b == 0x0d) { + buf.append("\\r"); + } else if (b == 0x0a) { + buf.append("\\n"); + } else if (b == 0x09) { + buf.append("\\t"); + } else if ((0x80 & b) != 0) { + buf.append('.').append(' '); + } else { + buf.append((char)b).append(' '); + } + buf.append(' '); + } + + //TODO this is a temporary way to verify the response; we should come up with something better. + public static class Response { + private Object data; + private int pos; + private int statusCode; + private String contentType; + private Object entity; + + public Response(Object data) { + this.data = data; + String line = readLine(); + if (line != null) { + statusCode = Integer.parseInt(line); + while ((line = readLine()) != null) { + if (line.length() > 0) { + int del = line.indexOf(':'); + String h = line.substring(0, del).trim(); + String v = line.substring(del + 1).trim(); + if ("Content-Type".equalsIgnoreCase(h)) { + contentType = v; + } + } + } + } + if (data instanceof String) { + entity = ((String)data).substring(pos); + } else if (data instanceof byte[]) { + entity = new byte[((byte[])data).length - pos]; + System.arraycopy((byte[])data, pos, (byte[])entity, 0, ((byte[])entity).length); + } + } + + + + public int getStatusCode() { + return statusCode; + } + + public String getContentType() { + return contentType; + } + + @SuppressWarnings("unused") + public Object getEntity() { + return entity; + } + + public String getTextEntity() { + return gettext(entity); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("Status: ").append(statusCode).append("\r\n"); + sb.append("Type: ").append(contentType).append("\r\n"); + sb.append("Entity: ").append(gettext(entity)).append("\r\n"); + return sb.toString(); + } + + private String readLine() { + StringBuilder sb = new StringBuilder(); + while (pos < length(data)) { + int c = getchar(data, pos++); + if (c == '\n') { + break; + } else if (c == '\r') { + continue; + } else { + sb.append((char)c); + } + } + if (sb.length() == 0) { + return null; + } + return sb.toString(); + } + + private int length(Object o) { + return o instanceof char[] ? ((String)o).length() : (o instanceof byte[] ? ((byte[])o).length : 0); + } + + private int getchar(Object o, int p) { + return 0xff & (o instanceof String ? ((String)o).charAt(p) : (o instanceof byte[] ? ((byte[])o)[p] : -1)); + } + + private String gettext(Object o) { + return o instanceof String ? (String)o : (o instanceof byte[] ? new String((byte[])o) : null); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Customer.java ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Customer.java b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Customer.java new file mode 100644 index 0000000..44025f0 --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Customer.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package demo.jaxrs.server; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "Customer") +public class Customer { + private long id; + private String name; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/CustomerService.java ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/CustomerService.java b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/CustomerService.java new file mode 100644 index 0000000..9ba3e05 --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/CustomerService.java @@ -0,0 +1,176 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package demo.jaxrs.server; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.StreamingOutput; + +@Path("/customerservice/") +@Produces("text/xml") +public class CustomerService { + private static ExecutorService executor = Executors.newSingleThreadExecutor(); + + long currentId = 123; + Map<Long, Customer> customers = new HashMap<Long, Customer>(); + Map<Long, Order> orders = new HashMap<Long, Order>(); + Set<OutputStream> monitors = new HashSet<OutputStream>(); + + public CustomerService() { + init(); + } + + @GET + @Path("/customers/{id}/") + public Customer getCustomer(@PathParam("id") String id) { + System.out.println("----invoking getCustomer, Customer id is: " + id); + long idNumber = Long.parseLong(id); + Customer customer = customers.get(idNumber); + sendCustomerEvent("retrieved", customer); + return customer; + } + + @PUT + @Path("/customers/") + public Response updateCustomer(Customer customer) { + System.out.println("----invoking updateCustomer, Customer name is: " + customer.getName()); + Customer c = customers.get(customer.getId()); + Response r; + if (c != null) { + customers.put(customer.getId(), customer); + r = Response.ok().build(); + sendCustomerEvent("updated", customer); + } else { + r = Response.notModified().build(); + } + + return r; + } + + @POST + @Path("/customers/") + public Response addCustomer(Customer customer) { + System.out.println("----invoking addCustomer, Customer name is: " + customer.getName()); + customer.setId(++currentId); + + customers.put(customer.getId(), customer); + sendCustomerEvent("added", customer); + return Response.ok(customer).build(); + } + + @DELETE + @Path("/customers/{id}/") + public Response deleteCustomer(@PathParam("id") String id) { + System.out.println("----invoking deleteCustomer, Customer id is: " + id); + long idNumber = Long.parseLong(id); + Customer c = customers.get(idNumber); + + Response r; + if (c != null) { + r = Response.ok().build(); + Customer customer = customers.remove(idNumber); + if (customer != null) { + sendCustomerEvent("deleted", customer); + } + } else { + r = Response.notModified().build(); + } + + return r; + } + + @Path("/orders/{orderId}/") + public Order getOrder(@PathParam("orderId") String orderId) { + System.out.println("----invoking getOrder, Order id is: " + orderId); + long idNumber = Long.parseLong(orderId); + Order c = orders.get(idNumber); + return c; + } + + @GET + @Path("/monitor") + @Produces("text/*") + public StreamingOutput getBookBought() { + return new StreamingOutput() { + public void write(final OutputStream out) throws IOException, WebApplicationException { + monitors.add(out); + out.write(("Subscribed at " + new java.util.Date()).getBytes()); + } + }; + } + + private void sendCustomerEvent(final String msg, final Customer customer) { + executor.execute(new Runnable() { + public void run() { + try { + String t = msg + ": " + customer.getId() + "/" + customer.getName(); + for (Iterator<OutputStream> it = monitors.iterator(); it.hasNext();) { + OutputStream out = it.next(); + try { + out.write(t.getBytes()); + } catch (IOException e) { + try { + out.close(); + } catch (IOException e2) { + // ignore; + } + it.remove(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + + } + final void init() { + Customer c = new Customer(); + c.setName("John"); + c.setId(123); + customers.put(c.getId(), c); + c = new Customer(); + c.setName("Homer"); + c.setId(235); + customers.put(c.getId(), c); + + Order o = new Order(); + o.setDescription("order 223"); + o.setId(223); + orders.put(o.getId(), o); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Order.java ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Order.java b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Order.java new file mode 100644 index 0000000..a06b68b --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Order.java @@ -0,0 +1,69 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package demo.jaxrs.server; + +import java.util.HashMap; +import java.util.Map; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "Order") +public class Order { + private long id; + private String description; + private Map<Long, Product> products = new HashMap<Long, Product>(); + + public Order() { + init(); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String d) { + this.description = d; + } + + @GET + @Path("products/{productId}/") + public Product getProduct(@PathParam("productId")int productId) { + System.out.println("----invoking getProduct with id: " + productId); + Product p = products.get(new Long(productId)); + return p; + } + + final void init() { + Product p = new Product(); + p.setId(323); + p.setDescription("product 323"); + products.put(p.getId(), p); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Product.java ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Product.java b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Product.java new file mode 100644 index 0000000..4452ec5 --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Product.java @@ -0,0 +1,43 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package demo.jaxrs.server; + +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "Product") +public class Product { + private long id; + private String description; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getDescription() { + return description; + } + + public void setDescription(String d) { + this.description = d; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Server.java ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Server.java b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Server.java new file mode 100644 index 0000000..fe59dd0 --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/java/demo/jaxrs/server/Server.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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package demo.jaxrs.server; + +import org.apache.cxf.jaxrs.JAXRSServerFactoryBean; +import org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider; + +public class Server { + + protected Server() throws Exception { + JAXRSServerFactoryBean sf = new JAXRSServerFactoryBean(); + sf.setResourceClasses(CustomerService.class); + sf.setResourceProvider(CustomerService.class, + new SingletonResourceProvider(new CustomerService())); + sf.setAddress("ws://localhost:9000/"); + + sf.create(); + } + + public static void main(String args[]) throws Exception { + new Server(); + System.out.println("Server ready..."); + + Thread.sleep(5 * 6000 * 1000); + System.out.println("Server exiting"); + System.exit(0); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/add_customer.xml ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/add_customer.xml b/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/add_customer.xml new file mode 100644 index 0000000..9ef3d4b --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/add_customer.xml @@ -0,0 +1,4 @@ +<?xml version="1.0"?> +<Customer> + <name>Jack</name> +</Customer> http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/index.html ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/index.html b/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/index.html new file mode 100644 index 0000000..2b1cc18 --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/index.html @@ -0,0 +1,172 @@ +<?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. + +--> +<html> +<head> +<meta charset="utf-8"/> +<title>CXF JAX-RS WebSocket Sample</title> +</head> +<body> +<h2>CXF JAX-RS WebSocket Sample</h2> + +<div id="output"> + <div id="config" style="float: left;"> + <strong>Service Endpoint URL:</strong><br /> + <input id="wsUri" size="72" style="width: 450px" value="ws://localhost:9000/"/> + <br/> + <button id="connect">Connect</button> + <button id="disconnect">Disconnect</button> + <br /> + <br /> + <strong>Request:</strong><br /> + <textarea id="request" rows="10" cols="72" style="width: 450px;">GET /customerservice/customers/123</textarea> + <br/> + <button id="send">Send</button> + </div> + <div id="log" style="float: left; margin-left: 20px; padding-left: 20px; width: 450px; height: 500px; border-left: solid 1px #cccccc;"> + <strong>Log:</strong><br /> + <button id="clearLogBut" style="position: relative; top: 3px;">Clear log</button> + <div id="consoleLog" style="margin-top: 5px; border: solid 1px #aaaaaa; overflow-y: scroll; height:100%"></div> + </div> + <div class="clearfix"></div> +</div> +<script language="javascript" type="text/javascript"> + var output; + + var connectBut = document.getElementById("connect"); + connectBut.onclick = doConnect; + + var disconnectBut = document.getElementById("disconnect"); + disconnectBut.onclick = doDisconnect; + + var request = document.getElementById("request"); + + var sendBut = document.getElementById("send"); + sendBut.onclick = doSend; + + var consoleLog = document.getElementById("consoleLog"); + + var clearLogBut = document.getElementById("clearLogBut"); + clearLogBut.onclick = clearLog; + + function init() { + output = document.getElementById("output"); + setGuiConnected(false); + } + + function doConnect() { + var wsUri = document.getElementById("wsUri"); + + websocket = new WebSocket(wsUri.value); + websocket.binaryType = 'arraybuffer'; + + websocket.onopen = function(evt) { + onOpen(evt) + }; + + websocket.onclose = function(evt) { + onClose(evt) + }; + + websocket.onmessage = function(evt) { + onMessage(evt) + }; + + websocket.onerror = function(evt) { + onError(evt) + }; + } + + function doDisconnect() { + websocket.close() + } + + function clearLog() { + while (consoleLog.childNodes.length > 0) { + consoleLog.removeChild(consoleLog.lastChild); + } + } + + function onOpen(evt) { + writeToLog('<span style="color:black; font-size:75%; font-style:italic">CONNECTED</span>'); + setGuiConnected(true); + } + + function onClose(evt) { + writeToLog('<span style="color:black; font-size:75%; font-style:italic">DISCONNECTED</span>'); + setGuiConnected(false); + } + + function onMessage(evt) { + writeToLog('<span style="color:blue; font-size:75%; font-style:italic">RECEIVED</span><span style="color: blue"><pre>' + createMessage(evt.data) + '</pre></span>'); + } + + function onError(evt) { + writeToLog('<span style="color:red; font-size:75%; font-style:italic">ERROR</span><span style="color: red;"><pre>' + evt.data + '</pre></span>'); + } + + function doSend(message) { + var reqmsg = document.getElementById("request"); + writeToLog('<span style="color:green; font-size:75%; font-style:italic">SENT</span><span style="color: green;"><pre>' + createMessage(reqmsg.value) + '</pre></span>'); + websocket.send(reqmsg.value); + } + + function setGuiConnected(isConnected) { + wsUri.disabled = isConnected; + connectBut.disabled = isConnected; + disconnectBut.disabled = !isConnected; + request.disabled = !isConnected; + sendBut.disabled = !isConnected; + } + + function createMessage(data) { + if (typeof data != 'string') { + data = arrayBufferToString(data); + } + return data.replace(/&/g, '&').replace(/</g, '<'); + } + + function arrayBufferToString(buf) { + var ba = new Uint8Array(buf); + var aa = new Array(ba.length); + for (i = 0; i < ba.length; i++) { + aa[i] = String.fromCharCode(ba[i]); + } + return Array.prototype.slice.call(aa).join(""); + } + + function writeToLog(message) { + var pre = document.createElement("p"); + pre.wordWrap = "break-word"; + pre.innerHTML = message; + consoleLog.appendChild(pre); + + while (consoleLog.childNodes.length > 50) { + consoleLog.removeChild(consoleLog.firstChild); + } + + consoleLog.scrollTop = consoleLog.scrollHeight; + } + + window.addEventListener("load", init, false); + + </script> +</body> +</html> http://git-wip-us.apache.org/repos/asf/cxf/blob/abe5b35e/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/update_customer.xml ---------------------------------------------------------------------- diff --git a/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/update_customer.xml b/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/update_customer.xml new file mode 100644 index 0000000..228421d --- /dev/null +++ b/distribution/src/main/release/samples/jax_rs/websocket/src/main/resources/update_customer.xml @@ -0,0 +1,5 @@ +<?xml version="1.0"?> +<Customer> + <name>Mary</name> + <id>123</id> +</Customer>
