From eed639ccf7fd95e0b31caf61787126e69592f207 Mon Sep 17 00:00:00 2001 From: ramkumar Date: Tue, 2 Dec 2008 11:17:24 +0000 Subject: Branch for 1.4 git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@722428 13f79535-47bb-0310-9956-ffa450edef68 --- .../sca/binding/atom/collection/Collection.java | 81 ++ .../binding/atom/collection/MediaCollection.java | 55 ++ .../binding/atom/collection/NotFoundException.java | 45 ++ .../binding/atom/provider/AtomBindingInvoker.java | 485 ++++++++++++ .../atom/provider/AtomBindingListenerServlet.java | 871 +++++++++++++++++++++ .../atom/provider/AtomBindingProviderFactory.java | 74 ++ .../sca/binding/atom/provider/AtomBindingUtil.java | 164 ++++ .../provider/AtomReferenceBindingProvider.java | 183 +++++ .../atom/provider/AtomServiceBindingProvider.java | 87 ++ ...che.tuscany.sca.provider.BindingProviderFactory | 19 + .../sca/binding/atom/AtomDeleteTestCase.java | 92 +++ .../tuscany/sca/binding/atom/AtomGetTestCase.java | 97 +++ .../tuscany/sca/binding/atom/AtomPostTestCase.java | 86 ++ .../tuscany/sca/binding/atom/AtomPutTestCase.java | 104 +++ .../sca/binding/atom/AtomTestCaseUtils.java | 96 +++ .../apache/tuscany/sca/binding/atom/Consumer.java | 35 + .../binding/atom/ConsumerProviderAtomTestCase.java | 155 ++++ .../sca/binding/atom/ContentNegotiationTest.java | 249 ++++++ .../tuscany/sca/binding/atom/CustomerClient.java | 29 + .../sca/binding/atom/CustomerClientImpl.java | 99 +++ .../sca/binding/atom/CustomerCollectionImpl.java | 149 ++++ .../sca/binding/atom/MediaCollectionImpl.java | 210 +++++ .../sca/binding/atom/MediaCollectionTestCase.java | 287 +++++++ .../apache/tuscany/sca/binding/atom/Provider.java | 41 + .../atom/ProviderEntryEntityTagsTestCase.java | 428 ++++++++++ .../atom/ProviderFeedEntityTagsTestCase.java | 382 +++++++++ .../atom/ProviderServiceDocumentTestCase.java | 209 +++++ .../tuscany/sca/binding/atom/Consumer.composite | 32 + .../tuscany/sca/binding/atom/Provider.composite | 33 + .../sca/binding/atom/ReceiptProvider.composite | 33 + 30 files changed, 4910 insertions(+) create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/Collection.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/MediaCollection.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/NotFoundException.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingProviderFactory.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingUtil.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomReferenceBindingProvider.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomServiceBindingProvider.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/main/resources/META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomDeleteTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomGetTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPostTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPutTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomTestCaseUtils.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Consumer.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ConsumerProviderAtomTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ContentNegotiationTest.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClient.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClientImpl.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerCollectionImpl.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionImpl.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Provider.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderEntryEntityTagsTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderFeedEntityTagsTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderServiceDocumentTestCase.java create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Consumer.composite create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Provider.composite create mode 100644 branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite (limited to 'branches/sca-java-1.4/modules/binding-atom-abdera/src') diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/Collection.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/Collection.java new file mode 100644 index 0000000000..7356cd0d2a --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/Collection.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom.collection; + +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.osoa.sca.annotations.Remotable; + +/** + * Provides access to a collection of resources using Atom. + * + * @version $Rev$ $Date$ + */ +@Remotable +public interface Collection { + + /** + * Get an Atom feed for a collection of resources. + * + * @return the Atom feed + */ + Feed getFeed(); + + /** + * Get an Atom feed for a collection of resources resulting + * from a query. + * + * @param queryString a query string + * @return the Atom feed + */ + Feed query(String queryString); + + /** + * Creates a new entry. + * + * @param entry + * @return + */ + Entry post(Entry entry); + + /** + * Retrieves an entry. + * + * @param id + * @return + */ + Entry get(String id) throws NotFoundException; + + /** + * Update an entry. + * + * @param id + * @param entry + * @return + */ + void put(String id, Entry entry) throws NotFoundException; + + /** + * Delete an entry. + * + * @param id + */ + void delete(String id) throws NotFoundException; + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/MediaCollection.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/MediaCollection.java new file mode 100644 index 0000000000..cc351442e7 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/MediaCollection.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom.collection; + +import java.io.InputStream; + +import org.osoa.sca.annotations.Remotable; + +import org.apache.abdera.model.Entry; + +/** + * Provides access to a collection of resources using Atom. + * + * @version $Rev$ $Date$ + */ +@Remotable +public interface MediaCollection extends Collection { + + /** + * Creates a new media entry + * + * @param title + * @param slug + * @param contentType + * @param media + */ + Entry postMedia(String title, String slug, String contentType, InputStream media); + + /** + * Update a media entry. + * + * @param id + * @param contentType + * @param media + * @return + */ + void putMedia(String id, String contentType, InputStream media) throws NotFoundException; + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/NotFoundException.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/NotFoundException.java new file mode 100644 index 0000000000..b457840922 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/collection/NotFoundException.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 org.apache.tuscany.sca.binding.atom.collection; + +/** + * Indicates that a resource could not be found. + * + * @version $Rev$ $Date$ + */ +public class NotFoundException extends Exception { + private static final long serialVersionUID = -5046027674128627383L; + + public NotFoundException() { + } + + public NotFoundException(String message) { + super(message); + } + + public NotFoundException(Throwable cause) { + super(cause); + } + + public NotFoundException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java new file mode 100644 index 0000000000..7219a3395b --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java @@ -0,0 +1,485 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom.provider; + +import static org.apache.tuscany.sca.binding.atom.provider.AtomBindingUtil.entry; +import static org.apache.tuscany.sca.binding.atom.provider.AtomBindingUtil.feedEntry; + +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import org.apache.abdera.Abdera; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Feed; +import org.apache.abdera.parser.Parser; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.StringRequestEntity; +import org.apache.tuscany.sca.binding.atom.collection.NotFoundException; +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.osoa.sca.ServiceRuntimeException; + +/** + * Invoker for the Atom binding. + * + * @version $Rev$ $Date$ + */ +class AtomBindingInvoker implements Invoker { + + private static final Factory abderaFactory = Abdera.getNewFactory(); + private static final Parser abderaParser = Abdera.getNewParser(); + + Operation operation; + String uri; + HttpClient httpClient; + String authorizationHeader; + AtomReferenceBindingProvider provider; + + AtomBindingInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + this.operation = operation; + this.uri = uri; + this.httpClient = httpClient; + this.authorizationHeader = authorizationHeader; + this.provider = bindingProvider; + } + + public Message invoke(Message msg) { + // Shouldn't get here, as the only supported methods are + // defined in the ResourceCollection interface, and implemented + // by specific invoker subclasses + throw new UnsupportedOperationException(operation.getName()); + } + + /** + * Get operation invoker + */ + public static class GetInvoker extends AtomBindingInvoker { + + public GetInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // Get an entry + String id = (String)((Object[])msg.getBody())[0]; + + // Send an HTTP GET + GetMethod getMethod = new GetMethod(uri + "/" + id); + getMethod.setRequestHeader("Authorization", authorizationHeader); + boolean parsing = false; + try { + httpClient.executeMethod(getMethod); + int status = getMethod.getStatusCode(); + + // Read the Atom entry + if (status == 200) { + Document doc = + abderaParser.parse(new InputStreamReader(getMethod.getResponseBodyAsStream())); + parsing = true; + org.apache.abdera.model.Entry feedEntry = doc.getRoot(); + + if (provider.supportsFeedEntries()) { + + // Return the Atom entry + msg.setBody(feedEntry); + + } else { + + // Convert the feed entry to a data entry and return the data item + Entry entry = entry(feedEntry, provider.getItemClassType(), + provider.getItemXMLType(), provider.getMediator()); + msg.setBody(entry.getData()); + } + + } else if (status == 404) { + msg.setFaultBody(new NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + if (!parsing) { + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + getMethod.releaseConnection(); + } + } + + return msg; + } + } + + /** + * Post operation invoker + */ + public static class PostInvoker extends AtomBindingInvoker { + + public PostInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // Post an entry + Object[] args = (Object[])msg.getBody(); + org.apache.abdera.model.Entry feedEntry; + if (provider.supportsFeedEntries()) { + + // Expect an Atom entry + feedEntry = (org.apache.abdera.model.Entry)args[0]; + } else { + + // Expect a key and data item + Entry entry = new Entry(args[0], args[1]); + feedEntry = feedEntry(entry, provider.getItemClassType(), + provider.getItemXMLType(), provider.getMediator(), abderaFactory); + } + + // Send an HTTP POST + PostMethod postMethod = new PostMethod(uri); + postMethod.setRequestHeader("Authorization", authorizationHeader); + boolean parsing = false; + try { + + // Write the Atom entry + StringWriter writer = new StringWriter(); + feedEntry.writeTo(writer); + // postMethod.setRequestHeader("Content-type", "application/atom+xml; charset=utf-8"); + postMethod.setRequestHeader("Content-type", "application/atom+xml;type=entry"); + postMethod.setRequestEntity(new StringRequestEntity(writer.toString())); + + httpClient.executeMethod(postMethod); + int status = postMethod.getStatusCode(); + + // Read the Atom entry + if (status == 200 || status == 201) { + Document doc = abderaParser.parse(new InputStreamReader(postMethod.getResponseBodyAsStream())); + parsing = true; + org.apache.abdera.model.Entry createdEntry = doc.getRoot(); + + // Returns the created Atom entry ID + if (provider.supportsFeedEntries()) { + + // Returns the created entry + msg.setBody(createdEntry); + + } else { + + // Returns the id of the created entry + msg.setBody(createdEntry.getId().toString()); + } + + } else if (status == 404) { + msg.setFaultBody(new NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + if (!parsing) { + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + postMethod.releaseConnection(); + } + } + + return msg; + } + } + + /** + * Put operation invoker + */ + public static class PutInvoker extends AtomBindingInvoker { + + public PutInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // Put an entry + Object[] args = (Object[])msg.getBody(); + String id; + org.apache.abdera.model.Entry feedEntry; + if (provider.supportsFeedEntries()) { + + // Expect a key and Atom entry + id = (String)args[0]; + feedEntry = (org.apache.abdera.model.Entry)args[1]; + } else { + + // Expect a key and data item + id = (String)args[0]; + Entry entry = new Entry(id, args[1]); + feedEntry = feedEntry(entry, provider.getItemClassType(), + provider.getItemXMLType(), provider.getMediator(), abderaFactory); + } + + // Send an HTTP PUT + PutMethod putMethod = new PutMethod(uri + "/" + id); + putMethod.setRequestHeader("Authorization", authorizationHeader); + + try { + + // Write the Atom entry + StringWriter writer = new StringWriter(); + feedEntry.writeTo(writer); + putMethod.setRequestHeader("Content-type", "application/atom+xml; charset=utf-8"); + putMethod.setRequestEntity(new StringRequestEntity(writer.toString())); + + httpClient.executeMethod(putMethod); + int status = putMethod.getStatusCode(); + if (status == 200 || status == 201 || status == 412) { + + msg.setBody(null); + + } else if (status == 404) { + msg.setFaultBody(new NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + putMethod.releaseConnection(); + } + + return msg; + } + } + + /** + * Delete operation invoker + */ + public static class DeleteInvoker extends AtomBindingInvoker { + + public DeleteInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // Delete an entry + String id = (String)((Object[])msg.getBody())[0]; + + // Send an HTTP DELETE + DeleteMethod deleteMethod = new DeleteMethod(uri + "/" + id); + deleteMethod.setRequestHeader("Authorization", authorizationHeader); + try { + httpClient.executeMethod(deleteMethod); + int status = deleteMethod.getStatusCode(); + if (status == 200) { + msg.setBody(null); + + } else if (status == 404) { + msg.setFaultBody(new NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + deleteMethod.releaseConnection(); + } + + return msg; + } + } + + /** + * GetAll operation invoker + */ + public static class GetAllInvoker extends AtomBindingInvoker { + + public GetAllInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // Get a feed + + // Send an HTTP GET + GetMethod getMethod = new GetMethod(uri); + getMethod.setRequestHeader("Authorization", authorizationHeader); + boolean parsing = false; + try { + httpClient.executeMethod(getMethod); + int status = getMethod.getStatusCode(); + // AtomBindingInvoker.printResponseHeader( getMethod ); + + // Read the Atom feed + if (status == 200) { + Document doc = abderaParser.parse(new InputStreamReader(getMethod.getResponseBodyAsStream())); + parsing = true; + Feed feed = doc.getRoot(); + + if (provider.supportsFeedEntries()) { + + // Returns the Atom feed + msg.setBody(feed); + + } else { + + // Returns an array of data entries + List> entries = new ArrayList>(); + for (org.apache.abdera.model.Entry feedEntry: feed.getEntries()) { + Entry entry = entry(feedEntry, provider.getItemClassType(), + provider.getItemXMLType(), provider.getMediator()); + entries.add(entry); + } + msg.setBody(entries.toArray(new Entry[entries.size()])); + } + + } else if (status == 404) { + msg.setFaultBody(new NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + if (!parsing) { + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + getMethod.releaseConnection(); + } + } + + return msg; + } + } + + /** + * Query operation invoker + */ + public static class QueryInvoker extends AtomBindingInvoker { + + public QueryInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // Get a feed from a query + String queryString = (String)((Object[])msg.getBody())[0]; + + // Send an HTTP GET + GetMethod getMethod = new GetMethod(uri); + getMethod.setRequestHeader("Authorization", authorizationHeader); + getMethod.setQueryString(queryString); + boolean parsing = false; + try { + httpClient.executeMethod(getMethod); + int status = getMethod.getStatusCode(); + + // Read the Atom feed + if (status == 200) { + Document doc = abderaParser.parse(new InputStreamReader(getMethod.getResponseBodyAsStream())); + parsing = true; + Feed feed = doc.getRoot(); + + if (provider.supportsFeedEntries()) { + + // Returns the Atom feed + msg.setBody(feed); + + } else { + + // Returns an array of data entries + List> entries = new ArrayList>(); + for (org.apache.abdera.model.Entry feedEntry: feed.getEntries()) { + Entry entry = entry(feedEntry, provider.getItemClassType(), + provider.getItemXMLType(), provider.getMediator()); + entries.add(entry); + } + msg.setBody(entries.toArray(new Entry[entries.size()])); + } + + } else if (status == 404) { + msg.setFaultBody(new NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + if (!parsing) { + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + getMethod.releaseConnection(); + } + } + + return msg; + } + } + + /** + * PostMedia operation invoker + */ + public static class PostMediaInvoker extends AtomBindingInvoker { + + public PostMediaInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // PostInvoker can detect media by content type (non-Feed, non-Entry) + return super.invoke(msg); + } + } + + /** + * PutMedia operation invoker + */ + public static class PutMediaInvoker extends AtomBindingInvoker { + + public PutMediaInvoker(Operation operation, String uri, HttpClient httpClient, String authorizationHeader, AtomReferenceBindingProvider bindingProvider) { + super(operation, uri, httpClient, authorizationHeader, bindingProvider); + } + + @Override + public Message invoke(Message msg) { + // PutInvoker can detect media by content type (non-Feed, non-Entry) + return super.invoke(msg); + } + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java new file mode 100644 index 0000000000..f6c713fae7 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java @@ -0,0 +1,871 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom.provider; + +import static org.apache.tuscany.sca.binding.atom.provider.AtomBindingUtil.entry; +import static org.apache.tuscany.sca.binding.atom.provider.AtomBindingUtil.feedEntry; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.URL; +import java.net.URLDecoder; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.Enumeration; +import java.util.List; +import java.util.StringTokenizer; +import java.util.logging.Logger; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.xml.namespace.QName; + +import org.apache.abdera.Abdera; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.model.Category; +import org.apache.abdera.model.Collection; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Element; +import org.apache.abdera.model.Feed; +import org.apache.abdera.model.Link; +import org.apache.abdera.model.Service; +import org.apache.abdera.model.Workspace; +import org.apache.abdera.parser.ParseException; +import org.apache.abdera.parser.Parser; +import org.apache.abdera.writer.WriterFactory; + +import org.apache.commons.codec.binary.Base64; +import org.apache.tuscany.sca.binding.http.HTTPCacheContext; +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.apache.tuscany.sca.invocation.InvocationChain; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.invocation.Message; +import org.apache.tuscany.sca.invocation.MessageFactory; +import org.apache.tuscany.sca.runtime.RuntimeWire; + +/** + * A resource collection binding listener, implemented as a Servlet and + * registered in a Servlet host provided by the SCA hosting runtime. + * + * @version $Rev$ $Date$ + */ +class AtomBindingListenerServlet extends HttpServlet { + private static final Logger logger = Logger.getLogger(AtomBindingListenerServlet.class.getName()); + private static final long serialVersionUID = 1L; + + private static final Factory abderaFactory = Abdera.getNewFactory(); + private static final Parser abderaParser = Abdera.getNewParser(); + private static final String ETAG = "ETag"; + private static final String LASTMODIFIED = "Last-Modified"; + private static final String LOCATION = "Location"; + private static final String CONTENTLOCATION = "Content-Location"; + private static final SimpleDateFormat dateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss Z" ); // RFC 822 date time + + private RuntimeWire wire; + private Invoker getFeedInvoker; + private Invoker getAllInvoker; + private Invoker queryInvoker; + private Invoker getInvoker; + private Invoker postInvoker; + private Invoker postMediaInvoker; + private Invoker putInvoker; + private Invoker putMediaInvoker; + private Invoker deleteInvoker; + private MessageFactory messageFactory; + private String title; + private Mediator mediator; + private DataType itemClassType; + private DataType itemXMLType; + private boolean supportsFeedEntries; + + /** + * Constructs a new binding listener. + * + * @param wire + * @param messageFactory + * @param feedType + */ + AtomBindingListenerServlet(RuntimeWire wire, MessageFactory messageFactory, Mediator mediator, String title) { + this.wire = wire; + this.messageFactory = messageFactory; + this.mediator = mediator; + this.title = title; + + // Get the invokers for the supported operations + Operation getOperation = null; + for (InvocationChain invocationChain : this.wire.getInvocationChains()) { + invocationChain.setAllowsPassByReference(true); + Operation operation = invocationChain.getTargetOperation(); + String operationName = operation.getName(); + if (operationName.equals("getFeed")) { + getFeedInvoker = invocationChain.getHeadInvoker(); + } else if (operationName.equals("getAll")) { + getAllInvoker = invocationChain.getHeadInvoker(); + } else if (operationName.equals("query")) { + queryInvoker = invocationChain.getHeadInvoker(); + } else if (operationName.equals("get")) { + getInvoker = invocationChain.getHeadInvoker(); + getOperation = operation; + } else if (operationName.equals("put")) { + putInvoker = invocationChain.getHeadInvoker(); + } else if (operationName.equals("putMedia")) { + putMediaInvoker = invocationChain.getHeadInvoker(); + } else if (operationName.equals("post")) { + postInvoker = invocationChain.getHeadInvoker(); + } else if (operationName.equals("postMedia")) { + postMediaInvoker = invocationChain.getHeadInvoker(); + } else if (operationName.equals("delete")) { + deleteInvoker = invocationChain.getHeadInvoker(); + } + } + + // Determine the collection item type + itemXMLType = new DataTypeImpl>(String.class.getName(), String.class, String.class); + Class itemClass = getOperation.getOutputType().getPhysical(); + if (itemClass == org.apache.abdera.model.Entry.class) { + supportsFeedEntries = true; + } + DataType outputType = getOperation.getOutputType(); + QName qname = outputType.getLogical().getElementName(); + qname = new QName(qname.getNamespaceURI(), itemClass.getSimpleName()); + itemClassType = new DataTypeImpl("java:complexType", itemClass, new XMLType(qname, null)); + + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + + // No authentication required for a get request + + // Test for any cache info in the request + HTTPCacheContext cacheContext = null; + try { + cacheContext = HTTPCacheContext.getCacheContextFromRequest( request ); + } catch ( java.text.ParseException e ) { + } + // System.out.println( "AtomBindingListener.doGet cache context=" + cacheContext ); + + // Get the request path + int servletPathLength = request.getContextPath().length() + request.getServletPath().length(); + String path = URLDecoder.decode(request.getRequestURI().substring(servletPathLength), "UTF-8"); + + logger.fine("get " + request.getRequestURI()); + + // Handle an Atom request + if (path != null && path.equals("/atomsvc")) { + /* + + + + resource + + entries + application/atom+xml;type=entry + + + + + */ + + // Return the Atom service document + response.setContentType("application/atomsvc+xml; charset=utf-8"); + + String href = request.getRequestURL().toString(); + href = href.substring(0, href.length() - "/atomsvc".length()); + + String workspaceURL = new String( href ); + int pathIndex = workspaceURL.indexOf( request.getServletPath() ); + if ( -1 != pathIndex ) + workspaceURL = workspaceURL.substring( 0, pathIndex ) + "/"; + + Service service = abderaFactory.newService(); + //service.setText("service"); + + Workspace workspace = abderaFactory.newWorkspace(); + if ( title != null ) + workspace.setTitle(title); + else + workspace.setTitle("workspace"); + workspace.setBaseUri( new IRI( workspaceURL )); + + Collection collection = workspace.addCollection("collection", href ); + Feed feed = getFeed( request ); + if ( feed != null ) { + String title = feed.getTitle(); + if ( title != null ) + collection.setTitle(title); + else + collection.setTitle("entries"); + collection.addAccepts("application/atom+xml;type=feed"); + collection.addAccepts("application/json;type=feed"); + collection.addAccepts("application/atom+xml;type=entry"); + collection.addAccepts("application/json;type=entry"); + List categories = feed.getCategories(); + if ( categories != null ) { + collection.addCategories(categories, false, null); + } else + collection.addCategories().setFixed(false); + + } else { + collection.setTitle("entries"); + // collection.addAccepts("application/atom+xml;type=feed"); + collection.addAccepts("application/atom+xml; type=entry"); + collection.addAccepts("application/json;type=entry"); + collection.addCategories().setFixed(false); + } + workspace.addCollection(collection); + + service.addWorkspace(workspace); + + //FIXME add prettyPrint support + try { + service.getDocument().writeTo(response.getOutputStream()); + } catch (IOException ioe) { + throw new ServletException(ioe); + } + + } else if (path == null || path.length() == 0 || path.equals("/")) { + + // Return a feed containing the entries in the collection + Feed feed = getFeed( request ); + if (feed != null) { + String feedETag = null; + if (feed.getId() != null) + feedETag = feed.getId().toString(); + Date feedUpdated = feed.getUpdated(); + // Test request for predicates. + String predicate = request.getHeader( "If-Match" ); + if (( predicate != null ) && ( !predicate.equals(feedETag) )) { + // No match, should short circuit + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return; + } + predicate = request.getHeader( "If-None-Match" ); + if (( predicate != null ) && ( predicate.equals(feedETag) )) { + // Match, should short circuit + response.sendError(HttpServletResponse.SC_NOT_MODIFIED); + return; + } + if ( feedUpdated != null ) { + predicate = request.getHeader( "If-Unmodified-Since" ); + if ( predicate != null ) { + try { + Date predicateDate = dateFormat.parse( predicate ); + if ( predicateDate.compareTo( feedUpdated ) < 0 ) { + // Match, should short circuit + response.sendError(HttpServletResponse.SC_NOT_MODIFIED); + return; + } + } catch ( java.text.ParseException e ) { + // Ignore and move on + } + } + predicate = request.getHeader( "If-Modified-Since" ); + if ( predicate != null ) { + try { + Date predicateDate = dateFormat.parse( predicate ); + if ( predicateDate.compareTo( feedUpdated ) > 0 ) { + // Match, should short circuit + response.sendError(HttpServletResponse.SC_NOT_MODIFIED); + return; + } + } catch ( java.text.ParseException e ) { + // Ignore and move on + } + } + } + // Provide Etag based on Id and time if given. + // Ignore if not given. (Browser may cache if trivial ETag is given.) + if ( feedETag != null ) + response.addHeader(ETAG, feedETag ); + if ( feedUpdated != null ) + response.addHeader(LASTMODIFIED, dateFormat.format( feedUpdated )); + + // Content negotiation + String acceptType = request.getHeader( "Accept" ); + String preferredType = getContentPreference( acceptType ); + if (( preferredType != null ) && ((preferredType.indexOf( "json") > -1) || (preferredType.indexOf( "JSON") > -1 ))) { + // JSON response body + response.setContentType("application/json;type=feed"); + + try { + Abdera abdera = new Abdera(); + WriterFactory wf = abdera.getWriterFactory(); + org.apache.abdera.writer.Writer json = wf.getWriter("json"); + feed.writeTo(json, response.getWriter()); + } catch (Exception e) { + throw new ServletException(e); + } + + } else { + // Write the Atom feed + response.setContentType("application/atom+xml;type=feed"); + try { + feed.getDocument().writeTo(response.getOutputStream()); + } catch (IOException ioe) { + throw new ServletException(ioe); + } + } + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + + } else if (path.startsWith("/")) { + // Return a specific entry in the collection + org.apache.abdera.model.Entry feedEntry; + + // Invoke the get operation on the service implementation + Message requestMessage = messageFactory.createMessage(); + String id = path.substring(1); + requestMessage.setBody(new Object[] {id}); + Message responseMessage = getInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + throw new ServletException((Throwable)responseMessage.getBody()); + } + if (supportsFeedEntries) { + // The service implementation returns a feed entry + feedEntry = responseMessage.getBody(); + } else { + // The service implementation only returns a data item, create an entry + // from it + Entry entry = new Entry(id, responseMessage.getBody()); + feedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory); + } + // Write the Atom entry + if (feedEntry != null) { + String entryETag = null; + if (feedEntry.getId() != null) + entryETag = feedEntry.getId().toString(); + Date entryUpdated = feedEntry.getUpdated(); + if ( entryUpdated != null ) + response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated )); + // TODO Check If-Modified-Since If-Unmodified-Since predicates against LASTMODIFIED. + // If true return 304 and null body. + + Link link = feedEntry.getSelfLink(); + if (link != null) { + response.addHeader(LOCATION, link.getHref().toString()); + } else { + link = feedEntry.getLink( "Edit" ); + if (link != null) { + response.addHeader(LOCATION, link.getHref().toString()); + } + } + + // Test request for predicates. + String predicate = request.getHeader( "If-Match" ); + if (( predicate != null ) && ( !predicate.equals(entryETag) )) { + // No match, should short circuit + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return; + } + predicate = request.getHeader( "If-None-Match" ); + if (( predicate != null ) && ( predicate.equals(entryETag) )) { + // Match, should short circuit + response.sendError(HttpServletResponse.SC_NOT_MODIFIED); + return; + } + if ( entryUpdated != null ) { + predicate = request.getHeader( "If-Unmodified-Since" ); + if ( predicate != null ) { + try { + Date predicateDate = dateFormat.parse( predicate ); + if ( predicateDate.compareTo( entryUpdated ) < 0 ) { + // Match, should short circuit + response.sendError(HttpServletResponse.SC_NOT_MODIFIED); + return; + } + } catch ( java.text.ParseException e ) { + // Ignore and move on + } + } + predicate = request.getHeader( "If-Modified-Since" ); + if ( predicate != null ) { + try { + Date predicateDate = dateFormat.parse( predicate ); + if ( predicateDate.compareTo( entryUpdated ) > 0 ) { + // Match, should short circuit + response.sendError(HttpServletResponse.SC_NOT_MODIFIED); + return; + } + } catch ( java.text.ParseException e ) { + // Ignore and move on + } + } + } + // Provide Etag based on Id and time if given. + // Ignore if not given. (Browser may cache if trivial ETag is given.) + if (entryETag != null) + response.addHeader(ETAG, entryETag ); + if ( entryUpdated != null ) + response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated )); + + // Content negotiation + String acceptType = request.getHeader( "Accept" ); + String preferredType = getContentPreference( acceptType ); + if (( preferredType != null ) && ((preferredType.indexOf( "json") > -1) || (preferredType.indexOf( "JSON") > -1 ))) { + // JSON response body + response.setContentType("application/json;type=entry"); + try { + Abdera abdera = new Abdera(); + WriterFactory wf = abdera.getWriterFactory(); + org.apache.abdera.writer.Writer json = wf.getWriter("json"); + feedEntry.writeTo(json, response.getWriter()); + } catch (Exception e) { + throw new ServletException(e); + } + } else { + // XML response body + response.setContentType("application/atom+xml;type=entry"); + try { + feedEntry.writeTo(getWriter(response)); + } catch (IOException ioe) { + throw new ServletException(ioe); + } + } + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } else { + // Path doesn't match any known pattern + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + + } + + protected Feed getFeed( HttpServletRequest request ) throws ServletException { + if (supportsFeedEntries) { + // The service implementation supports feed entries, invoke its getFeed operation + Message requestMessage = messageFactory.createMessage(); + Message responseMessage; + if (request.getQueryString() != null) { + requestMessage.setBody(new Object[] {request.getQueryString()}); + responseMessage = queryInvoker.invoke(requestMessage); + } else { + responseMessage = getFeedInvoker.invoke(requestMessage); + } + if (responseMessage.isFault()) { + throw new ServletException((Throwable)responseMessage.getBody()); + } + return (Feed)responseMessage.getBody(); + } else { + + // The service implementation does not support feed entries, + // invoke its getAll operation to get the data item collection, then create + // feed entries from the items + Message requestMessage = messageFactory.createMessage(); + Message responseMessage; + if (request.getQueryString() != null) { + requestMessage.setBody(new Object[] {request.getQueryString()}); + responseMessage = queryInvoker.invoke(requestMessage); + } else { + responseMessage = getAllInvoker.invoke(requestMessage); + } + if (responseMessage.isFault()) { + throw new ServletException((Throwable)responseMessage.getBody()); + } + Entry[] collection = + (Entry[])responseMessage.getBody(); + if (collection != null) { + + // Create the feed + Feed feed = abderaFactory.newFeed(); + + // Set the feed title + if (title != null) { + feed.setTitle(title); + } else { + feed.setTitle("Feed"); + } + // All feeds must provide Id and updated elements. + // However, some do not, so provide some program protection. + feed.setId( "Feed" + feed.hashCode()); + Date responseLastModified = new Date( 0 ); + + // Add entries to the feed + for (Entry entry: collection) { + org.apache.abdera.model.Entry feedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory); + // Use the most recent entry update as the feed update + Date entryUpdated = feedEntry.getUpdated(); + if (( entryUpdated != null ) && (entryUpdated.compareTo( responseLastModified ) > 0 )) + responseLastModified = entryUpdated; + feed.addEntry(feedEntry); + } + // If no entries were newly updated, + if ( responseLastModified.compareTo( new Date( 0 ) ) == 0 ) + responseLastModified = new Date(); + return feed; + } + } + return null; + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, + IOException { + // Authenticate the user + String user = processAuthorizationHeader(request); + if (user == null) { + unauthorized(response); + return; + } + + // Get the request path + String path = URLDecoder.decode(request.getRequestURI().substring(request.getServletPath().length()), "UTF-8"); + + if (path == null || path.length() == 0 || path.equals("/")) { + org.apache.abdera.model.Entry createdFeedEntry = null; + + // Create a new Atom entry + String contentType = request.getContentType(); + if (contentType != null && contentType.startsWith("application/atom+xml")) { + + // Read the entry from the request + org.apache.abdera.model.Entry feedEntry; + try { + Document doc = abderaParser.parse(request.getReader()); + feedEntry = doc.getRoot(); + } catch (ParseException pe) { + throw new ServletException(pe); + } + + // Let the component implementation create it + if (supportsFeedEntries) { + + // The service implementation supports feed entries, pass the entry to it + Message requestMessage = messageFactory.createMessage(); + requestMessage.setBody(new Object[] {feedEntry}); + Message responseMessage = postInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + throw new ServletException((Throwable)responseMessage.getBody()); + } + createdFeedEntry = responseMessage.getBody(); + } else { + + // The service implementation does not support feed entries, pass the data item to it + Message requestMessage = messageFactory.createMessage(); + Entry entry = entry(feedEntry, itemClassType, itemXMLType, mediator); + requestMessage.setBody(new Object[] {entry.getKey(), entry.getData()}); + Message responseMessage = postInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + throw new ServletException((Throwable)responseMessage.getBody()); + } + entry.setKey(responseMessage.getBody()); + createdFeedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory); + } + + } else if (contentType != null) { + // Create a new media entry + + // Get incoming headers + String title = request.getHeader("Title"); + String slug = request.getHeader("Slug"); + + // Let the component implementation create the media entry + Message requestMessage = messageFactory.createMessage(); + requestMessage.setBody(new Object[] {title, slug, contentType, request.getInputStream()}); + Message responseMessage = postMediaInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + throw new ServletException((Throwable)responseMessage.getBody()); + } + createdFeedEntry = responseMessage.getBody(); + + // Transfer media info to response header. + // Summary is a comma separated list of header properties. + String summary = createdFeedEntry.getSummary(); + addPropertiesToHeader( response, summary ); + + } else { + response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + } + + // A new entry for non-media was created successfully. + if (createdFeedEntry != null) { + // Set location of the created entry in the Location header + IRI feedId = createdFeedEntry.getId(); + if ( feedId != null ) + response.addHeader(ETAG, "\"" + feedId.toString() + "\"" ); + Date entryUpdated = createdFeedEntry.getUpdated(); + if ( entryUpdated != null ) + response.addHeader(LASTMODIFIED, dateFormat.format( entryUpdated )); + Link link = createdFeedEntry.getSelfLink(); + if (link != null) { + response.addHeader(LOCATION, link.getHref().toString()); + } + Link editLink = createdFeedEntry.getEditLink(); + if (editLink != null) { + response.addHeader(LOCATION, editLink.getHref().toString()); + } + Link editMediaLink = createdFeedEntry.getEditMediaLink(); + if (editMediaLink != null) { + response.addHeader(CONTENTLOCATION, editMediaLink.getHref().toString()); + } + + // Write the created Atom entry + response.setStatus(HttpServletResponse.SC_CREATED); + response.setContentType("application/atom+xml;type=entry"); + try { + createdFeedEntry.writeTo(getWriter(response)); + } catch (ParseException pe) { + throw new ServletException(pe); + } + + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + private Writer getWriter(HttpServletResponse response) throws UnsupportedEncodingException, IOException { + Writer writer = new OutputStreamWriter(response.getOutputStream(), "UTF-8"); + return writer; + } + + @Override + protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // Authenticate the user + String user = processAuthorizationHeader(request); + if (user == null) { + unauthorized(response); + return; + } + + // Get the request path + String path = request.getRequestURI().substring(request.getServletPath().length()); + + if (path != null && path.startsWith("/")) { + String id = path.substring(1); + + // Update an Atom entry + String contentType = request.getContentType(); + if (contentType != null && contentType.startsWith("application/atom+xml")) { + + // Read the entry from the request + org.apache.abdera.model.Entry feedEntry; + try { + Document doc = abderaParser.parse(request.getReader()); + feedEntry = doc.getRoot(); + } catch (ParseException pe) { + throw new ServletException(pe); + } + + // Let the component implementation create it + if (supportsFeedEntries) { + // The service implementation supports feed entries, pass the entry to it + Message requestMessage = messageFactory.createMessage(); + requestMessage.setBody(new Object[] {id, feedEntry}); + Message responseMessage = putInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + Object body = responseMessage.getBody(); + if (body.getClass().getName().endsWith(".NotFoundException")) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } else { + throw new ServletException((Throwable)responseMessage.getBody()); + } + } + } else { + // The service implementation does not support feed entries, pass the data item to it + Message requestMessage = messageFactory.createMessage(); + Entry entry = entry(feedEntry, itemClassType, itemXMLType, mediator); + requestMessage.setBody(new Object[] {entry.getKey(), entry.getData()}); + Message responseMessage = putInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + Object body = responseMessage.getBody(); + if (body.getClass().getName().endsWith(".NotFoundException")) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } else { + throw new ServletException((Throwable)responseMessage.getBody()); + } + } + } + + } else if (contentType != null) { + + // Update a media entry + + // Let the component implementation create the media entry + Message requestMessage = messageFactory.createMessage(); + requestMessage.setBody(new Object[] {id, contentType, request.getInputStream()}); + Message responseMessage = putMediaInvoker.invoke(requestMessage); + + Object body = responseMessage.getBody(); + if (responseMessage.isFault()) { + if (body.getClass().getName().endsWith(".NotFoundException")) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } else { + throw new ServletException((Throwable)responseMessage.getBody()); + } + } + + // Transfer content to response header. + + } else { + response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE); + } + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } + } + + @Override + protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, + IOException { + + // Authenticate the user + String user = processAuthorizationHeader(request); + if (user == null) { + unauthorized(response); + return; + } + + // Get the request path + String path = URLDecoder.decode(request.getRequestURI().substring(request.getServletPath().length()), "UTF-8"); + + String id; + if (path != null && path.startsWith("/")) { + id = path.substring(1); + } else { + id = ""; + } + + // Delete a specific entry from the collection + Message requestMessage = messageFactory.createMessage(); + requestMessage.setBody(new Object[] {id}); + Message responseMessage = deleteInvoker.invoke(requestMessage); + if (responseMessage.isFault()) { + Object body = responseMessage.getBody(); + if (body.getClass().getName().endsWith(".NotFoundException")) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + } else { + throw new ServletException((Throwable)responseMessage.getBody()); + } + } + } + + /** + * Process the authorization header + * + * @param request + * @return + * @throws ServletException + */ + private String processAuthorizationHeader(HttpServletRequest request) throws ServletException { + + // FIXME temporarily disabling this as it doesn't work with all browsers + if (true) + return "admin"; + + try { + String authorization = request.getHeader("Authorization"); + if (authorization != null) { + StringTokenizer tokens = new StringTokenizer(authorization); + if (tokens.hasMoreTokens()) { + String basic = tokens.nextToken(); + if (basic.equalsIgnoreCase("Basic")) { + String credentials = tokens.nextToken(); + String userAndPassword = new String(Base64.decodeBase64(credentials.getBytes())); + int colon = userAndPassword.indexOf(":"); + if (colon != -1) { + String user = userAndPassword.substring(0, colon); + String password = userAndPassword.substring(colon + 1); + + // Authenticate the User. + if (authenticate(user, password)) { + return user; + } + } + } + } + } + } catch (Exception e) { + throw new ServletException(e); + } + return null; + } + + /** + * Authenticate a user. + * + * @param user + * @param password + * @return + */ + private boolean authenticate(String user, String password) { + // TODO Handle this using SCA security policies + return ("admin".equals(user) && "admin".equals(password)); + } + + /** + * Reject an unauthorized request. + * + * @param response + */ + private void unauthorized(HttpServletResponse response) throws IOException { + response.setHeader("WWW-Authenticate", "BASIC realm=\"Tuscany\""); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + } + + public static String getContentPreference( String acceptType ) { + if (( acceptType == null ) || ( acceptType.length() < 1 )) { + return "application/atom+xml"; + } + StringTokenizer st = new StringTokenizer( acceptType, "," ); + if ( st.hasMoreTokens() ) + return st.nextToken(); + return "application/atom+xml"; + } + + /** Take a list of key values and add them to the header. + * For instance "Content-Type=image/gif,Content-Length=14201" + * @param response + * @param properties + */ + public static void addPropertiesToHeader( HttpServletResponse response, String properties ) { + if ( properties == null ) return; + StringTokenizer props = new StringTokenizer( properties, ","); + while( props.hasMoreTokens()) { + String prop = props.nextToken(); + StringTokenizer keyVal = new StringTokenizer( prop, "="); + String key = null; + String val = null; + if ( keyVal.hasMoreTokens() ) + key = keyVal.nextToken(); + if ( keyVal.hasMoreTokens() ) + val = keyVal.nextToken(); + if (( key != null ) && ( val != null )) { + response.addHeader(key, val); + } + } + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingProviderFactory.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingProviderFactory.java new file mode 100644 index 0000000000..b6b805e383 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingProviderFactory.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom.provider; + +import org.apache.tuscany.sca.binding.atom.AtomBinding; +import org.apache.tuscany.sca.contribution.ModelFactoryExtensionPoint; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.databinding.DataBindingExtensionPoint; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.databinding.TransformerExtensionPoint; +import org.apache.tuscany.sca.databinding.impl.MediatorImpl; +import org.apache.tuscany.sca.host.http.ServletHost; +import org.apache.tuscany.sca.host.http.ServletHostExtensionPoint; +import org.apache.tuscany.sca.invocation.MessageFactory; +import org.apache.tuscany.sca.provider.BindingProviderFactory; +import org.apache.tuscany.sca.provider.ReferenceBindingProvider; +import org.apache.tuscany.sca.provider.ServiceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentReference; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; + +/** + * Implementation of a Binding provider factory for the Atom binding. + * + * @version $Rev$ $Date$ + */ +public class AtomBindingProviderFactory implements BindingProviderFactory { + + private MessageFactory messageFactory; + private Mediator mediator; + private ServletHost servletHost; + + public AtomBindingProviderFactory(ExtensionPointRegistry extensionPoints) { + ServletHostExtensionPoint servletHosts = extensionPoints.getExtensionPoint(ServletHostExtensionPoint.class); + this.servletHost = servletHosts.getServletHosts().get(0); + ModelFactoryExtensionPoint modelFactories = extensionPoints.getExtensionPoint(ModelFactoryExtensionPoint.class); + this.messageFactory = modelFactories.getFactory(MessageFactory.class); + this.mediator = new MediatorImpl(extensionPoints.getExtensionPoint(DataBindingExtensionPoint.class), + extensionPoints.getExtensionPoint(TransformerExtensionPoint.class)); + } + + public ReferenceBindingProvider createReferenceBindingProvider(RuntimeComponent component, + RuntimeComponentReference reference, + AtomBinding binding) { + return new AtomReferenceBindingProvider(component, reference, binding, mediator); + } + + public ServiceBindingProvider createServiceBindingProvider(RuntimeComponent component, + RuntimeComponentService service, + AtomBinding binding) { + return new AtomServiceBindingProvider(component, service, binding, servletHost, messageFactory, mediator); + } + + public Class getModelType() { + return AtomBinding.class; + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingUtil.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingUtil.java new file mode 100644 index 0000000000..62fde86122 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingUtil.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom.provider; + +import java.util.Date; + +import org.apache.abdera.factory.Factory; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Link; +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.data.collection.Item; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.interfacedef.DataType; + +/** + * Utility methods used in this package. + * + * @version $Rev$ $Date$ + */ +class AtomBindingUtil { + + /** + * Create a data item from an Atom entry. + * @param feedEntry + * @param itemClassType + * @param itemXMLType + * @param mediator + * @return + */ + static Entry entry(org.apache.abdera.model.Entry feedEntry, + DataType itemClassType, DataType itemXMLType, Mediator mediator) { + if (feedEntry != null) { + if (itemClassType.getPhysical() == Item.class) { + String key = feedEntry.getId().toString(); + + Item item = new Item(); + item.setTitle(feedEntry.getTitle()); + item.setContents(feedEntry.getContent()); + + for (Link link : feedEntry.getLinks()) { + if (link.getRel() == null || "self".equals(link.getRel())) { + if (item.getLink() == null) { + item.setLink(link.getHref().toString()); + } + } else if ("related".equals(link.getRel())) { + item.setRelated(link.getHref().toString()); + } else if ("alternate".equals(link.getRel())) { + item.setAlternate(link.getHref().toString()); + } + } + + item.setDate(feedEntry.getUpdated()); + + return new Entry(key, item); + + } else { + String key = null; + if ( feedEntry.getId() != null) { + key = feedEntry.getId().toString(); + } + + // Create the item from XML + if (feedEntry.getContentElement().getElements().size() == 0) { + return null; + } + + String value = feedEntry.getContent(); + Object data = mediator.mediate(value, itemXMLType, itemClassType, null); + + return new Entry(key, data); + } + } else { + return null; + } + } + + /** + * Create an Atom entry for a key and item from a collection. + * @param entry + * @param itemClassType + * @param itemXMLType + * @param mediator + * @param factory + * @return + */ + static org.apache.abdera.model.Entry feedEntry(Entry entry, + DataType itemClassType, DataType itemXMLType, Mediator mediator, + Factory factory) { + Object key = entry.getKey(); + Object data = entry.getData(); + if (data instanceof Item) { + Item item = (Item)data; + + org.apache.abdera.model.Entry feedEntry = factory.newEntry(); + if (key != null) { + feedEntry.setId(key.toString()); + } + feedEntry.setTitle(item.getTitle()); + feedEntry.setContentAsHtml(item.getContents()); + + String href = item.getLink(); + if (href == null && key != null) { + href = key.toString(); + } + + if (href != null) { + feedEntry.addLink(href); + } + String related = item.getRelated(); + if (related != null) { + feedEntry.addLink(related, "related"); + } + String alternate = item.getAlternate(); + if (alternate != null) { + feedEntry.addLink(alternate, "alternate"); + } + + Date date = item.getDate(); + if (date != null) { + feedEntry.setUpdated(date); + } + return feedEntry; + + } else if (data != null) { + org.apache.abdera.model.Entry feedEntry = factory.newEntry(); + feedEntry.setId(key.toString()); + feedEntry.setTitle("item"); + + + // Convert the item to XML + String value = mediator.mediate(data, itemClassType, itemXMLType, null).toString(); + + Content content = factory.newContent(); + content.setContentType(Content.Type.XML); + content.setValue(value); + + feedEntry.setContentElement(content); + + feedEntry.addLink(key.toString()); + + return feedEntry; + } else { + return null; + } + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomReferenceBindingProvider.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomReferenceBindingProvider.java new file mode 100644 index 0000000000..f2b1d1f72b --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomReferenceBindingProvider.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom.provider; + +import java.net.URI; + +import javax.xml.namespace.QName; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.httpclient.Credentials; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpConnectionManager; +import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.tuscany.sca.binding.atom.AtomBinding; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.interfacedef.DataType; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.interfacedef.Operation; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +import org.apache.tuscany.sca.interfacedef.util.XMLType; +import org.apache.tuscany.sca.invocation.Invoker; +import org.apache.tuscany.sca.provider.ReferenceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentReference; + +/** + * Implementation of the Atom binding provider. + * + * @version $Rev$ $Date$ + */ +class AtomReferenceBindingProvider implements ReferenceBindingProvider { + + private RuntimeComponentReference reference; + private AtomBinding binding; + private String authorizationHeader; + private HttpClient httpClient; + private Mediator mediator; + private DataType itemClassType; + private DataType itemXMLType; + private boolean supportsFeedEntries; + + /** + * Constructs a new AtomReferenceBindingProvider + * @param component + * @param reference + * @param binding + * @param mediator + */ + AtomReferenceBindingProvider(RuntimeComponent component, + RuntimeComponentReference reference, + AtomBinding binding, + Mediator mediator) { + this.reference = reference; + this.binding = binding; + this.mediator = mediator; + + // Prepare authorization header + String authorization = "admin" + ":" + "admin"; + authorizationHeader = "Basic " + new String(Base64.encodeBase64(authorization.getBytes())); + + // Create an HTTP client + HttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); + connectionManager.getParams().setDefaultMaxConnectionsPerHost(10); + connectionManager.getParams().setConnectionTimeout(60000); + httpClient = new HttpClient(connectionManager); + } + + public Invoker createInvoker(Operation operation) { + + String operationName = operation.getName(); + if (operationName.equals("get")) { + + // Determine the collection item type + itemXMLType = new DataTypeImpl>(String.class.getName(), String.class, String.class); + Class itemClass = operation.getOutputType().getPhysical(); + DataType outputType = operation.getOutputType(); + QName qname = outputType.getLogical().getElementName(); + qname = new QName(qname.getNamespaceURI(), itemClass.getSimpleName()); + itemClassType = new DataTypeImpl("java:complexType", itemClass, new XMLType(qname, null)); + if (itemClassType.getPhysical() == org.apache.abdera.model.Entry.class) { + supportsFeedEntries = true; + } + + return new AtomBindingInvoker.GetInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + + } else if (operationName.equals("post")) { + return new AtomBindingInvoker.PostInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } else if (operationName.equals("put")) { + return new AtomBindingInvoker.PutInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } else if (operationName.equals("delete")) { + return new AtomBindingInvoker.DeleteInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } else if (operationName.equals("getFeed") || operationName.equals("getAll")) { + return new AtomBindingInvoker.GetAllInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } else if (operationName.equals("postMedia")) { + return new AtomBindingInvoker.PostMediaInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } else if (operationName.equals("putMedia")) { + return new AtomBindingInvoker.PutMediaInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } else if (operationName.equals("query")) { + return new AtomBindingInvoker.QueryInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } + + return new AtomBindingInvoker(operation, binding.getURI(), httpClient, authorizationHeader, this); + } + + public InterfaceContract getBindingInterfaceContract() { + return reference.getInterfaceContract(); + } + + public void start() { + + // Configure the HTTP client credentials + Credentials credentials = new UsernamePasswordCredentials("admin", "admin"); + httpClient.getParams().setAuthenticationPreemptive(true); + URI uri = URI.create(binding.getURI()); + httpClient.getState().setCredentials(new AuthScope(uri.getHost(), uri.getPort()), credentials); + + // Find the get operation on the reference interface + if (true) { + return; + } + } + + public void stop() { + + } + + public boolean supportsOneWayInvocation() { + return false; + } + + /** + * Returns the mediator. + * @return + */ + Mediator getMediator() { + return mediator; + } + + /** + * Returns the item class type. + * @return + */ + DataType getItemClassType() { + return itemClassType; + } + + /** + * Returns the item XML type. + * @return + */ + DataType getItemXMLType() { + return itemXMLType; + } + + /** + * Returns true if the invoker should work with Atom + * feed entries. + * @return + */ + boolean supportsFeedEntries() { + return supportsFeedEntries; + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomServiceBindingProvider.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomServiceBindingProvider.java new file mode 100644 index 0000000000..e1d470f5a3 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomServiceBindingProvider.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom.provider; + +import org.apache.tuscany.sca.binding.atom.AtomBinding; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.host.http.ServletHost; +import org.apache.tuscany.sca.interfacedef.InterfaceContract; +import org.apache.tuscany.sca.invocation.MessageFactory; +import org.apache.tuscany.sca.provider.ServiceBindingProvider; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.apache.tuscany.sca.runtime.RuntimeWire; + +/** + * Implementation of the Atom binding provider. + * + * @version $Rev$ $Date$ + */ +class AtomServiceBindingProvider implements ServiceBindingProvider { + + private RuntimeComponentService service; + private AtomBinding binding; + private ServletHost servletHost; + private MessageFactory messageFactory; + private String servletMapping; + private Mediator mediator; + + AtomServiceBindingProvider(RuntimeComponent component, + RuntimeComponentService service, + AtomBinding binding, + ServletHost servletHost, + MessageFactory messageFactory, + Mediator mediator) { + this.service = service; + this.binding = binding; + this.servletHost = servletHost; + this.messageFactory = messageFactory; + this.mediator = mediator; + } + + public InterfaceContract getBindingInterfaceContract() { + return service.getInterfaceContract(); + } + + public boolean supportsOneWayInvocation() { + return false; + } + + public void start() { + RuntimeComponentService componentService = (RuntimeComponentService)service; + RuntimeWire wire = componentService.getRuntimeWire(binding); + + AtomBindingListenerServlet servlet = + new AtomBindingListenerServlet(wire, messageFactory, mediator, binding.getTitle()); + + servletMapping = binding.getURI(); + if (!servletMapping.endsWith("/")) { + servletMapping += "/"; + } + if (!servletMapping.endsWith("*")) { + servletMapping += "*"; + } + servletHost.addServletMapping(servletMapping, servlet); + } + + public void stop() { + servletHost.removeServletMapping(servletMapping); + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/resources/META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/resources/META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory new file mode 100644 index 0000000000..1b9052299c --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/main/resources/META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory @@ -0,0 +1,19 @@ +# 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. + +# Implementation class for the binding extension +org.apache.tuscany.sca.binding.atom.provider.AtomBindingProviderFactory;model=org.apache.tuscany.sca.binding.atom.AtomBinding diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomDeleteTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomDeleteTestCase.java new file mode 100644 index 0000000000..f9ac5f9c78 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomDeleteTestCase.java @@ -0,0 +1,92 @@ +package org.apache.tuscany.sca.binding.atom; + +import java.util.UUID; + +import junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.host.embedded.SCADomain; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AtomDeleteTestCase { + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>AtomDeleteTestCase.init entry"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + scaConsumerDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Consumer.composite"); + testService = scaConsumerDomain.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + } + + @AfterClass + public static void destroy() throws Exception { + // System.out.println(">>>AtomDeleteTestCase.destroy entry"); + scaConsumerDomain.close(); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull(scaConsumerDomain); + Assert.assertNotNull(testService); + Assert.assertNotNull(abdera); + } + + @Test + public void testAtomDelete() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Sponge Bob"); + System.out.println(">>> post entry= " + postEntry.getTitle()); + + Entry newEntry = resourceCollection.post(postEntry); + System.out.println("<<< Entry posted for " + newEntry.getTitle()); + + System.out.println(">>> get id=" + newEntry.getId()); + + resourceCollection.delete(newEntry.getId().toString()); + + } + + @Test + public void testAtomDeleteException() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + try { + // Generates custom ID + String id = "urn:uuid:customer-" + UUID.randomUUID().toString(); + resourceCollection.delete(id); + } catch (Exception e) { + // ID doesn't match with the existing IDs and NotFoundException is + // thrown + Assert.assertEquals("NotFoundException", e.getClass().getSimpleName()); + } + + } + + private Entry postEntry(String value) { + Entry entry = abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomGetTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomGetTestCase.java new file mode 100644 index 0000000000..8d78d167c3 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomGetTestCase.java @@ -0,0 +1,97 @@ +package org.apache.tuscany.sca.binding.atom; + +import junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.host.embedded.SCADomain; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AtomGetTestCase { + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>AtomGetTestCase.init entry"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + scaConsumerDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Consumer.composite"); + testService = scaConsumerDomain.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + } + + @AfterClass + public static void destroy() throws Exception { + // System.out.println(">>>AtomGetTestCase.destroy entry"); + scaConsumerDomain.close(); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull(scaConsumerDomain); + Assert.assertNotNull(testService); + Assert.assertNotNull(abdera); + } + + @Test + public void testAtomGet() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Sponge Bob"); + System.out.println(">>> post entry= " + postEntry.getTitle()); + + Entry newEntry = resourceCollection.post(postEntry); + System.out.println("<<< Entry posted for " + newEntry.getTitle()); + + System.out.println(">>> get id=" + newEntry.getId()); + + Entry getEntry = resourceCollection.get(newEntry.getId().toString()); + + Assert.assertEquals(newEntry.getTitle(), getEntry.getTitle()); + System.out.println("<<< get id=" + getEntry.getId() + " entry=" + getEntry.getTitle()); + } + + @Test + public void testAtomGetException() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Sponge Bob"); + System.out.println(">>> post entry= " + postEntry.getTitle()); + + Entry newEntry = resourceCollection.post(postEntry); + System.out.println("<<< Entry posted for " + newEntry.getTitle()); + System.out.println(newEntry.getId()); + + // Delete the entry to force the Collection to throw NotFoundException + resourceCollection.delete(newEntry.getId().toString()); + + try { + resourceCollection.get(newEntry.getId().toString()); + } catch (Exception e) { + Assert.assertEquals("NotFoundException", e.getClass().getSimpleName()); + } + } + + private Entry postEntry(String value) { + Entry entry = abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPostTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPostTestCase.java new file mode 100644 index 0000000000..ef5fe5a873 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPostTestCase.java @@ -0,0 +1,86 @@ +package org.apache.tuscany.sca.binding.atom; + +import junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.host.embedded.SCADomain; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AtomPostTestCase { + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>AtomPostTestCase.init entry"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + scaConsumerDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Consumer.composite"); + testService = scaConsumerDomain.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + } + + @AfterClass + public static void destroy() throws Exception { + System.out.println(">>>AtomPostTestCase.destroy entry"); + scaConsumerDomain.close(); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull(scaConsumerDomain); + Assert.assertNotNull(testService); + Assert.assertNotNull(abdera); + } + + @Test + public void testAtomPost() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Sponge Bob"); + System.out.println(">>> post entry= " + postEntry.getTitle()); + + Entry newEntry = resourceCollection.post(postEntry); + + Assert.assertEquals(postEntry.getTitle(), newEntry.getTitle()); + + System.out.println("<<< new entry= " + newEntry.getTitle()); + + } + + @Test + public void testAtomPostException() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Exception_Test"); + + try { + resourceCollection.post(postEntry); + } catch (Exception e) { + Assert.assertEquals("HTTP status code: 500", e.getMessage()); + } + } + + private Entry postEntry(String value) { + Entry entry = abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPutTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPutTestCase.java new file mode 100644 index 0000000000..a5c1226780 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPutTestCase.java @@ -0,0 +1,104 @@ +package org.apache.tuscany.sca.binding.atom; + +import java.util.UUID; + +import junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.host.embedded.SCADomain; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class AtomPutTestCase { + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>AtomPutTestCase.init entry"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + scaConsumerDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Consumer.composite"); + testService = scaConsumerDomain.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + } + + @AfterClass + public static void destroy() throws Exception { + // System.out.println(">>>AtomPutTestCase.destroy entry"); + scaConsumerDomain.close(); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull(scaConsumerDomain); + Assert.assertNotNull(testService); + Assert.assertNotNull(abdera); + } + + @Test + public void testAtomPut() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Sponge Bob"); + System.out.println(">>> post entry= " + postEntry.getTitle()); + + Entry newEntry = resourceCollection.post(postEntry); + System.out.println("<<< Entry posted for " + newEntry.getTitle()); + System.out.println(newEntry.getId()); + + System.out.println(">>> put id=" + newEntry.getId() + " entry=" + newEntry.getTitle()); + resourceCollection.put(newEntry.getId().toString(), updateEntry(newEntry, "James Bond")); + System.out.println("<<< put id=" + newEntry.getId() + " entry=" + newEntry.getTitle()); + } + + @Test + public void testAtomPutException() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Sponge Bob"); + System.out.println(">>> post entry= " + postEntry.getTitle()); + + // Generate random ID to pass as parameter in PUT() -- + String id = "urn:uuid:customer-" + UUID.randomUUID().toString(); + try { + // ID doesn't match with the existing IDs and NotFoundException is thrown + resourceCollection.put(id, updateEntry(postEntry, "James Bond")); + } catch (Exception e) { + Assert.assertEquals("NotFoundException", e.getClass().getSimpleName()); + } + } + + private Entry postEntry(String value) { + Entry entry = abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + + private Entry updateEntry(Entry entry, String value) { + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomTestCaseUtils.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomTestCaseUtils.java new file mode 100644 index 0000000000..6beed6881f --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/AtomTestCaseUtils.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom; + +import java.io.IOException; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Base; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.protocol.client.ClientResponse; +import org.apache.abdera.protocol.client.RequestOptions; +import org.apache.abdera.writer.Writer; +import org.apache.abdera.writer.WriterFactory; + +/** + * Utilities to help print and test various aspects of entity tag support. + */ +public class AtomTestCaseUtils { + + public static void prettyPrint(Abdera abdera, Base doc) throws IOException { + WriterFactory factory = abdera.getWriterFactory(); + Writer writer = factory.getWriter("prettyxml"); + writer.writeTo(doc, System.out); + System.out.println(); + } + + public static void printRequestHeaders( String title, String indent, RequestOptions request ) { + System.out.println( title ); + if ( request == null ) { + System.out.println( indent + " request is null"); + return; + } + String [] headerNames = request.getHeaderNames(); + for ( String headerName: headerNames) { + String header = request.getHeader(headerName); + System.out.println( indent + " header name,value=" + headerName + "," + header ); + } + } + + public static void printResponseHeaders( String title, String indent, ClientResponse response ) { + System.out.println( title ); + if ( response == null ) { + System.out.println( indent + " response is null"); + return; + } + String [] headerNames = response.getHeaderNames(); + for ( String headerName: headerNames) { + String header = response.getHeader(headerName); + System.out.println( indent + " header name,value=" + headerName + "," + header ); + } + + } + + public static Entry newEntry(String value) { + Abdera abdera = new Abdera(); + Entry entry = abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + + public static Entry updateEntry(Entry entry, String value) { + Abdera abdera = new Abdera(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Consumer.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Consumer.java new file mode 100644 index 0000000000..34d3ed1d1c --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Consumer.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom; + +import org.apache.tuscany.sca.host.embedded.SCADomain; + +public class Consumer { + + public static void main(String[] args) throws Exception { + + SCADomain scaDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Consumer.composite"); + + CustomerClient testService = scaDomain.getService(CustomerClient.class, "CustomerClient"); + testService.testCustomerCollection(); + + scaDomain.close(); + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ConsumerProviderAtomTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ConsumerProviderAtomTestCase.java new file mode 100644 index 0000000000..9af85a970c --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ConsumerProviderAtomTestCase.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom; + +import java.util.Date; + +import junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.host.embedded.SCADomain; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Test case for the given package. + */ +public class ConsumerProviderAtomTestCase { + + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>AtomBindingIntegratedTestCase.init entry"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + scaConsumerDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Consumer.composite"); + testService = scaConsumerDomain.getService(CustomerClient.class,"CustomerClient"); + abdera = new Abdera(); + } + + @AfterClass + public static void destroy() throws Exception { + System.out.println(">>>AtomBindingIntegratedTestCase.destroy entry"); + scaConsumerDomain.close(); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull(scaConsumerDomain); + Assert.assertNotNull(testService); + Assert.assertNotNull(abdera); + } + + @Test + public void testEntry() throws Exception { + // System.out.println( getClass().getName() + ".testEntry entry" ); + Entry entry = abdera.newEntry(); + Assert.assertNotNull(entry); + + String testTitle = "Sponge Bob"; + entry.setTitle(testTitle); + Assert.assertEquals(testTitle, entry.getTitle()); + + String testContent = "This is the content"; + entry.setContent(testContent); + Assert.assertEquals(testContent, entry.getContent()); + + Date now = new Date(); + entry.setEdited(now); + Assert.assertEquals(now, entry.getEdited()); + + Date later = new Date(); + entry.setPublished(later); + Assert.assertEquals(later, entry.getPublished()); + + String testSummary = "This is the summary"; + entry.setSummary(testSummary); + Assert.assertEquals(testSummary, entry.getSummary()); + } + + @Test + public void testCustomerCollection() throws Exception { + System.out.println(getClass().getName() + ".testCustomerCollection entry"); + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry newEntry = newEntry("Sponge Bob"); + System.out.println(">>> post entry=" + newEntry.getTitle()); + newEntry = resourceCollection.post(newEntry); + System.out.println("<<< post id=" + newEntry.getId() + " entry=" + newEntry.getTitle()); + + newEntry = newEntry("Jane Bond"); + System.out.println(">>> post entry=" + newEntry.getTitle()); + newEntry = resourceCollection.post(newEntry); + System.out.println("<<< post id=" + newEntry.getId() + " entry=" + newEntry.getTitle()); + + System.out.println(">>> get id=" + newEntry.getId()); + Entry entry = resourceCollection.get(newEntry.getId().toString()); + System.out.println("<<< get id=" + entry.getId() + " entry=" + entry.getTitle()); + + System.out.println(">>> put id=" + newEntry.getId() + " entry=" + entry.getTitle()); + resourceCollection.put(entry.getId().toString(), updateEntry(entry, "James Bond")); + System.out.println("<<< put id=" + entry.getId() + " entry=" + entry.getTitle()); + + System.out.println(">>> delete id=" + entry.getId()); + resourceCollection.delete(entry.getId().toString()); + System.out.println("<<< delete id=" + entry.getId()); + + System.out.println(">>> get collection"); + Feed feed = resourceCollection.getFeed(); + System.out.println("<<< get collection"); + for (Object o : feed.getEntries()) { + Entry e = (Entry)o; + System.out.println("id = " + e.getId() + " entry = " + e.getTitle()); + } + } + + private Entry newEntry(String value) { + Entry entry = abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + + private Entry updateEntry(Entry entry, String value) { + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } +} \ No newline at end of file diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ContentNegotiationTest.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ContentNegotiationTest.java new file mode 100644 index 0000000000..08f8e3db2a --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ContentNegotiationTest.java @@ -0,0 +1,249 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom; + +import java.io.IOException; +import java.io.Reader; +import java.text.SimpleDateFormat; +import java.util.Date; + +import junit.framework.Assert; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.host.embedded.SCADomain; + +import org.apache.abdera.Abdera; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.model.Base; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Service; +import org.apache.abdera.protocol.Response.ResponseType; +import org.apache.abdera.protocol.client.AbderaClient; +import org.apache.abdera.protocol.client.ClientResponse; +import org.apache.abdera.protocol.client.RequestOptions; +import org.apache.abdera.protocol.client.util.BaseRequestEntity; +import org.apache.abdera.util.EntityTag; +import org.apache.abdera.parser.Parser; + + +/** + * Tests use of content negotiation for Atom binding in Tuscany. + * Uses the SCA provided Provider composite to act as a server. + * Uses the Abdera provided Client to act as a client. + */ +public class ContentNegotiationTest { + public final static String providerURI = "http://localhost:8084/customer"; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + protected static AbderaClient client; + protected static Parser abderaParser; + protected static String lastId; + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>ContentNegotiationTest.init"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + System.out.println(">>>ContentNegotiationTest.destroy"); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull( client ); + } + + @Test + public void testPost() throws Exception { + System.out.println(">>>ContentNegotiationTest.testPost"); + // Testing of entry creation + Factory factory = abdera.getFactory(); + String customerName = "Fred Farkle"; + Entry entry = factory.newEntry(); + entry.setTitle("customer " + customerName); + entry.setUpdated(new Date()); + entry.addAuthor("Apache Tuscany"); + // ID created by collection. + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(customerName); + entry.setContentElement(content); + + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + // AtomTestCaseUtils.printRequestHeaders( "Post request headers", " ", opts ); + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + ClientResponse res = client.post(colUri.toString(), entry, opts); + + // Assert response status code is 201-OK. + // Assert response header Content-Type: application/atom+xml; charset=UTF-8 + Assert.assertEquals(201, res.getStatus()); + String returnedContentType = res.getContentType().toString().trim(); + Assert.assertEquals(contentType, returnedContentType ); + + String eTag = res.getHeader( "ETag" ); + if ( eTag != null) + lastId = eTag.substring( 1, eTag.length()-1); + + // AtomTestCaseUtils.printResponseHeaders( "Entry post response headers:", " ", res ); + // System.out.println("Entry post response content:"); + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + } + + @Test + public void testXMLEntryGet() throws Exception { + System.out.println(">>>ContentNegotiationTest.testXMLEntryGet"); + RequestOptions opts = new RequestOptions(); + opts.setHeader( "Accept", "application/atom+xml" ); + + IRI colUri = new IRI(providerURI).resolve("customer"); + ClientResponse res = client.get(colUri.toString() + "/" + lastId, opts); + Assert.assertEquals(200, res.getStatus()); + String returnedContentType = res.getContentType().toString().trim(); + // Assert.assertEquals(contentType, returnedContentType ); + res.release(); + } + + @Test + public void testJSONEntryGet() throws Exception { + System.out.println(">>>ContentNegotiationTest.testJSONEntryGet"); + RequestOptions opts = new RequestOptions(); + opts.setHeader( "Accept", "application/json" ); + + IRI colUri = new IRI(providerURI).resolve("customer"); + ClientResponse res = client.get(colUri.toString() + "/" + lastId, opts); + try { + Assert.assertEquals(200, res.getStatus()); + // Abdera 0.4 throws exception on getContentType with application/json. + // System.out.println( "ContentNegotiationTest.testJSONEntryGet contentType=" + res.getContentType()); + String contentType = res.getHeader( "Content-Type"); + Assert.assertTrue( -1 < contentType.indexOf( "application/json" )); + // Following is a poor man's JSONObject test to avoid dependency on JSON libs. + // JSONObject jsonResp = new JSONObject(res.); + // Assert.assertEquals(12345, jsonResp.getInt("result")); + String responseBody = readResponse( res.getReader() ); + Assert.assertTrue( responseBody.startsWith( "{") ); + Assert.assertTrue( responseBody.endsWith( "}") ); + Assert.assertTrue( -1 < responseBody.indexOf( "\"id\"" )); + Assert.assertTrue( -1 < responseBody.indexOf( "\"title\"" )); + Assert.assertTrue( -1 < responseBody.indexOf( "\"updated\"" )); + // AtomTestCaseUtils.printResponseHeaders( "JSON Entry response headers:", " ", res ); + // System.out.println( "ContentNegotiationTest.testJSONEntryGet JSON entry body=" + responseBody ); + } finally { + res.release(); + } + } + + @Test + public void testXMLFeedGet() throws Exception { + System.out.println(">>>ContentNegotiationTest.testXMLFeedGet"); + RequestOptions opts = new RequestOptions(); + opts.setHeader( "Accept", "application/atom+xml" ); + + // Atom feed request + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Asser feed provided since no predicates + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + // AtomTestCaseUtils.printResponseHeaders( "Feed response headers:", " ", res ); + // System.out.println("Feed response content:"); + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + + // Perform other tests on feed. + Document doc = res.getDocument(); + Assert.assertNotNull( doc ); + Feed feed = doc.getRoot(); + Assert.assertNotNull( feed ); + // RFC 4287 requires non-null id, title, updated elements + Assert.assertNotNull( feed.getId() ); + Assert.assertNotNull( feed.getTitle() ); + Assert.assertNotNull( feed.getUpdated() ); + // AtomTestCaseUtils.printFeed( "Feed values", " ", feed ); + } finally { + res.release(); + } + } + + @Test + public void testJSONFeedGet() throws Exception { + System.out.println(">>>ContentNegotiationTest.testJSONFeedGet"); + RequestOptions opts = new RequestOptions(); + opts.setHeader( "Accept", "application/json" ); + + // JSON feed request + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Assert feed provided since no predicates + Assert.assertEquals(200, res.getStatus()); + // Abdera 0.4 throws exception on getContentType with application/json. + // System.out.println( "ContentNegotiationTest.testJSONEntryGet contentType=" + res.getContentType()); + String contentType = res.getHeader( "Content-Type"); + Assert.assertTrue( -1 < contentType.indexOf( "application/json" )); + // Following is a poor man's JSONObject test to avoid dependency on JSON libs. + // JSONObject jsonResp = new JSONObject(res.); + // Assert.assertEquals(12345, jsonResp.getInt("result")); + String responseBody = readResponse( res.getReader() ); + Assert.assertTrue( responseBody.startsWith( "{") ); + Assert.assertTrue( responseBody.endsWith( "}") ); + Assert.assertTrue( -1 < responseBody.indexOf( "\"id\"" )); + Assert.assertTrue( -1 < responseBody.indexOf( "\"title\"" )); + Assert.assertTrue( -1 < responseBody.indexOf( "\"updated\"" )); + Assert.assertTrue( -1 < responseBody.indexOf( "\"entries\"" )); + // AtomTestCaseUtils.printResponseHeaders( "JSON Entry response headers:", " ", res ); + // System.out.println( "ContentNegotiationTest.testJSONEntryGet JSON entry body=" + responseBody ); + } finally { + res.release(); + } + } + + protected String readResponse( Reader responseReader ) { + if ( responseReader == null ) return ""; + StringBuffer sb = new StringBuffer(1024); + try { + int charValue = 0; + while ((charValue = responseReader.read()) != -1) { + //result = result + (char) charValue; + sb.append((char)charValue); + } + } catch ( IOException e ) { + } + return sb.toString(); + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClient.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClient.java new file mode 100644 index 0000000000..bc32b91367 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClient.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom; + +import org.apache.tuscany.sca.binding.atom.collection.Collection; + +public interface CustomerClient { + + void testCustomerCollection() throws Exception; + + Collection getCustomerCollection(); +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClientImpl.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClientImpl.java new file mode 100644 index 0000000000..3e5705b14c --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClientImpl.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.osoa.sca.annotations.Reference; + +public class CustomerClientImpl implements CustomerClient { + + protected final Abdera abdera = new Abdera(); + + @Reference + public Collection customerCollection; + + public void testCustomerCollection() throws Exception { + + Entry newEntry = newEntry("Sponge Bob"); + System.out.println(">>> post entry=" + newEntry.getTitle()); + newEntry = customerCollection.post(newEntry); + System.out.println("<<< post id=" + newEntry.getId() + " entry=" + newEntry.getTitle()); + + newEntry = newEntry("Jane Bond"); + System.out.println(">>> post entry=" + newEntry.getTitle()); + newEntry = customerCollection.post(newEntry); + System.out.println("<<< post id=" + newEntry.getId() + " entry=" + newEntry.getTitle()); + + System.out.println(">>> get id=" + newEntry.getId()); + Entry entry = customerCollection.get(newEntry.getId().toString()); + System.out.println("<<< get id=" + entry.getId() + " entry=" + entry.getTitle()); + + System.out.println(">>> put id=" + newEntry.getId() + " entry=" + entry.getTitle()); + customerCollection.put(entry.getId().toString(), updateEntry(entry, "James Bond")); + System.out.println("<<< put id=" + entry.getId() + " entry=" + entry.getTitle()); + + System.out.println(">>> delete id=" + entry.getId()); + customerCollection.delete(entry.getId().toString()); + System.out.println("<<< delete id=" + entry.getId()); + + System.out.println(">>> get collection"); + Feed feed = customerCollection.getFeed(); + System.out.println("<<< get collection"); + for (Object o : feed.getEntries()) { + Entry e = (Entry)o; + System.out.println("id = " + e.getId() + " entry = " + e.getTitle()); + } + } + + public Collection getCustomerCollection() { + return customerCollection; + } + + private Entry newEntry(String value) { + + Entry entry = this.abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = this.abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + + entry.setContentElement(content); + + return entry; + } + + private Entry updateEntry(Entry entry, String value) { + + entry.setTitle("customer " + value); + + Content content = this.abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + + entry.setContentElement(content); + + return entry; + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerCollectionImpl.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerCollectionImpl.java new file mode 100644 index 0000000000..db66b00a50 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerCollectionImpl.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.binding.atom.collection.NotFoundException; +import org.osoa.sca.annotations.Scope; + +@Scope("COMPOSITE") +public class CustomerCollectionImpl implements Collection { + private final Abdera abdera = new Abdera(); + private Map entries = new HashMap(); + public Date lastModified = new Date(); + + /** + * Default constructor + */ + public CustomerCollectionImpl() { + + } + + public Entry post(Entry entry) { + System.out.println(">>> CustomerCollectionImpl.post entry=" + entry.getTitle()); + + if(!("Exception_Test".equalsIgnoreCase(entry.getTitle()))) + { + String id = "urn:uuid:customer-" + UUID.randomUUID().toString(); + entry.setId(id); + + entry.addLink("" + id, "edit"); + entry.addLink("" + id, "alternate"); + Date now = new Date(); + entry.setUpdated(now); + lastModified = now; + entries.put(id, entry); + + System.out.println(">>> CustomerCollectionImpl.post return id=" + id); + + return entry; + + } + else + { + throw new IllegalArgumentException("Exception in Post method"); + } + } + + public Entry get(String id) { + System.out.println(">>> CustomerCollectionImpl.get id=" + id); + return entries.get(id); + } + + public void put(String id, Entry entry) throws NotFoundException { + System.out.println(">>> CustomerCollectionImpl.put id=" + id + " entry=" + entry.getTitle()); + if(entries.containsKey(id)){ + Date now = new Date(); + entry.setUpdated(now); + lastModified = now; + entries.put(id, entry); + } + else { + throw new NotFoundException(); + } + } + + public void delete(String id) throws NotFoundException { + System.out.println(">>> CustomerCollectionImpl.delete id=" + id); + if(entries.containsKey(id)){ + entries.remove(id); + lastModified = new Date(); + } + else { + throw new NotFoundException(); + } + } + + public Feed getFeed() { + System.out.println(">>> CustomerCollectionImpl.getFeed"); + + Feed feed = this.abdera.getFactory().newFeed(); + feed.setId("customers" + this.hashCode() ); // provide unique id for feed instance. + feed.setTitle("customers"); + feed.setSubtitle("This is a sample feed"); + feed.setUpdated(lastModified); + feed.addLink(""); + feed.addLink("", "self"); + + for (Entry entry : entries.values()) { + feed.addEntry(entry); + } + + return feed; + } + + public Feed query(String queryString) { + System.out.println(">>> CustomerCollectionImpl.query collection " + queryString); + return getFeed(); + } + + // This method used for testing. + protected void testPut(String value) { + String id = "urn:uuid:customer-" + UUID.randomUUID().toString(); + + Entry entry = abdera.getFactory().newEntry(); + entry.setId(id); + entry.setTitle("customer " + value); + + Content content = this.abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + + entry.setContentElement(content); + + entry.addLink("" + id, "edit"); + entry.addLink("" + id, "alternate"); + + entry.setUpdated(new Date()); + + entries.put(id, entry); + System.out.println(">>> id=" + id); + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionImpl.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionImpl.java new file mode 100644 index 0000000000..5d5953aaaa --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionImpl.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.abdera.Abdera; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.binding.atom.collection.MediaCollection; +import org.apache.tuscany.sca.binding.atom.collection.NotFoundException; +import org.osoa.sca.annotations.Scope; + +@Scope("COMPOSITE") +public class MediaCollectionImpl implements MediaCollection { + private final Abdera abdera = new Abdera(); + private Map entries = new HashMap(); + private Map mediaFiles = new HashMap(); + public Date lastModified = new Date(); + + public Entry post(Entry entry) { + System.out.println(">>> MediaCollectionImpl.post entry=" + entry.getTitle()); + + if(!("Exception_Test".equalsIgnoreCase(entry.getTitle()))) + { + String id = "urn:uuid:customer-" + UUID.randomUUID().toString(); + entry.setId(id); + + entry.addLink("" + id, "edit"); + entry.addLink("" + id, "alternate"); + Date now = new Date(); + entry.setUpdated(now); + lastModified = now; + entries.put(id, entry); + + System.out.println(">>> MediaCollectionImpl.post return id=" + id); + + return entry; + + } + else + { + throw new IllegalArgumentException("Exception in Post method"); + } + } + + public Entry get(String id) { + System.out.println(">>> MediaCollectionImpl.get id=" + id); + return entries.get(id); + } + + public void put(String id, Entry entry) throws NotFoundException { + System.out.println(">>> MediaCollectionImpl.put id=" + id + " entry=" + entry.getTitle()); + if(entries.containsKey(id)){ + Date now = new Date(); + entry.setUpdated(now); + lastModified = now; + entries.put(id, entry); + } + else { + throw new NotFoundException(); + } + } + + public void delete(String id) throws NotFoundException { + System.out.println(">>> MediaCollectionImpl.delete id=" + id); + if(entries.containsKey(id)){ + entries.remove(id); + lastModified = new Date(); + } + else { + throw new NotFoundException(); + } + } + + public Feed getFeed() { + System.out.println(">>> MediaCollectionImpl.getFeed"); + + Feed feed = this.abdera.getFactory().newFeed(); + feed.setId("customers" + this.hashCode() ); // provide unique id for feed instance. + feed.setTitle("customers"); + feed.setSubtitle("This is a sample feed"); + feed.setUpdated(lastModified); + feed.addLink(""); + feed.addLink("", "self"); + + for (Entry entry : entries.values()) { + feed.addEntry(entry); + } + + return feed; + } + + public Feed query(String queryString) { + System.out.println(">>> MediaCollectionImpl.query collection " + queryString); + return getFeed(); + } + + // This method used for testing. + protected void testPut(String value) { + String id = "urn:uuid:customer-" + UUID.randomUUID().toString(); + + Entry entry = abdera.getFactory().newEntry(); + entry.setId(id); + entry.setTitle("customer " + value); + + Content content = this.abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + + entry.setContentElement(content); + + entry.addLink("" + id, "edit"); + entry.addLink("" + id, "alternate"); + + entry.setUpdated(new Date()); + + entries.put(id, entry); + System.out.println(">>> id=" + id); + } + + // MediaCollection role + public Entry postMedia(String title, String slug, String contentType, InputStream media) { + System.out.println(">>> MediaCollectionImpl.postMedia title=" + title + ", slug=" + slug + ", contentType=" + contentType ); + + Factory factory = abdera.getFactory(); + Entry entry = factory.newEntry(); + // Must provide entry to media as per Atom Pub spec (http://tools.ietf.org/html/rfc5023#section-9.6) + // + // + // The Beach (REQUIRED) + // urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a (REQUIRED) + // 2005-10-07T17:17:08Z + // (REQUIRED, OPTIONAL to populate + // + // + // + // + + // Normalize title + entry.setTitle( title ); + String normalTitle = title.replace( " ", "_" ); + String hostURL = "http://media.example.org/"; + int lastDelimiterPos = contentType != null ? contentType.lastIndexOf( "/" ) : -1; + String extension = ""; + if ( lastDelimiterPos != -1 ) { + extension = contentType.substring( lastDelimiterPos + 1 ); + } else { + extension = contentType; + } + long mediaLength = -1; + try { + mediaLength = media.skip( Long.MAX_VALUE ); + } catch ( IOException e ){} + + // A true implementation would store the media to a repository, e.g. file system. + // This implementation record's the id and the location. + String id = "urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a-" + mediaFiles.size(); + String reposLocation = hostURL + "edit/" + normalTitle; + mediaFiles.put( id, reposLocation ); + + // Build entry for media link. + entry.setUpdated( new Date() ); + entry.setId( id ); + // Convention. Return header properties as key values. + entry.setSummary( "Content-Type=" + contentType + ",Content-Length=" + mediaLength ); + entry.setContent( new IRI( hostURL + normalTitle + "." + extension ), contentType ); + entry.addLink( reposLocation + ".atom", "edit" ); + entry.addLink( reposLocation + "." + extension, "edit-media" ); + return entry; + } + + public void putMedia(String id, String contentType, InputStream media) throws NotFoundException { + System.out.println(">>> MediaCollectionImpl.putMedia id=" + id + ", contentType=" + contentType ); + + // Must responsd with success or not found as per Atom Pub spec (http://tools.ietf.org/html/rfc5023#section-9.6) + // Body is null. + if ( !id.endsWith( "0" ) ) + throw new NotFoundException( "Media at id=" + id + " not found." ); + + // A true implementation would update the media in the media repository. + } + +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionTestCase.java new file mode 100644 index 0000000000..0aabf225a4 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionTestCase.java @@ -0,0 +1,287 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom; + +import java.io.File; +import java.io.FileInputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import junit.framework.Assert; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.tuscany.sca.host.embedded.SCADomain; + +import org.apache.abdera.Abdera; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Link; +import org.apache.abdera.protocol.client.AbderaClient; +import org.apache.abdera.protocol.client.ClientResponse; +import org.apache.abdera.protocol.client.RequestOptions; +import org.apache.abdera.protocol.client.util.BaseRequestEntity; +import org.apache.abdera.util.EntityTag; +import org.apache.abdera.parser.Parser; + +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.methods.InputStreamRequestEntity; + +/** + * Tests use of server provided entry entity tags for Atom binding in Tuscany. + * Tests conditional gets (e.g. get if-none-match) or conditional posts (post if-match) + * using entity tags or last modified header entries. + * Uses the SCA provided Provider composite to act as a server. + * Uses the Abdera provided Client to act as a client. + */ +public class MediaCollectionTestCase { + public final static String providerURI = "http://localhost:8084/receipt"; + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + protected static AbderaClient client; + protected static Parser abderaParser; + protected static String eTag; + protected static Date lastModified; + protected static String mediaId; + protected static final SimpleDateFormat dateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss Z" ); // RFC 822 date time + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>MediaCollectionTestCase.init"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite"); + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + System.out.println(">>>MediaCollectionTestCase.destroy"); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull( client ); + } + + @Test + public void testMediaEntryPost() throws Exception { + // Pseudo Code (see APP (http://tools.ietf.org/html/rfc5023#section-9.6) + // Post request + // POST /edit/ HTTP/1.1 + // Host: media.example.org + // Content-Type: image/png + // Slug: The Beach + // Content-Length: nnn + // ...binary data... + + // Testing of entry creation + String receiptName = "Auto Repair Bill"; + String fileName = "ReceiptToms.gif"; + File input = new File( fileName ); + boolean exists = input.exists(); + Assert.assertTrue( exists ); + + // Prepare HTTP post + // PostMethod post = new PostMethod( colUri.toString() ); + PostMethod post = new PostMethod( providerURI ); + post.addRequestHeader( "Content-Type", "image/gif" ); + post.addRequestHeader( "Title", "Title " + receiptName + "" ); + post.addRequestHeader( "Slug", "Slug " + receiptName + "" ); + post.setRequestEntity( + new InputStreamRequestEntity( new FileInputStream( input ), "image/gif" ) ); + + // Get HTTP client + HttpClient httpclient = new HttpClient(); + try { + // Execute request + int result = httpclient.executeMethod(post); + // Pseudo Code (see APP (http://tools.ietf.org/html/rfc5023#section-9.6) + // Post response + // Tuscany responds with proper media links. Note that the media is + // stored in a different location than the media information which is + // stored in the Atom feed. + // HTTP/1.1 201 Created + // Display status code + // System.out.println("Response status code: " + result + ", status text=" + post.getStatusText() ); + Assert.assertEquals(201, result ); + // Display response + // System.out.println("Response body: "); + // System.out.println(post.getResponseBodyAsString()); // Warning: BodyAsString recommends BodyAsStream + + // Location: http://example.org/media/edit/the_beach.atom (REQUIRED) + // System.out.println( "Response Location=" + post.getResponseHeader( "Location" ).getValue() + "." ); + Header header = post.getResponseHeader( "Location" ); + Assert.assertNotNull( header ); + Assert.assertNotNull( header.getValue() ); + // ContentLocation: http://example.org/media/edit/the_beach.jpg (REQUIRED) + // System.out.println( "Response Content-Location=" + post.getResponseHeader( "Content-Location" ).getValue() ); + header = post.getResponseHeader( "Content-Location" ); + Assert.assertNotNull( header ); + Assert.assertNotNull( header.getValue() ); + // Content-Type: application/atom+xml;type=entry;charset="utf-8" + // System.out.println( "Response Content-Type=" + post.getResponseHeader( "Content-Type" ).getValue()); + header = post.getResponseHeader( "Content-Type" ); + Assert.assertNotNull( header ); + Assert.assertNotNull( header.getValue() ); + // Content-Length: nnn (OPTIONAL) + // System.out.println( "Response Content-Length=" + post.getResponseHeader( "Content-Length" ).getValue() ); + header = post.getResponseHeader( "Content-Length" ); + Assert.assertNotNull( header ); + Assert.assertNotNull( header.getValue() ); + // + // + // The Beach (REQUIRED) + // urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a (REQUIRED) + // 2005-10-07T17:17:08Z + // Daffy + // (REQUIRED, OPTIONAL to populate + // + // + // + // + Document document = abderaParser.parse( post.getResponseBodyAsStream() ); + Entry entry = document.getRoot(); + String title = entry.getTitle(); + // System.out.println( "mediaPost entry.title=" + title ); + Assert.assertNotNull( title ); + IRI id = entry.getId(); + // System.out.println( "mediaPost entry.id=" + id ); + Assert.assertNotNull( id ); + mediaId = id.toString(); + Assert.assertNotNull( mediaId ); // Save for put/update request + Date updated = entry.getUpdated(); + // System.out.println( "mediaPost entry.updated=" + updated); + Assert.assertNotNull( updated ); + String summary = entry.getSummary(); + // System.out.println( "mediaPost entry.summary=" + summary); + Assert.assertNotNull( summary ); + IRI contentSrc = entry.getContentSrc(); + // System.out.println( "mediaPost entry.content.src=" + contentSrc + ", type=" + entry.getContentType()); + Assert.assertNotNull( contentSrc ); + Link editLink = entry.getEditLink(); + // System.out.println( "mediaPost entry.editLink" + " rel=" + editLink.getRel() + ", href=" + editLink.getHref() ); + Assert.assertNotNull( editLink ); + Assert.assertNotNull( editLink.getRel() ); + Assert.assertNotNull( editLink.getHref() ); + Link editMediaLink = entry.getEditMediaLink(); + // System.out.println( "mediaPost entry.editMediaLink" + " rel=" + editMediaLink.getRel() + ", href=" + editMediaLink.getHref() ); + Assert.assertNotNull( editMediaLink ); + Assert.assertNotNull( editMediaLink.getRel() ); + Assert.assertNotNull( editMediaLink.getHref() ); + + } finally { + // Release current connection to the connection pool once you are done + post.releaseConnection(); + } + } + + @Test + public void testMediaEntryPutFound() throws Exception { + // Pseudo Code (see APP (http://tools.ietf.org/html/rfc5023#section-9.6) + // Testing of entry update + String receiptName = "Value Autoglass Bill"; + String fileName = "ReceiptValue.jpg"; + File input = new File( fileName ); + boolean exists = input.exists(); + Assert.assertTrue( exists ); + + // Prepare HTTP put request + // PUT /edit/the_beach.png HTTP/1.1 + // Host: media.example.org + // Content-Type: image/png + // Content-Length: nnn + // ...binary data... + PutMethod put = new PutMethod( providerURI + "/" + mediaId ); + put.addRequestHeader( "Content-Type", "image/jpg" ); + put.addRequestHeader( "Title", "Title " + receiptName + "" ); + put.addRequestHeader( "Slug", "Slug " + receiptName + "" ); + put.setRequestEntity( + new InputStreamRequestEntity( new FileInputStream( input ), "image/jpg" ) ); + + // Get HTTP client + HttpClient httpclient = new HttpClient(); + try { + // Execute request + int result = httpclient.executeMethod(put); + // Pseudo Code (see APP (http://tools.ietf.org/html/rfc5023#section-9.6) + // Display status code + // System.out.println("Response status code: " + result + ", status text=" + put.getStatusText() ); + Assert.assertEquals(200, result ); + // Display response. Should be empty for put. + // System.out.println("Response body: "); + // System.out.println(put.getResponseBodyAsString()); // Warning: BodyAsString recommends BodyAsStream + } finally { + // Release current connection to the connection pool once you are done + put.releaseConnection(); + } + } + + @Test + public void testMediaEntryPutNotFound() throws Exception { + // Pseudo Code (see APP (http://tools.ietf.org/html/rfc5023#section-9.6) + // Testing of entry update + String receiptName = "Value Autoglass Bill"; + String fileName = "ReceiptValue.jpg"; + File input = new File( fileName ); + boolean exists = input.exists(); + Assert.assertTrue( exists ); + + // Prepare HTTP put request + // PUT /edit/the_beach.png HTTP/1.1 + // Host: media.example.org + // Content-Type: image/png + // Content-Length: nnn + // ...binary data... + PutMethod put = new PutMethod( providerURI + "/" + mediaId + "-bogus" ); // Does not exist. + put.addRequestHeader( "Content-Type", "image/jpg" ); + put.addRequestHeader( "Title", "Title " + receiptName + "" ); + put.addRequestHeader( "Slug", "Slug " + receiptName + "" ); + put.setRequestEntity( + new InputStreamRequestEntity( new FileInputStream( input ), "image/jpg" ) ); + + // Get HTTP client + HttpClient httpclient = new HttpClient(); + try { + // Execute request + int result = httpclient.executeMethod(put); + // Pseudo Code (see APP (http://tools.ietf.org/html/rfc5023#section-9.6) + // Display status code + // System.out.println("Response status code: " + result + ", status text=" + put.getStatusText() ); + Assert.assertEquals(404, result ); + // Display response. Should be empty for put. + // System.out.println("Response body: "); + // System.out.println(put.getResponseBodyAsString()); // Warning: BodyAsString recommends BodyAsStream + } finally { + // Release current connection to the connection pool once you are done + put.releaseConnection(); + } + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Provider.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Provider.java new file mode 100644 index 0000000000..3e1bf543e5 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/Provider.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom; + +import java.io.IOException; + +import org.apache.tuscany.sca.host.embedded.SCADomain; + +public class Provider { + + public static void main(String[] args) { + + SCADomain scaDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + System.out.println("Provider.composite ready..."); + + try { + System.in.read(); + } catch (IOException e) { + e.printStackTrace(); + } + + scaDomain.close(); + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderEntryEntityTagsTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderEntryEntityTagsTestCase.java new file mode 100644 index 0000000000..703419f527 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderEntryEntityTagsTestCase.java @@ -0,0 +1,428 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import junit.framework.Assert; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.host.embedded.SCADomain; + +import org.apache.abdera.Abdera; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.model.Base; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Service; +import org.apache.abdera.protocol.Response.ResponseType; +import org.apache.abdera.protocol.client.AbderaClient; +import org.apache.abdera.protocol.client.ClientResponse; +import org.apache.abdera.protocol.client.RequestOptions; +import org.apache.abdera.protocol.client.util.BaseRequestEntity; +import org.apache.abdera.util.EntityTag; +import org.apache.abdera.parser.Parser; + +/** + * Tests use of server provided entry entity tags for Atom binding in Tuscany. + * Tests conditional gets (e.g. get if-none-match) or conditional posts (post if-match) + * using entity tags or last modified header entries. + * Uses the SCA provided Provider composite to act as a server. + * Uses the Abdera provided Client to act as a client. + */ +public class ProviderEntryEntityTagsTestCase { + public final static String providerURI = "http://localhost:8084/customer"; + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + protected static AbderaClient client; + protected static Parser abderaParser; + protected static String eTag; + protected static Date lastModified; + protected static final SimpleDateFormat dateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss Z" ); // RFC 822 date time + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>ProviderEntryEntityTagsTestCase.init"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + System.out.println(">>>ProviderEntryEntityTagsTestCase.destroy"); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull( client ); + } + + @Test + public void testEmptyCachePost() throws Exception { + // Pseudo-code + // 1) Example HTTP POST request (new entry put, new etag response) + // User client post request + // POST /myblog/entries HTTP/1.1 + // Slug: First Post + // + // + // + // Atom-Powered Robots Run Amok + // urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + // 2007-02-123T17:09:02Z + // Captain Lansing + // It's something moving... solid metal + // + + // Expected Atom server response (note unique ETag) + // HTTP/1.1 201 Created + // Date: Fri, 23 Feb 2007 21:17:11 GMT + // Content-Length: nnn + // Content-Type: application/atom+xml;type=entry + // Location: http://example.org/edit/first-post.atom + // Content-Location: http://example.org/edit/first-post.atom + // ETag: "e180ee84f0671b1" + // Last-Modified: Fri, 25 Jul 2008 14:36:44 -0500 + // + // + // + // Atom-Powered Robots Run Amok + // urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + // 2007-02-123T17:09:02Z + // Captain Lansing + // It's something moving... solid metal + // + + // Testing of entry creation + Factory factory = abdera.getFactory(); + String customerName = "Fred Farkle"; + Entry entry = factory.newEntry(); + entry.setTitle("customer " + customerName); + entry.setUpdated(new Date()); + entry.addAuthor("Apache Tuscany"); + // ID created by collection. + // entry.setId(id); // auto-provided + // entry.addLink("" + id, "edit"); // auto-provided + // entry.addLink("" + id, "alternate"); // auto-provided + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(customerName); + entry.setContentElement(content); + + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + // AtomTestCaseUtils.printRequestHeaders( "Post request headers", " ", opts ); + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + ClientResponse res = client.post(colUri.toString(), entry, opts); + + // Assert response status code is 201-OK. + // Assert response header Content-Type: application/atom+xml; charset=UTF-8 + // Assert response header Location: http://example.org/edit/first-post.atom + // Assert response header Content-Location: http://example.org/edit/first-post.atom + // Assert response header ETag: "e180ee84f0671b1" + // Assert response header Last-Modified: Fri, 25 Jul 2008 14:36:44 -0500 + // Assert collection size is 1. + Assert.assertEquals(201, res.getStatus()); + Assert.assertEquals(contentType, res.getContentType().toString().trim()); + // Assert.assertNotNull( res.getLocation().toString() ); + // Assert.assertEquals( "", res.getContentLocation().toString() ); + // Save eTag for subsequent tests; + eTag = res.getHeader( "ETag" ); + Assert.assertNotNull( eTag ); + lastModified = res.getLastModified(); + Assert.assertNotNull(lastModified); + } + + @Test + public void testDirtyCachePut() throws Exception { + // 2) Conditional PUT request (post with etag. entry provided is stale) + // User client PUT request + // PUT /edit/first-post.atom HTTP/1.1 + // > If-Match: "e180ee84f0671b1" + // + // + // + // Atom-Powered Robots Run Amok + // urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a + // 2007-02-24T16:34:06Z + // Captain Lansing + // Update: it's a hoax! + // + // Testing of entry creation + Factory factory = abdera.getFactory(); + String customerName = "Molly Ringwald"; + Entry entry = factory.newEntry(); + entry.setTitle("customer " + customerName); + entry.setUpdated( new Date()); + entry.addAuthor("Apache Tuscany"); + String id = eTag.substring( 1, eTag.length()-1); + entry.setId(id); // auto-provided + // entry.addLink("" + id, "edit"); // auto-provided + // entry.addLink("" + id, "alternate"); // auto-provided + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(customerName); + entry.setContentElement(content); + + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + opts.setHeader( "If-None-Match", eTag); + + // AtomTestCaseUtils.printRequestHeaders( "Put request headers", " ", opts ); + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + id = eTag.substring( 1, eTag.length()-1); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.put(colUri.toString() + id, entry, opts); + ClientResponse res = client.put(colUri.toString() + "/" + id, new BaseRequestEntity( entry ), opts); + // Expected Atom server response (item was edited by another user) + // > HTTP/1.1 412 Precondition Failed + // Date: Sat, 24 Feb 2007 16:34:11 GMT + + // If-Match Assert response status code is 412. Precondition failed. + // If-None-Match Assert response status code is 200. OK + Assert.assertEquals(200, res.getStatus()); + // Put provides null body and no etags. + res.release(); + } + + @Test + public void testETagMissGet() throws Exception { + // 4) Conditional GET example (get with etag. etag not in cache) + // User client GET request + // GET /edit/first-post.atom HTTP/1.1 + // > If-None-Match: "e180ee84f0671b1" + + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + opts.setHeader( "If-None-Match", "123456"); + opts.setHeader( "Pragma", "no-cache"); // turn off client caching + + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + String id = eTag.substring( 1, eTag.length()-1); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.put(colUri.toString() + id, entry, opts); + ClientResponse res = client.get(colUri.toString() + "/" + id, opts); + // Expected Atom server response (item was edited by another user) + // > HTTP/1.1 412 Precondition Failed + // Date: Sat, 24 Feb 2007 16:34:11 GMT + + // Atom server response (item was up to date) + // > HTTP/1.1 200 OK + // Date: Sat, 24 Feb 2007 13:17:11 GMT + // > ETag: "bb4f5e86e92ddb8549604a0df0763581" + // > Last-Modified: Mon, 28 Jul 2008 10:25:37 -0500 + + // Assert response status code is 200 OK. + // Assert header Content-Type: application/atom+xml;type=entry + // Assert header Location: http://example.org/edit/first-post.atom + // Assert header Content-Location: http://example.org/edit/first-post.atom + // Assert header ETag: "555555" (etag response != etag request) + // Assert header Last-Modified: Fri, 25 Jul 2008 14:36:44 -0500 + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(contentType, res.getContentType().toString().trim()); + // Assert.assertNotNull( res.getLocation().toString() ); + // Assert.assertEquals( "", res.getContentLocation().toString() ); + Assert.assertNotNull( res.getHeader( "ETag" ) ); + lastModified = res.getLastModified(); + Assert.assertNotNull(lastModified); + res.release(); + } + + @Test + public void testETagHitGet() throws Exception { + // 3) Conditional GET example (get with etag. etag match) + // User client GET request + // GET /edit/first-post.atom HTTP/1.1 + // > If-None-Match: "e180ee84f0671b1" + + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + opts.setHeader( "If-None-Match", eTag); + opts.setHeader( "Pragma", "no-cache"); // turn off client caching + + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + String id = eTag.substring( 1, eTag.length()-1); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.put(colUri.toString() + id, entry, opts); + ClientResponse res = client.get(colUri.toString() + "/" + id, opts); + // Atom server response (item was up to date) + // > HTTP/1.1 304 Not Modified + // Date: Sat, 24 Feb 2007 13:17:11 GMT + + // Assert response status code is 304 Not Modified. + // Assert header ETag: "e180ee84f0671b1" + // Assert header Last-Modified: Fri, 25 Jul 2008 14:36:44 -0500 + // Assert.assertEquals(304, res.getStatus()); + res.release(); + } + + + @Test + public void testUpToDateGet() throws Exception { + // 3) Conditional GET example (get with If-Mod. entry is up to date) + // User client GET request + // GET /edit/first-post.atom HTTP/1.1 + // > If-Modified-Since: Sat, 29 Oct 2025 19:43:31 GMT + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + opts.setHeader( "If-Modified-Since", "Sat, 29 Oct 2025 19:43:31 GMT"); // "EEE, dd MMM yyyy HH:mm:ss Z // RFC 822 Date + opts.setHeader( "Pragma", "no-cache"); // turn off client caching + + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + String id = eTag.substring( 1, eTag.length()-1); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.put(colUri.toString() + id, entry, opts); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.get(colUri.toString() + "/" + id, opts); + ClientResponse res = client.execute( "GET", colUri.toString(), (BaseRequestEntity)null, opts); + + // Atom server response (item was up to date) + // > HTTP/1.1 304 Not Modified + // Date: Sat, 24 Feb 2007 13:17:11 GMT + + // Assert response status code is 304 Not Modified. + Assert.assertEquals(304, res.getStatus()); + res.release(); + } + + @Test + public void testOutOfDateGet() throws Exception { + // 4) Conditional GET example (get with If-Mod. entry is not to date) + // User client GET request + // GET /edit/first-post.atom HTTP/1.1 + // > If-Modified-Since: Sat, 29 Oct 1844 19:43:31 GMT + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + opts.setHeader( "If-Modified-Since", "Sat, 29 Oct 1844 19:43:31 GMT"); // "EEE, dd MMM yyyy HH:mm:ss Z // RFC 822 Date + opts.setHeader( "Pragma", "no-cache"); // turn off client caching + + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + String id = eTag.substring( 1, eTag.length()-1); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.put(colUri.toString() + id, entry, opts); + ClientResponse res = client.get(colUri.toString() + "/" + id, opts); + + // Atom server response (item was up to date) + // > HTTP/1.1 200 OK + // Date: Sat, 24 Feb 2007 13:17:11 GMT + // > ETag: "bb4f5e86e92ddb8549604a0df0763581" + // > Last-Modified: Mon, 28 Jul 2008 10:25:37 -0500 + + // Assert response status code is 200 OK. + // Assert header ETag: "e180ee84f0671b1" + // Assert header Last-Modified: Greater than If-Mod + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(contentType, res.getContentType().toString().trim()); + // Assert.assertNotNull( res.getLocation().toString() ); + // Assert.assertEquals( "", res.getContentLocation().toString() ); + Assert.assertNotNull( res.getHeader( "ETag" ) ); + lastModified = res.getLastModified(); + Assert.assertNotNull(lastModified); + res.release(); + } + + @Test + public void testUpToDateUnModGet() throws Exception { + // 3) Conditional GET example (get with If-Unmod. entry is not modified (< predicate date). + // User client GET request + // GET /edit/first-post.atom HTTP/1.1 + // > If-Unmodified-Since: Sat, 29 Oct 2025 19:43:31 GMT + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + opts.setHeader( "If-Unmodified-Since", "Sat, 29 Oct 2050 19:43:31 GMT" ); + opts.setHeader( "Pragma", "no-cache"); // turn off client caching + + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + String id = eTag.substring( 1, eTag.length()-1); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.put(colUri.toString() + id, entry, opts); + ClientResponse res = client.get(colUri.toString() + "/" + id, opts); + + // Atom server response (item was up to date) + // > HTTP/1.1 200 OK + // Date: Sat, 24 Feb 2007 13:17:11 GMT + // > ETag: "bb4f5e86e92ddb8549604a0df0763581" + // > Last-Modified: Mon, 28 Jul 2008 10:25:37 -0500 + + // Assert response status code is 200 OK. + // Assert header Content-Type: application/atom+xml;type=entry + // Assert header Location: http://example.org/edit/first-post.atom + // Assert header Content-Location: http://example.org/edit/first-post.atom + // Assert header ETag: "e180ee84f0671b1" + // Assert header Last-Modified: Less than If-Unmod + Assert.assertEquals(200, res.getStatus()); + res.release(); + } + + @Test + public void testOutOfDateUnModGet() throws Exception { + // 4) Conditional GET example (get with If-Unmod. entry is modified (> predicate date) + // User client GET request + // GET /edit/first-post.atom HTTP/1.1 + // Host: example.org + // > If-Unmodified-Since: Sat, 29 Oct 1844 19:43:31 GMT + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + opts.setHeader( "If-Unmodified-Since", "Sat, 29 Oct 1844 19:43:31 GMT" ); + opts.setHeader( "Pragma", "no-cache"); // turn off client caching + + IRI colUri = new IRI(providerURI).resolve("customer"); + // res = client.post(colUri.toString() + "?test=foo", entry, opts); + String id = eTag.substring( 1, eTag.length()-1); + // Warning. AbderaClient.put(String uri,Base base,RequestOptions options) caches on the client side. + // ClientResponse res = client.put(colUri.toString() + id, entry, opts); + ClientResponse res = client.get(colUri.toString() + "/" + id, opts); + + // Atom server response (item was up to date) + // > HTTP/1.1 304 Not Modified + // Date: Sat, 24 Feb 2007 13:17:11 GMT + + // Assert response status code is 304 Not Modified. + Assert.assertEquals(304, res.getStatus()); + res.release(); + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderFeedEntityTagsTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderFeedEntityTagsTestCase.java new file mode 100644 index 0000000000..4915f7426e --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderFeedEntityTagsTestCase.java @@ -0,0 +1,382 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.net.Socket; +import java.text.SimpleDateFormat; +import java.util.Date; + +import junit.framework.Assert; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.tuscany.sca.host.embedded.SCADomain; + +import org.apache.abdera.Abdera; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.model.Base; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Service; +import org.apache.abdera.model.Collection; +import org.apache.abdera.protocol.Response.ResponseType; +import org.apache.abdera.protocol.client.AbderaClient; +import org.apache.abdera.protocol.client.ClientResponse; +import org.apache.abdera.protocol.client.RequestOptions; +import org.apache.abdera.protocol.client.util.BaseRequestEntity; +import org.apache.abdera.util.EntityTag; +import org.apache.abdera.parser.Parser; + +/** + * Tests use of server provided feed entity tags for Atom binding in Tuscany. + * Tests conditional gets (e.g. get if-none-match) or conditional posts (post if-match) + * using entity tags and last modified entries in headers. + * Uses the SCA provided Provider composite to act as a server. + * Uses the Abdera provided Client to act as a client. + */ +public class ProviderFeedEntityTagsTestCase { + public final static String providerURI = "http://localhost:8084/customer"; + protected static SCADomain scaConsumerDomain; + protected static SCADomain scaProviderDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + protected static AbderaClient client; + protected static Parser abderaParser; + protected static String eTag; + protected static Date lastModified; + protected static final SimpleDateFormat dateFormat = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss Z" ); // RFC 822 date time + + @BeforeClass + public static void init() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.init"); + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.destroy"); + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull( client ); + } + + @Test + public void testFeedBasics() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedBasics"); + // Normal feed request + ClientResponse res = client.get(providerURI); + Assert.assertNotNull(res); + try { + // Assert feed provided since no predicates + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + // AtomTestCaseUtils.printResponseHeaders( "Feed response headers:", " ", res ); + // System.out.println("Feed response content:"); + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + + // Perform other tests on feed. + // Warning. AbderaClient.getEntityTag is very particular on tag pattern. + // Document doc = res.getDocument(); + String body = read( res.getInputStream() ); + // RFC 4287 requires non-null id, title, updated elements + Assert.assertTrue( -1 != body.indexOf( "" )); + Assert.assertTrue( -1 != body.indexOf( "" )); + Assert.assertTrue( -1 != body.indexOf( "" )); + + eTag = res.getHeader("ETag"); + Assert.assertNotNull( eTag ); + lastModified = res.getLastModified(); + Assert.assertNotNull( lastModified ); + } finally { + res.release(); + } + } + + @Test + public void testUnmodifiedGetIfMatch() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedUnmodifiedGetIfMatch"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-Match", eTag); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + String thisETag = res.getHeader("ETag"); + Assert.assertNotNull( thisETag ); + Date thisLastModified = res.getLastModified(); + Assert.assertNotNull( thisLastModified ); + + // Should return 200 - Feed provided since it matches etag. + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + // AtomTestCaseUtils.printResponseHeaders( "Feed response headers:", " ", res ); + // System.out.println("Feed response content:"); + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + } finally { + res.release(); + } + } + + @Test + public void testUnmodifiedGetIfNoneMatch() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedUnmodifiedGetIfNoneMatch"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-None-Match", eTag); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 304 - Feed not provided since it matches ETag. + Assert.assertEquals(304, res.getStatus()); + } finally { + res.release(); + } + } + + @Test + public void testUnmodifiedGetIfUnModified() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedUnmodifiedGetIfUnModified"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-Unmodified-Since", dateFormat.format( new Date() )); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 304 - Feed not provided since feed is modified since. + Assert.assertEquals(304, res.getStatus()); + } finally { + res.release(); + } + } + + @Test + public void testUnmodifiedGetIfModified() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedUnmodifiedGetIfModified"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-Modified-Since", dateFormat.format( new Date( 0 ) )); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 200 - Feed provided since feed is changed. + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + + String thisETag = res.getHeader("ETag"); + Assert.assertNotNull( thisETag ); + Date thisLastModified = res.getLastModified(); + Assert.assertNotNull( thisLastModified ); + } finally { + res.release(); + } + } + + @Test + public void testModifiedGetIfNoneMatch() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedModifiedGetIfNoneMatch"); + // Post some new content to the feed. + Factory factory = abdera.getFactory(); + String customerName = "Fred Farkle"; + Entry entry = factory.newEntry(); + entry.setTitle("customer " + customerName); + entry.setUpdated(new Date()); + entry.addAuthor("Apache Tuscany"); + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(customerName); + entry.setContentElement(content); + + RequestOptions opts = new RequestOptions(); + String contentType = "application/atom+xml; type=entry"; + opts.setContentType(contentType); + IRI colUri = new IRI(providerURI).resolve("customer"); + ClientResponse res = client.post(colUri.toString(), entry, opts); + + // Feed request with predicates + opts = new RequestOptions(); + contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-None-Match", eTag); + + res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 304 - Feed not provided since it matches ETag. + Assert.assertEquals(304, res.getStatus()); + // AtomTestCaseUtils.printResponseHeaders( "Feed response headers:", " ", res ); + // System.out.println("Feed response content:"); + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + } finally { + res.release(); + } + } + + @Test + public void testModifiedGetIfMatch() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedModifiedGetIfMatch"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-Match", eTag); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + String thisETag = res.getHeader("ETag"); + Assert.assertNotNull( thisETag ); + Date thisLastModified = res.getLastModified(); + Assert.assertNotNull( thisLastModified ); + + // Should return 200 - value since feed is changed + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + + // AtomTestCaseUtils.printResponseHeaders( "Feed modified if-none-match response headers:", " ", res ); + // System.out.println("Feed response content:"); + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + } finally { + res.release(); + } + } + + @Test + public void testModifiedGetIfUnModified() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedUnmodifiedGetIfUnModified"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-Unmodified-Since", dateFormat.format( new Date() )); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 304 - Feed not provided since feed is modified since. + Assert.assertEquals(304, res.getStatus()); + } finally { + res.release(); + } + } + + @Test + public void testModifiedGetIfModified() throws Exception { + System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedUnmodifiedGetIfModified"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-Modified-Since", dateFormat.format( lastModified )); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 200 - Feed provided since feed is changed. + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + + String thisETag = res.getHeader("ETag"); + Assert.assertNotNull( thisETag ); + Date thisLastModified = res.getLastModified(); + Assert.assertNotNull( thisLastModified ); + } finally { + res.release(); + } + } + + + public static void printFeed( String title, String indent, Feed feed ) { + if ( feed == null ) { + System.out.println( title + " feed is null"); + return; + } + + System.out.println( title ); + System.out.println( indent + "id=" + feed.getId() ); + System.out.println( indent + "title=" + feed.getTitle() ); + System.out.println( indent + "updated=" + feed.getUpdated() ); + System.out.println( indent + "author=" + feed.getAuthor() ); + Collection collection = feed.getCollection(); + if ( collection == null ) { + System.out.println( indent + "collection=null" ); + } else { + System.out.println( indent + "collection=" + collection ); + } + // System.out.println( indent + "collection size=" + feed.getCollection() ); + // for (Collection collection : workspace.getCollections()) { + // if (collection.getTitle().equals("customers")) { + // String expected = uri + "customers"; + // String actual = collection.getResolvedHref().toString(); + // assertEquals(expected, actual); + // } + // } + + } + + /** + * Read response ream from the given socket. + * @param socket + * @return + * @throws IOException + */ + private static String read(InputStream inputStream) throws IOException { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader( inputStream )); + StringBuffer sb = new StringBuffer(); + String str; + while ((str = reader.readLine()) != null) { + sb.append(str); + } + return sb.toString(); + } finally { + if (reader != null) { + reader.close(); + } + } + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderServiceDocumentTestCase.java b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderServiceDocumentTestCase.java new file mode 100644 index 0000000000..96ea508210 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderServiceDocumentTestCase.java @@ -0,0 +1,209 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.sca.binding.atom; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import junit.framework.Assert; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.tuscany.sca.host.embedded.SCADomain; +import org.apache.tuscany.sca.binding.atom.collection.Collection; + +import org.apache.abdera.Abdera; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Service; +import org.apache.abdera.protocol.Response.ResponseType; +import org.apache.abdera.protocol.client.AbderaClient; +import org.apache.abdera.protocol.client.ClientResponse; +import org.apache.abdera.protocol.client.RequestOptions; +import org.apache.abdera.parser.Parser; + +/** + * Tests use of service documents provided by atom binding based collections. + * Uses the SCA provided Provider composite to act as a server. + * Uses the Abdera provided Client to act as a client. + */ +public class ProviderServiceDocumentTestCase { + public final static String providerURI = "http://localhost:8084/customer"; + protected static SCADomain scaProviderDomain; + protected static SCADomain scaConsumerDomain; + protected static CustomerClient testService; + protected static Abdera abdera; + protected static AbderaClient client; + protected static Parser abderaParser; + + @BeforeClass + public static void init() throws Exception { + scaProviderDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Provider.composite"); + scaConsumerDomain = SCADomain.newInstance("org/apache/tuscany/sca/binding/atom/Consumer.composite"); + testService = scaConsumerDomain.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + scaProviderDomain.close(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderDomain); + Assert.assertNotNull( client ); + } + + @Test + public void testFeedBasics() throws Exception { + // Normal feed request + ClientResponse res = client.get(providerURI); + Assert.assertNotNull(res); + try { + // Assert feed provided since no predicates + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + // AtomTestCaseUtils.printResponseHeaders( "Feed response headers:", " ", res ); + // System.out.println("Feed response content:"); + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + + // Perform other tests on feed. + // Warning. AbderaClient.getEntityTag is very particular on tag pattern. + // Document doc = res.getDocument(); + String body = read( res.getInputStream() ); + // RFC 4287 requires non-null id, title, updated elements + Assert.assertTrue( -1 != body.indexOf( "" )); + Assert.assertTrue( -1 != body.indexOf( "" )); + Assert.assertTrue( -1 != body.indexOf( "" )); + } finally { + res.release(); + } + } + + @Test + public void testServiceDocumentGet() throws Exception { + Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + + Entry postEntry = postEntry("Sponge Bob"); + Entry newEntry = resourceCollection.post(postEntry); + postEntry = postEntry("Austin Powers"); + newEntry = resourceCollection.post(postEntry); + postEntry = postEntry("Count Dracula"); + newEntry = resourceCollection.post(postEntry); + + // Service document + ClientResponse res = client.get(providerURI + "/atomsvc"); + Assert.assertNotNull(res); + try { + // Asser feed provided since no predicates + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + + // Perform other tests on feed. + // AtomTestCaseUtils.prettyPrint(abdera, res.getDocument()); + Document serviceDoc = res.getDocument(); + Service service = serviceDoc.getRoot(); + Assert.assertNotNull( service ); + org.apache.abdera.model.Collection collection = service.getCollection( "workspace", "customers" ); + String title = collection.getTitle(); + Assert.assertEquals("customers", title); + String href = collection.getHref().toString(); + Assert.assertTrue( href.contains( "customer") ); + } finally { + res.release(); + } + } + + public static void printFeed( String title, String indent, Feed feed ) { + if ( feed == null ) { + System.out.println( title + " feed is null"); + return; + } + + System.out.println( title ); + System.out.println( indent + "id=" + feed.getId() ); + System.out.println( indent + "title=" + feed.getTitle() ); + System.out.println( indent + "updated=" + feed.getUpdated() ); + System.out.println( indent + "author=" + feed.getAuthor() ); + // Collection collection = feed.getCollection(); + // if ( collection == null ) { + // System.out.println( indent + "collection=null" ); + // } else { + // System.out.println( indent + "collection=" + collection ); + // } + // System.out.println( indent + "collection size=" + feed.getCollection() ); + // for (Collection collection : workspace.getCollections()) { + // if (collection.getTitle().equals("customers")) { + // String expected = uri + "customers"; + // String actual = collection.getResolvedHref().toString(); + // assertEquals(expected, actual); + // } + // } + + } + + private Entry postEntry(String value) { + Entry entry = abdera.newEntry(); + entry.setTitle("customer " + value); + + Content content = abdera.getFactory().newContent(); + content.setContentType(Content.Type.TEXT); + content.setValue(value); + entry.setContentElement(content); + + return entry; + } + + /** + * Read response ream from the given socket. + * @param socket + * @return + * @throws IOException + */ + private static String read(InputStream inputStream) throws IOException { + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader( inputStream )); + StringBuffer sb = new StringBuffer(); + String str; + while ((str = reader.readLine()) != null) { + sb.append(str); + } + return sb.toString(); + } finally { + if (reader != null) { + reader.close(); + } + } + } +} diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Consumer.composite b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Consumer.composite new file mode 100644 index 0000000000..a2c50f872e --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Consumer.composite @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Provider.composite b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Provider.composite new file mode 100644 index 0000000000..60097ee0f7 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/Provider.composite @@ -0,0 +1,33 @@ + + + + + + + + + + + + + diff --git a/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite new file mode 100644 index 0000000000..71aa932a12 --- /dev/null +++ b/branches/sca-java-1.4/modules/binding-atom-abdera/src/test/resources/org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite @@ -0,0 +1,33 @@ + + + + + + + + + + + + + -- cgit v1.2.3