diff options
Diffstat (limited to '')
48 files changed, 6378 insertions, 0 deletions
diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/collection/Collection.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/collection/Collection.java new file mode 100644 index 0000000000..81ec1ddad3 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/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.oasisopen.sca.annotation.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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/collection/MediaCollection.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/collection/MediaCollection.java new file mode 100644 index 0000000000..d318e5b7e7 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/collection/MediaCollection.java @@ -0,0 +1,54 @@ +/* + * 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.apache.abdera.model.Entry; +import org.oasisopen.sca.annotation.Remotable; + +/** + * 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/collection/NotFoundException.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/collection/NotFoundException.java new file mode 100644 index 0000000000..b457840922 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java new file mode 100644 index 0000000000..4af8c3ca14 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingInvoker.java @@ -0,0 +1,609 @@ +/* + * 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.InputStream; +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.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +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.oasisopen.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, + org.apache.http.client.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, + org.apache.http.client.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 + HttpGet getMethod = new HttpGet(uri + "/" + id); + if (authorizationHeader != null) { + getMethod.setHeader("Authorization", authorizationHeader); + } + boolean parsing = false; + HttpResponse response = null; + try { + response = httpClient.execute(getMethod); + int status = response.getStatusLine().getStatusCode(); + + // Read the Atom entry + if (status == 200) { + InputStream content = response.getEntity().getContent(); + Document<org.apache.abdera.model.Entry> doc = abderaParser.parse(content); + content.close(); + 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<Object, Object> entry = + entry(feedEntry, + provider.getItemClassType(), + provider.getItemXMLType(), + provider.getMediator()); + msg.setBody(entry.getData()); + } + + } else if (status == 404) { + if (provider.supportsFeedEntries()) + msg.setFaultBody(new NotFoundException()); + else + msg.setFaultBody(new org.apache.tuscany.sca.data.collection.NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + release(getMethod, response); + + } + + 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<Object, Object> entry = new Entry<Object, Object>(args[0], args[1]); + feedEntry = + feedEntry(entry, + provider.getItemClassType(), + provider.getItemXMLType(), + provider.getMediator(), + abderaFactory); + } + + // Send an HTTP POST + HttpPost postMethod = new HttpPost(uri); + if (authorizationHeader != null) { + postMethod.setHeader("Authorization", authorizationHeader); + } + boolean parsing = false; + HttpResponse response = null; + try { + + // Write the Atom entry + StringWriter writer = new StringWriter(); + feedEntry.writeTo(writer); + // postMethod.setHeader("Content-type", "application/atom+xml; charset=utf-8"); - TUSCANY-3734 + postMethod.setHeader("Content-type", "application/atom+xml"); + postMethod.setEntity(new StringEntity(writer.toString())); + + response = httpClient.execute(postMethod); + int status = response.getStatusLine().getStatusCode(); + + // Read the Atom entry + if (status == 200 || status == 201) { + InputStream content = postMethod.getEntity().getContent(); + Document<org.apache.abdera.model.Entry> doc = abderaParser.parse(content); + content.close(); + 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) { + if (provider.supportsFeedEntries()) + msg.setFaultBody(new NotFoundException()); + else + msg.setFaultBody(new org.apache.tuscany.sca.data.collection.NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + release(postMethod, response); + + } + + 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<Object, Object> entry = new Entry<Object, Object>(id, args[1]); + feedEntry = + feedEntry(entry, + provider.getItemClassType(), + provider.getItemXMLType(), + provider.getMediator(), + abderaFactory); + } + + // Send an HTTP PUT + HttpPut putMethod = new HttpPut(uri + "/" + id); + if (authorizationHeader != null) { + putMethod.setHeader("Authorization", authorizationHeader); + } + + HttpResponse response = null; + try { + + // Write the Atom entry + StringWriter writer = new StringWriter(); + feedEntry.writeTo(writer); + //putMethod.setHeader("Content-type", "application/atom+xml; charset=utf-8"); - TUSCANY-3734 + putMethod.setHeader("Content-type", "application/atom+xml"); + putMethod.setEntity(new StringEntity(writer.toString())); + + response = httpClient.execute(putMethod); + int status = response.getStatusLine().getStatusCode(); + if (status == 200 || status == 201 || status == 412) { + + msg.setBody(null); + + } else if (status == 404) { + if (provider.supportsFeedEntries()) + msg.setFaultBody(new NotFoundException()); + else + msg.setFaultBody(new org.apache.tuscany.sca.data.collection.NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + release(putMethod, response); + } + + 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 + HttpDelete deleteMethod = new HttpDelete(uri + "/" + id); + if (authorizationHeader != null) { + deleteMethod.setHeader("Authorization", authorizationHeader); + } + HttpResponse response = null; + try { + response = httpClient.execute(deleteMethod); + int status = response.getStatusLine().getStatusCode(); + if (status == 200) { + msg.setBody(null); + + } else if (status == 404) { + if (provider.supportsFeedEntries()) + msg.setFaultBody(new NotFoundException()); + else + msg.setFaultBody(new org.apache.tuscany.sca.data.collection.NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + release(deleteMethod, response); + } + + 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 + HttpGet getMethod = new HttpGet(uri); + if (authorizationHeader != null) { + getMethod.setHeader("Authorization", authorizationHeader); + } + boolean parsing = false; + HttpResponse response = null; + try { + response = httpClient.execute(getMethod); + int status = response.getStatusLine().getStatusCode(); + // AtomBindingInvoker.printResponseHeader( getMethod ); + + // Read the Atom feed + if (status == 200) { + InputStream content = response.getEntity().getContent(); + Document<Feed> doc = abderaParser.parse(content); + content.close(); + parsing = true; + + Feed feed = null; + try { + feed = doc.getRoot(); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid feed format :" + uri); + } + + if (provider.supportsFeedEntries()) { + + // Returns the Atom feed + msg.setBody(feed); + + } else { + + // Returns an array of data entries + List<Entry<Object, Object>> entries = new ArrayList<Entry<Object, Object>>(); + for (org.apache.abdera.model.Entry feedEntry : feed.getEntries()) { + Entry<Object, Object> entry = + entry(feedEntry, + provider.getItemClassType(), + provider.getItemXMLType(), + provider.getMediator()); + entries.add(entry); + } + msg.setBody(entries.toArray(new Entry[entries.size()])); + } + + } else if (status == 404) { + if (provider.supportsFeedEntries()) { + msg.setFaultBody(new NotFoundException()); + } else { + msg.setFaultBody(new org.apache.tuscany.sca.data.collection.NotFoundException()); + } + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + release(getMethod, response); + + } + + 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 + HttpGet getMethod = new HttpGet(uri + "?" + queryString); + if (authorizationHeader != null) { + getMethod.setHeader("Authorization", authorizationHeader); + } + // getMethod.setQueryString(queryString); + boolean parsing = false; + HttpResponse response = null; + try { + response = httpClient.execute(getMethod); + int status = response.getStatusLine().getStatusCode(); + + // Read the Atom feed + if (status == 200) { + InputStream content = response.getEntity().getContent(); + Document<Feed> doc = abderaParser.parse(content); + content.close(); + parsing = true; + Feed feed = doc.getRoot(); + + if (provider.supportsFeedEntries()) { + + // Returns the Atom feed + msg.setBody(feed); + + } else { + + // Returns an array of data entries + List<Entry<Object, Object>> entries = new ArrayList<Entry<Object, Object>>(); + for (org.apache.abdera.model.Entry feedEntry : feed.getEntries()) { + Entry<Object, Object> entry = + entry(feedEntry, + provider.getItemClassType(), + provider.getItemXMLType(), + provider.getMediator()); + entries.add(entry); + } + msg.setBody(entries.toArray(new Entry[entries.size()])); + } + + } else if (status == 404) { + if (provider.supportsFeedEntries()) + msg.setFaultBody(new NotFoundException()); + else + msg.setFaultBody(new org.apache.tuscany.sca.data.collection.NotFoundException()); + } else { + msg.setFaultBody(new ServiceRuntimeException("HTTP status code: " + status)); + } + + } catch (Exception e) { + msg.setFaultBody(new ServiceRuntimeException(e)); + } finally { + + // Release the connection unless the Abdera parser is + // parsing the response, in this case it will release it + release(getMethod, response); + + } + + return msg; + } + } + + private static void release(HttpRequestBase request, HttpResponse response) { + + if (response != null) { + HttpEntity entity = response.getEntity(); + if (entity != null) { + try { + entity.consumeContent(); + } catch (IOException e) { + if (request != null) { + request.abort(); + } + } + } + } + } + + /** + * 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java new file mode 100644 index 0000000000..861ff5d182 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingListenerServlet.java @@ -0,0 +1,910 @@ +/* + * 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.URLDecoder; +import java.text.SimpleDateFormat; +import java.util.Date; +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 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.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.tuscany.sca.binding.atom.utils.AtomBindingHttpUtils; +import org.apache.tuscany.sca.common.http.HTTPCacheContext; +import org.apache.tuscany.sca.common.http.HTTPUtils; +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.Operation; +import org.apache.tuscany.sca.interfacedef.impl.DataTypeImpl; +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.Invocable; + +/** + * 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 Invocable 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 String description; + private Mediator mediator; + private DataType<?> itemClassType; + private DataType<?> itemXMLType; + private boolean supportsFeedEntries; + + /** + * Constructs a new binding listener. + * + * @param wire + * @param messageFactory + * @param feedType + */ + AtomBindingListenerServlet(Invocable wire, MessageFactory messageFactory, Mediator mediator, String title, String description) { + this.wire = wire; + this.messageFactory = messageFactory; + this.mediator = mediator; + this.title = title; + this.description = description; + + // 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 + if (getOperation != null) { + itemXMLType = new DataTypeImpl<Class<?>>(String.class.getName(), String.class, String.class); + Class<?> itemClass = getOperation.getOutputType().getLogical().get(0).getPhysical(); + if (itemClass == org.apache.abdera.model.Entry.class) { + supportsFeedEntries = true; + } + //We assume that the item type is the same for both input and + //ouput for all operations on the interface + itemClassType = getOperation.getOutputType().getLogical().get(0); + } + } + + @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.createCacheContextFromRequest( request ); + } catch ( java.text.ParseException e ) { + } + // System.out.println( "AtomBindingListener.doGet cache context=" + cacheContext ); + + // Get the request path + String path = URLDecoder.decode(HTTPUtils.getRequestPath(request), "UTF-8"); + + logger.fine("get " + request.getRequestURI()); + + // Handle an Atom request + if (path != null && path.equals("/atomsvc")) { + /* + <?xml version='1.0' encoding='UTF-8'?> + <service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom"> + <workspace> + <atom:title type="text">resource</atom:title> + <collection href="http://luck.ibm.com:8084/customer"> + <atom:title type="text">entries</atom:title> + <accept>application/atom+xml</accept> + <categories /> + </collection> + </workspace> + </service> + */ + + // Return the Atom service document + response.setDateHeader("Date", System.currentTimeMillis()); + response.setContentType("application/atomsvc+xml"); + + 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"); + collection.addAccepts("application/json"); + List<Category> 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"); + collection.addAccepts("application/json"); + 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; + feedETag = HTTPUtils.calculateHashETag(feed.toString().getBytes("utf-8")); + 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( exactSeconds(feedUpdated) ) < 0 ) { + // Match, should short circuit + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + 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( exactSeconds(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 )); + } + + //default http header for response + AtomBindingHttpUtils.prepareHTTPResponse(request, response); + + // 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"); + + 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"); + 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()) { + Object body = responseMessage.getBody(); + if (body.getClass().getName().endsWith(".NotFoundException")) { + response.sendError(HttpServletResponse.SC_NOT_FOUND); + return; + } else { + 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<Object, Object> entry = new Entry<Object, Object>(id, responseMessage.getBody()); + feedEntry = feedEntry(entry, itemClassType, itemXMLType, mediator, abderaFactory); + } + // Write the Atom entry + if (feedEntry != null) { + String entryETag = null; + entryETag = HTTPUtils.calculateHashETag(feedEntry.toString().getBytes("utf-8")); + 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 )); + } + + //default http header for response + AtomBindingHttpUtils.prepareHTTPResponse(request, response); + + // 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"); + 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"); + 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<Object, Object>[] collection = (Entry<Object, Object>[])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"); + } + if (description != null) { + feed.setSubtitle(description); + } + // All feeds must provide Id and updated elements. + // However, some do not, so provide some program protection. + feed.setId(request.getRequestURI()); + Date responseLastModified = new Date( 0 ); + + // Add entries to the feed + for (Entry<Object, Object> 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(HTTPUtils.getRequestPath(request), "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<org.apache.abdera.model.Entry> 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<Object, Object> 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, "\"" + HTTPUtils.calculateHashETag(createdFeedEntry.toString().getBytes("utf-8")) + "\"" ); + } + 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()); + } + + //default http header for response + AtomBindingHttpUtils.prepareHTTPResponse(request, response); + + // Write the created Atom entry + response.setStatus(HttpServletResponse.SC_CREATED); + response.setContentType("application/atom+xml"); + 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 = URLDecoder.decode(HTTPUtils.getRequestPath(request), "UTF-8"); + + 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<org.apache.abdera.model.Entry> 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<Object, Object> 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(HTTPUtils.getRequestPath(request), "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); + } + + /** + * Get content-type preference using application/atom-xml as default + * @param acceptType content-type preference using application/atom-xml as default + * @return + */ + private 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 + */ + private 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); + } + } + } + + /** + * Round a date down to an exact number of seconds + * @param date with millisecond precision + * @return date rounded down to nearest second + */ + private static Date exactSeconds(Date date) { + return new Date(date.getTime() / 1000 * 1000); + } + +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingProviderFactory.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingProviderFactory.java new file mode 100644 index 0000000000..a160925e4e --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingProviderFactory.java @@ -0,0 +1,65 @@ +/* + * 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.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.FactoryExtensionPoint; +import org.apache.tuscany.sca.core.UtilityExtensionPoint; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.host.http.ServletHost; +import org.apache.tuscany.sca.host.http.ServletHostHelper; +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.RuntimeEndpoint; +import org.apache.tuscany.sca.runtime.RuntimeEndpointReference; + +/** + * Implementation of a Binding provider factory for the Atom binding. + * + * @version $Rev$ $Date$ + */ +public class AtomBindingProviderFactory implements BindingProviderFactory<AtomBinding> { + + private MessageFactory messageFactory; + private Mediator mediator; + private ServletHost servletHost; + + public AtomBindingProviderFactory(ExtensionPointRegistry extensionPoints) { + this.servletHost = ServletHostHelper.getServletHost(extensionPoints); + FactoryExtensionPoint modelFactories = extensionPoints.getExtensionPoint(FactoryExtensionPoint.class); + this.messageFactory = modelFactories.getFactory(MessageFactory.class); + this.mediator = extensionPoints.getExtensionPoint(UtilityExtensionPoint.class).getUtility(Mediator.class); + } + + public ReferenceBindingProvider createReferenceBindingProvider(RuntimeEndpointReference endpointReference) { + return new AtomReferenceBindingProvider(endpointReference, mediator); + } + + public ServiceBindingProvider createServiceBindingProvider(RuntimeEndpoint endpoint) { + return new AtomServiceBindingProvider(endpoint, messageFactory, mediator, servletHost); + } + + public Class<AtomBinding> getModelType() { + return AtomBinding.class; + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingUtil.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingUtil.java new file mode 100644 index 0000000000..ad1c8d2fd0 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomBindingUtil.java @@ -0,0 +1,167 @@ +/* + * 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.Content.Type; +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<Object, Object> entry(org.apache.abdera.model.Entry feedEntry, + DataType<?> itemClassType, DataType<?> itemXMLType, Mediator mediator) { + if (feedEntry != null) { + if (itemClassType.getPhysical() == Item.class || feedEntry.getContentType() == Type.HTML ) { + 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<Object, Object>(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<Object, Object>(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<Object, Object> 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomReferenceBindingProvider.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomReferenceBindingProvider.java new file mode 100644 index 0000000000..9f4ea496c1 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomReferenceBindingProvider.java @@ -0,0 +1,171 @@ +/* + * 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.http.client.HttpClient; +import org.apache.tuscany.sca.assembly.EndpointReference; +import org.apache.tuscany.sca.binding.atom.AtomBinding; +import org.apache.tuscany.sca.databinding.Mediator; +import org.apache.tuscany.sca.host.http.client.HttpClientFactory; +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.RuntimeComponentReference; + +/** + * Implementation of the Atom binding provider. + * + * @version $Rev$ $Date$ + */ +class AtomReferenceBindingProvider implements ReferenceBindingProvider { + private EndpointReference endpointReference; + private RuntimeComponentReference reference; + private AtomBinding binding; + private String authorizationHeader = null; + 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(EndpointReference endpointReference, + Mediator mediator) { + this.endpointReference = endpointReference; + this.reference = (RuntimeComponentReference) endpointReference.getReference(); + this.binding = (AtomBinding) endpointReference.getBinding(); + this.mediator = mediator; + + // Prepare authorization header + // TUSCANY-3735: Don't send authorization header by default as this can cause problems. + // Commented out the following two lines until we have a better way to control this. + //String authorization = "admin" + ":" + "admin"; + //authorizationHeader = "Basic " + new String(Base64.encodeBase64(authorization.getBytes())); + + } + + public Invoker createInvoker(Operation operation) { + + String operationName = operation.getName(); + if (operationName.equals("get")) { + + // Determine the collection item type + itemXMLType = new DataTypeImpl<Class<?>>(String.class.getName(), String.class, String.class); + DataType<XMLType> outputType = operation.getOutputType().getLogical().get(0); + itemClassType = outputType; + 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 + // TUSCANY-3735: Don't use authentication by default as this can cause problems. + // Commented out the following four lines until we have a better way to control this. + //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 + // Create an HTTP client + HttpClientFactory clientFactory = new HttpClientFactory(); + httpClient = clientFactory.createHttpClient(); + } + + public void stop() { + if (httpClient != null) { + httpClient.getConnectionManager().shutdown(); + } + } + + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomServiceBindingProvider.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomServiceBindingProvider.java new file mode 100644 index 0000000000..bc8149a67a --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/provider/AtomServiceBindingProvider.java @@ -0,0 +1,113 @@ +/* + * 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.Invocable; +import org.apache.tuscany.sca.runtime.RuntimeComponent; +import org.apache.tuscany.sca.runtime.RuntimeComponentService; +import org.apache.tuscany.sca.runtime.RuntimeEndpoint; + +/** + * Implementation of the Atom binding provider. + * + * @version $Rev$ $Date$ + */ +class AtomServiceBindingProvider implements ServiceBindingProvider { + private MessageFactory messageFactory; + + private RuntimeEndpoint endpoint; + + private RuntimeComponent component; + private RuntimeComponentService service; + private InterfaceContract serviceContract; + private AtomBinding binding; + private ServletHost servletHost; + private Mediator mediator; + + private String servletMapping; + private String bindingURI; + + AtomServiceBindingProvider(RuntimeEndpoint endpoint, + MessageFactory messageFactory, + Mediator mediator, + ServletHost servletHost) { + this.endpoint = endpoint; + this.component = (RuntimeComponent)endpoint.getComponent(); + this.service = (RuntimeComponentService)endpoint.getService(); + this.binding = (AtomBinding) endpoint.getBinding(); + + this.servletHost = servletHost; + this.messageFactory = messageFactory; + this.mediator = mediator; + + // TUSCANY-3166 + this.serviceContract = endpoint.getComponentTypeServiceInterfaceContract(); + } + + public InterfaceContract getBindingInterfaceContract() { + return serviceContract; + } + + public boolean supportsOneWayInvocation() { + return false; + } + + public void start() { + Invocable wire = (RuntimeEndpoint) endpoint; + + AtomBindingListenerServlet servlet = + new AtomBindingListenerServlet(wire, messageFactory, mediator, binding.getTitle(), binding.getDescription()); + + servletMapping = binding.getURI(); + if (!servletMapping.endsWith("/")) { + servletMapping += "/"; + } + if (!servletMapping.endsWith("*")) { + servletMapping += "*"; + } + servletHost.addServletMapping(servletMapping, servlet); + + bindingURI = binding.getURI(); + if (!bindingURI.endsWith("/")) { + bindingURI += "/"; + } + + String mappedURI = servletHost.addServletMapping(bindingURI, servlet); + String deployedURI = mappedURI; + if (deployedURI.endsWith("*")) { + deployedURI = deployedURI.substring(0, deployedURI.length() - 1); + } + if (deployedURI.endsWith("/")) { + deployedURI = deployedURI.substring(0, deployedURI.length() - 1); + } + binding.setURI(deployedURI); + } + + public void stop() { + servletHost.removeServletMapping(servletMapping); + servletHost.removeServletMapping(bindingURI); + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/utils/AtomBindingHttpUtils.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/utils/AtomBindingHttpUtils.java new file mode 100644 index 0000000000..f6f914c71c --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/java/org/apache/tuscany/sca/binding/atom/utils/AtomBindingHttpUtils.java @@ -0,0 +1,56 @@ +/* + * 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.utils; + +import java.util.Date; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.tuscany.sca.common.http.HTTPUtils; + +public class AtomBindingHttpUtils { + /** + * Utility method to set common http headers and other stuff in a + * default http response. Applications / Extensions can then override and + * tweak as they see fit. + * + * @param response + */ + public static void prepareHTTPResponse(HttpServletRequest request, HttpServletResponse response) { + + // common http default response values + HTTPUtils.prepareHTTPResponse(response); + + //set Cache-Control to no-cache to avoid intermediary + //proxy/reverse-proxy caches and always hit the server + //that would identify if the value was current or not + //response.setHeader("Cache-Control", "no-cache"); + //response.setHeader("Expires", new Date(0).toGMTString()); + + } + + private static boolean isIE(HttpServletRequest request) { + String userAgent = request.getHeader("user-agent"); + + return (userAgent.indexOf("MSIE") > -1); + } + +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/resources/META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/resources/META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory new file mode 100644 index 0000000000..1accd4cfb4 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/main/resources/META-INF/services/org.apache.tuscany.sca.provider.BindingProviderFactory @@ -0,0 +1,20 @@ +# 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AbstractProviderConsumerTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AbstractProviderConsumerTestCase.java new file mode 100644 index 0000000000..77c2562c0f --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AbstractProviderConsumerTestCase.java @@ -0,0 +1,53 @@ +/* + * 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.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; + +/** + * + * @version $Rev$ $Date$ + */ +public abstract class AbstractProviderConsumerTestCase { + protected static Node scaProviderNode; + protected static Node scaConsumerNode; + + protected static void initTestEnvironment(Class<?> testClazz) throws Exception { + String contribution = ContributionLocationHelper.getContributionLocation(testClazz); + + scaProviderNode = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Provider.composite", new Contribution("provider", contribution)); + scaProviderNode.start(); + + scaConsumerNode = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Consumer.composite", new Contribution("consumer", contribution)); + scaConsumerNode.start(); + } + + protected static void destroyTestEnvironment() throws Exception { + if (scaConsumerNode != null) { + scaConsumerNode.stop(); + } + if (scaProviderNode != null) { + scaProviderNode.stop(); + } + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomDeleteTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomDeleteTestCase.java new file mode 100644 index 0000000000..da3c8b7594 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomDeleteTestCase.java @@ -0,0 +1,118 @@ +/* + * 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.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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @version $Rev$ $Date$ + */ +public class AtomDeleteTestCase extends AbstractProviderConsumerTestCase { + + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + try { + // System.out.println(">>>AtomDeleteTestCase.init entry"); + + initTestEnvironment(AtomDeleteTestCase.class); + + testService = scaConsumerNode.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + // System.out.println(">>>AtomDeleteTestCase.destroy entry"); + + destroyTestEnvironment(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + Assert.assertNotNull(scaConsumerNode); + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomFeedNonCollectionTest.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomFeedNonCollectionTest.java new file mode 100644 index 0000000000..9c0ad5a26a --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomFeedNonCollectionTest.java @@ -0,0 +1,219 @@ +/* + * 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.List; +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Assert; + +import org.apache.abdera.model.Link; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.data.collection.Item; +import org.apache.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Test cases for using an Atom feed that does not implement + * the Collections interface but does have a getAll() method. + * + * @version $Rev$ $Date$ + */ +public class AtomFeedNonCollectionTest { + /** + * Used to generate unique IDs for the feed entries. + */ + private static final AtomicInteger ID_GEN = new AtomicInteger(); + + /** + * Data used for creating test feed entries. + */ + private static final String[] FEED_ENTRY_TITLES = {"Apache Tuscany", "Apache"}; + + /** + * Data used for creating test feed entries. + */ + private static final String[] FEED_ENTRY_CONTENT = {"This is Apache Tuscany", "This is Apache"}; + + /** + * Data used for creating test feed entries. + */ + private static final String[] FEED_ENTRY_LINK = {"http://tuscany.apache.org", "http://www.apache.org"}; + + /** + * The number of test feed entries. + */ + private static final int FEED_ENTRY_COUNT = FEED_ENTRY_TITLES.length; + + protected static Node scaProviderNode; + protected static Node scaConsumerNode; + + private static CustomerClient testService; + + @BeforeClass + public static void init() throws Exception { + try { + //System.out.println(">>>AtomFeedNonCollectionTest.init entry"); + + String contribution = ContributionLocationHelper.getContributionLocation(AtomFeedNonCollectionTest.class); + + scaProviderNode = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/ProviderNonCollection.composite", new Contribution("provider", contribution)); + scaProviderNode.start(); + + scaConsumerNode = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Consumer.composite", new Contribution("consumer", contribution)); + scaConsumerNode.start(); + + testService = scaConsumerNode.getService(CustomerClient.class, "CustomerClient"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + if (scaConsumerNode != null) { + scaConsumerNode.stop(); + } + if (scaProviderNode != null) { + scaProviderNode.stop(); + } + } + + /** + * Make sure everything has been initialized correctly. + */ + @SuppressWarnings("unchecked") + @Before + public void testPrelim() { + Assert.assertNotNull(scaConsumerNode); + Assert.assertNotNull(scaProviderNode); + Assert.assertNotNull(testService); + + // Add some entries to the Atom feed + final Entry<Object, Object>[] testEntries = new Entry[FEED_ENTRY_COUNT]; + for (int i = 0; i < FEED_ENTRY_COUNT; i++) { + testEntries[i] = createFeedEntry(FEED_ENTRY_TITLES[i], FEED_ENTRY_CONTENT[i], FEED_ENTRY_LINK[i]); + } + CustomerNonCollectionImpl.entries = testEntries; + } + + /** + * Post test cleanup + */ + @After + public void testPostCleanup() + { + // Clear any old feed entries that have been added by previous tests + CustomerNonCollectionImpl.entries = null; + } + + /** + * Test that we can retrieve entries from a feed that does not implement + * the Collection interface. + */ + @Test + public void testThatCanGetFeedEntriesFromNonCollectionImplementation() { + // Get the entries from the feed + final Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + final List<org.apache.abdera.model.Entry> entries = resourceCollection.getFeed().getEntries(); + + // Validate the feed entries + Assert.assertNotNull(entries); + Assert.assertEquals(FEED_ENTRY_COUNT, entries.size()); + for (int i = 0; i < FEED_ENTRY_COUNT; i++) { + final org.apache.abdera.model.Entry entry = entries.get(i); + Assert.assertEquals(FEED_ENTRY_TITLES[i], entry.getTitle()); + Assert.assertEquals(FEED_ENTRY_CONTENT[i], entry.getContent()); + final List<Link> links = entry.getLinks(); + Assert.assertNotNull(links); + Assert.assertEquals(1, links.size()); + final Link link = links.get(0); + final String linkStr = link.getHref().toString(); + Assert.assertEquals(FEED_ENTRY_LINK[i], linkStr); + } + } + + /** + * Tests that the title of the feed can be set by the title + * attribute on the binding.atom + */ + @Test + public void testThatFeedTitleSet() { + final String expectedFeedTitle = "Atom binding Non Collection"; + + // Get the title of the feed + final Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + final String feedTitle = resourceCollection.getFeed().getTitle(); + + // Validate the title + Assert.assertEquals(expectedFeedTitle, feedTitle); + } + + /** + * Tests that the description of the feed can be set by the description + * attribute on the binding.atom + */ + @Test + public void testThatFeedDescriptionSet() { + final String expectedFeedDescription = "Feed used for unit testing"; + + // Get the description of the feed + final Collection resourceCollection = testService.getCustomerCollection(); + Assert.assertNotNull(resourceCollection); + final String feedDescription = resourceCollection.getFeed().getSubtitle(); + + // Validate the description + Assert.assertEquals(expectedFeedDescription, feedDescription); + } + + /** + * Creates a feed entry. + * + * @param title Title for the feed entry + * @param content Contents of the feed entry + * @param link Link for the feed entry + * @return A new feed entry. + */ + private Entry<Object, Object> createFeedEntry(String title, String content, String link) { + final Item item = new Item(title, content, link, null, new Date()); + final Entry<Object, Object> entry = new Entry<Object, Object>(nextFeedID(), item); + return entry; + } + + /** + * Generates the feed entry ID. + * + * @return Next feed entry ID + */ + private String nextFeedID() { + return Integer.toString(ID_GEN.incrementAndGet()); + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomGetTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomGetTestCase.java new file mode 100644 index 0000000000..7f74ef019d --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomGetTestCase.java @@ -0,0 +1,121 @@ +/* + * 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 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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @version $Rev$ $Date$ + */ +public class AtomGetTestCase extends AbstractProviderConsumerTestCase { + + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + try { + //System.out.println(">>>AtomGetTestCase.init entry"); + + initTestEnvironment(AtomFeedNonCollectionTest.class); + + testService = scaConsumerNode.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + // System.out.println(">>>AtomGetTestCase.destroy entry"); + destroyTestEnvironment(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + Assert.assertNotNull(scaConsumerNode); + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPostTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPostTestCase.java new file mode 100644 index 0000000000..50a684beba --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPostTestCase.java @@ -0,0 +1,110 @@ +/* + * 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 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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @version $Rev$ $Date$ + */ +public class AtomPostTestCase extends AbstractProviderConsumerTestCase { + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + try { + //System.out.println(">>>AtomPostTestCase.init entry"); + + initTestEnvironment(AtomPostTestCase.class); + + testService = scaConsumerNode.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + //System.out.println(">>>AtomPostTestCase.destroy entry"); + + destroyTestEnvironment(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + Assert.assertNotNull(scaConsumerNode); + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPutTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPutTestCase.java new file mode 100644 index 0000000000..4e09eea032 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/AtomPutTestCase.java @@ -0,0 +1,129 @@ +/* + * 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.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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * + * @version $Rev$ $Date$ + */ +public class AtomPutTestCase extends AbstractProviderConsumerTestCase { + + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + try { + //System.out.println(">>>AtomPutTestCase.init entry"); + + initTestEnvironment(AtomPutTestCase.class); + + testService = scaConsumerNode.getService(CustomerClient.class, "CustomerClient"); + abdera = new Abdera(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + // System.out.println(">>>AtomPutTestCase.destroy entry"); + + destroyTestEnvironment(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + Assert.assertNotNull(scaConsumerNode); + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/Consumer.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/Consumer.java new file mode 100644 index 0000000000..e6fa67883a --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/Consumer.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.tuscany.sca.binding.atom; + +import org.apache.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; + +/** + * + * @version $Rev$ $Date$ + */ +public class Consumer { + + public static void main(String[] args) throws Exception { + + String contribution = ContributionLocationHelper.getContributionLocation(Consumer.class); + Node node = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Consumer.composite",new Contribution("c1", contribution)); + + CustomerClient testService = node.getService(CustomerClient.class, "CustomerClient"); + testService.testCustomerCollection(); + + node.stop(); + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ConsumerProviderAtomTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ConsumerProviderAtomTestCase.java new file mode 100644 index 0000000000..07909388e1 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ConsumerProviderAtomTestCase.java @@ -0,0 +1,159 @@ +/* + * 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.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Test case for the given package. + * + * @version $Rev$ $Date$ + */ +public class ConsumerProviderAtomTestCase extends AbstractProviderConsumerTestCase { + + protected static CustomerClient testService; + protected static Abdera abdera; + + @BeforeClass + public static void init() throws Exception { + try { + //System.out.println(">>>AtomBindingIntegratedTestCase.init entry"); + + initTestEnvironment(ConsumerProviderAtomTestCase.class); + + testService = scaConsumerNode.getService(CustomerClient.class,"CustomerClient"); + abdera = new Abdera(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + //System.out.println(">>>AtomBindingIntegratedTestCase.destroy entry"); + + destroyTestEnvironment(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + Assert.assertNotNull(scaConsumerNode); + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ContentNegotiationTest.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ContentNegotiationTest.java new file mode 100644 index 0000000000..e9ebf046a6 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ContentNegotiationTest.java @@ -0,0 +1,258 @@ +/* + * 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.util.Date; + +import junit.framework.Assert; + +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.Document; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.parser.Parser; +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.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + + +/** + * 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. + * + * @version $Rev$ $Date$ + */ +public class ContentNegotiationTest { + public final static String providerURI = "http://localhost:8084/customer"; + + protected static Node scaProviderNode; + + 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 { + try { + //System.out.println(">>>ContentNegotiationTest.init"); + String contribution = ContributionLocationHelper.getContributionLocation(ContentNegotiationTest.class); + + scaProviderNode = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Provider.composite", new Contribution("provider", contribution)); + scaProviderNode.start(); + + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + //System.out.println(">>>ContentNegotiationTest.destroy"); + if (scaProviderNode != null) { + scaProviderNode.stop(); + } + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + 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"; + 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<Feed> 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClient.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClient.java new file mode 100644 index 0000000000..85c4d4f56a --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClient.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * + * @version $Rev$ $Date$ + */ +public interface CustomerClient { + + void testCustomerCollection() throws Exception; + + Collection getCustomerCollection(); +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClientImpl.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClientImpl.java new file mode 100644 index 0000000000..4e3b8241f8 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerClientImpl.java @@ -0,0 +1,103 @@ +/* + * 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.oasisopen.sca.annotation.Reference; + +/** + * + * @version $Rev$ $Date$ + */ +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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerCollectionImpl.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerCollectionImpl.java new file mode 100644 index 0000000000..9e50cbbae7 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerCollectionImpl.java @@ -0,0 +1,153 @@ +/* + * 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.oasisopen.sca.annotation.Scope; + +/** + * + * @version $Rev$ $Date$ + */ +@Scope("COMPOSITE") +public class CustomerCollectionImpl implements Collection { + private final Abdera abdera = new Abdera(); + private Map<String, Entry> entries = new HashMap<String, Entry>(); + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerNonCollectionImpl.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerNonCollectionImpl.java new file mode 100644 index 0000000000..dbf6b5aed5 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/CustomerNonCollectionImpl.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; + +import org.apache.tuscany.sca.data.collection.Entry; +import org.oasisopen.sca.annotation.Scope; + +/** + * Implementation of an Atom feed that does not implement the Collections + * interface but does have a getAll() method that will be used by the Atom + * binding to get the feed entries. + * + * @version $Rev$ $Date$ + */ +@Scope("COMPOSITE") +public class CustomerNonCollectionImpl { + /** + * All feed entries. + * This is set directly by the unit tests. + * @see AtomFeedNonCollectionTest + */ + public static Entry<Object, Object>[] entries; + + /** + * Default constructor + */ + public CustomerNonCollectionImpl() { + } + + /** + * Get all entries for this feed. + * + * @return All entries for this feed + */ + public Entry<Object, Object>[] getAll() { + return entries; + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionImpl.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionImpl.java new file mode 100644 index 0000000000..8ff89030db --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionImpl.java @@ -0,0 +1,213 @@ +/* + * 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.MediaCollection; +import org.apache.tuscany.sca.binding.atom.collection.NotFoundException; +import org.oasisopen.sca.annotation.Scope; + +/** + * + * @version $Rev$ $Date$ + */ +@Scope("COMPOSITE") +public class MediaCollectionImpl implements MediaCollection { + private final Abdera abdera = new Abdera(); + private Map<String, Entry> entries = new HashMap<String, Entry>(); + private Map<String, String> mediaFiles = new HashMap<String, String>(); + 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) + // <?xml version="1.0"?> + // <entry xmlns="http://www.w3.org/2005/Atom"> + // <title>The Beach</title> (REQUIRED) + // <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> (REQUIRED) + // <updated>2005-10-07T17:17:08Z</updated> + // <summary type="text" /> (REQUIRED, OPTIONAL to populate + // <content type="image/png" src="http://media.example.org/the_beach.png"/> + // <link rel="edit-media" href="http://media.example.org/edit/the_beach.png" /> + // <link rel="edit" href="http://example.org/media/edit/the_beach.atom" /> + // </entry> + + // 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionTestCase.java new file mode 100644 index 0000000000..f3cb052e39 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/MediaCollectionTestCase.java @@ -0,0 +1,300 @@ +/* + * 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.text.SimpleDateFormat; +import java.util.Date; + +import junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Link; +import org.apache.abdera.parser.Parser; +import org.apache.abdera.protocol.client.AbderaClient; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.entity.FileEntity; +import org.apache.tuscany.sca.host.http.client.HttpClientFactory; +import org.apache.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * 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. + * + * @version $Rev$ $Date$ + */ +public class MediaCollectionTestCase { + public final static String providerURI = "http://localhost:8084/receipt"; + + protected static Node scaProviderNode; + + 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 { + try { + //System.out.println(">>>MediaCollectionTestCase.init"); + String contribution = ContributionLocationHelper.getContributionLocation(MediaCollectionTestCase.class); + + scaProviderNode = + NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite", + new Contribution("provider", contribution)); + scaProviderNode.start(); + + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + System.out.println(">>>MediaCollectionTestCase.destroy"); + if (scaProviderNode != null) { + scaProviderNode.stop(); + } + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + 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 = "target/test-classes/ReceiptToms.gif"; + File input = new File(fileName); + boolean exists = input.exists(); + Assert.assertTrue(exists); + + // Prepare HTTP post + // PostMethod post = new PostMethod( colUri.toString() ); + HttpPost post = new HttpPost(providerURI); + post.addHeader("Content-Type", "image/gif"); + post.addHeader("Title", "Title " + receiptName + ""); + post.addHeader("Slug", "Slug " + receiptName + ""); + + post.setEntity(new FileEntity(input, "image/gif")); + + // Get HTTP client + org.apache.http.client.HttpClient httpclient = new HttpClientFactory().createHttpClient(); + try { + // Execute request + HttpResponse response = httpclient.execute(post); + int result = response.getStatusLine().getStatusCode(); + // 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=" + response.getFirstHeader( "Location" ).getValue() + "." ); + Header header = response.getFirstHeader("Location"); + Assert.assertNotNull(header); + Assert.assertNotNull(header.getValue()); + // ContentLocation: http://example.org/media/edit/the_beach.jpg (REQUIRED) + // System.out.println( "Response Content-Location=" + response.getFirstHeader( "Content-Location" ).getValue() ); + header = response.getFirstHeader("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=" + response.getFirstHeader( "Content-Type" ).getValue()); + header = response.getFirstHeader("Content-Type"); + Assert.assertNotNull(header); + Assert.assertNotNull(header.getValue()); + // Content-Length: nnn (OPTIONAL) + // System.out.println( "Response Content-Length=" + response.getFirstHeader( "Content-Length" ).getValue() ); + header = response.getFirstHeader("Content-Length"); + Assert.assertNotNull(header); + Assert.assertNotNull(header.getValue()); + // <?xml version="1.0"?> + // <entry xmlns="http://www.w3.org/2005/Atom"> + // <title>The Beach</title> (REQUIRED) + // <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> (REQUIRED) + // <updated>2005-10-07T17:17:08Z</updated> + // <author><name>Daffy</name></author> + // <summary type="text" /> (REQUIRED, OPTIONAL to populate + // <content type="image/png" src="http://media.example.org/the_beach.png"/> + // <link rel="edit-media" href="http://media.example.org/edit/the_beach.png" /> + // <link rel="edit" href="http://example.org/media/edit/the_beach.atom" /> + // </entry> + Document<Entry> document = abderaParser.parse(response.getEntity().getContent()); + 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 = "target/test-classes/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... + HttpPut put = new HttpPut(providerURI + "/" + mediaId); + put.addHeader("Content-Type", "image/jpg"); + put.addHeader("Title", "Title " + receiptName + ""); + put.addHeader("Slug", "Slug " + receiptName + ""); + put.setEntity(new FileEntity(input, "image/jpg")); + + // Get HTTP client + HttpClient httpclient = new HttpClientFactory().createHttpClient(); + try { + // Execute request + HttpResponse response = httpclient.execute(put); + response.getEntity().consumeContent(); + int result = response.getStatusLine().getStatusCode(); + // 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 = "target/test-classes/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... + HttpPut put = new HttpPut(providerURI + "/" + mediaId + "-bogus"); // Does not exist. + put.addHeader("Content-Type", "image/jpg"); + put.addHeader("Title", "Title " + receiptName + ""); + put.addHeader("Slug", "Slug " + receiptName + ""); + put.setEntity(new FileEntity(input, "image/jpg")); + + // Get HTTP client + HttpClient httpclient = new HttpClientFactory().createHttpClient(); + try { + // Execute request + HttpResponse response = httpclient.execute(put); + int result = response.getStatusLine().getStatusCode(); + // 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/Provider.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/Provider.java new file mode 100644 index 0000000000..e2759235b0 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/Provider.java @@ -0,0 +1,49 @@ +/* + * 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.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; + +/** + * + * @version $Rev$ $Date$ + */ +public class Provider { + + public static void main(String[] args) { + + String contribution = ContributionLocationHelper.getContributionLocation(Provider.class); + Node node = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Provider.composite", new Contribution("c1", contribution)); + System.out.println("Provider.composite ready..."); + + try { + System.in.read(); + } catch (IOException e) { + e.printStackTrace(); + } + + node.stop(); + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderEntryEntityTagsTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderEntryEntityTagsTestCase.java new file mode 100644 index 0000000000..c39bee65f6 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderEntryEntityTagsTestCase.java @@ -0,0 +1,432 @@ +/* + * 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.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.parser.Parser; +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.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * 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. + * + * @version $Rev$ $Date$ + */ +public class ProviderEntryEntityTagsTestCase { + public final static String providerURI = "http://localhost:8084/customer"; + + protected static Node scaProviderNode; + + 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"); + String contribution = ContributionLocationHelper.getContributionLocation(ProviderEntryEntityTagsTestCase.class); + + scaProviderNode = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Provider.composite", new Contribution("provider", contribution)); + scaProviderNode.start(); + + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + //System.out.println(">>>ProviderEntryEntityTagsTestCase.destroy"); + if (scaProviderNode != null) { + scaProviderNode.stop(); + } + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + 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 + // + // <?xml version="1.0" ?> + // <entry xmlns="http://www.w3.org/2005/Atom"> + // <title>Atom-Powered Robots Run Amok</title> + // <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + // <updated>2007-02-123T17:09:02Z</updated> + // <author><name>Captain Lansing</name></author> + // <content>It's something moving... solid metal</content> + // </entry> + + // 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 + // + // <?xml version="1.0" ?> + // <entry xmlns="http://www.w3.org/2005/Atom"> + // <title>Atom-Powered Robots Run Amok</title> + // <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + // <updated>2007-02-123T17:09:02Z</updated> + // <author><name>Captain Lansing</name></author> + // <content>It's something moving... solid metal</content> + // </entry> + + // 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"; + 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" + // + // <?xml version="1.0" ?> + // <entry xmlns="http://www.w3.org/2005/Atom"> + // <title>Atom-Powered Robots Run Amok</title> + // <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id> + // <updated>2007-02-24T16:34:06Z</updated> + // <author><name>Captain Lansing</name></author> + // <content>Update: it's a hoax!</content> + // </entry> + // 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"; + 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"; + 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"; + 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"; + 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"; + 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"; + 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"; + 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderFeedEntityTagsTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderFeedEntityTagsTestCase.java new file mode 100644 index 0000000000..ad5a179562 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderFeedEntityTagsTestCase.java @@ -0,0 +1,410 @@ +/* + * 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 junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.factory.Factory; +import org.apache.abdera.i18n.iri.IRI; +import org.apache.abdera.model.Collection; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.parser.Parser; +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.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * 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. + * + * @version $Rev$ $Date$ + */ +public class ProviderFeedEntityTagsTestCase { + public final static String providerURI = "http://localhost:8084/customer"; + + protected static Node scaProviderNode; + + 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"); + String contribution = ContributionLocationHelper.getContributionLocation(ProviderEntryEntityTagsTestCase.class); + + scaProviderNode = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/Provider.composite", new Contribution("provider", contribution)); + scaProviderNode.start(); + + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + //System.out.println(">>>ProviderFeedEntityTagsTestCase.destroy"); + if (scaProviderNode != null) { + scaProviderNode.stop(); + } + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + 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<Feed> doc = res.getDocument(); + String body = read( res.getInputStream() ); + // RFC 4287 requires non-null id, title, updated elements + Assert.assertTrue( -1 != body.indexOf( "</id>" )); + Assert.assertTrue( -1 != body.indexOf( "</title>" )); + Assert.assertTrue( -1 != body.indexOf( "</updated>" )); + + 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 200 - Feed provided since feed is unmodified since. + Assert.assertEquals(200, res.getStatus()); + Assert.assertEquals(ResponseType.SUCCESS, res.getType()); + } 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"; + 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 matches eTag + 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.testFeedModifiedGetIfUnModified"); + // Feed request with predicates + RequestOptions opts = new RequestOptions(); + final String contentType = "application/atom+xml"; + opts.setContentType(contentType); + opts.setHeader( "If-Unmodified-Since", dateFormat.format( previousSecond(lastModified) )); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 412 - Feed not provided since feed is modified since. + Assert.assertEquals(412, res.getStatus()); + } finally { + res.release(); + } + } + + @Test + public void testModifiedGetIfModified() throws Exception { + //System.out.println(">>>ProviderFeedEntityTagsTestCase.testFeedModifiedGetIfModified"); + // 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() )); + + ClientResponse res = client.get(providerURI, opts); + Assert.assertNotNull(res); + try { + // Should return 304 - Feed not provided since feed is unmodified. + Assert.assertEquals(304, res.getStatus()); + } finally { + res.release(); + } + + // Feed request with predicates + opts = new RequestOptions(); + opts.setContentType(contentType); + opts.setHeader( "If-Modified-Since", dateFormat.format( previousSecond(lastModified) )); + + 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(); + } + } + } + + /** + * Subtract one second from a date + * @param date with millisecond precision + * @return date with one second subtracted + */ + private Date previousSecond(Date date) { + return new Date(date.getTime() - 1000); + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderServiceDocumentTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderServiceDocumentTestCase.java new file mode 100644 index 0000000000..f94df3cff8 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/ProviderServiceDocumentTestCase.java @@ -0,0 +1,204 @@ +/* + * 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 junit.framework.Assert; + +import org.apache.abdera.Abdera; +import org.apache.abdera.model.Content; +import org.apache.abdera.model.Document; +import org.apache.abdera.model.Entry; +import org.apache.abdera.model.Feed; +import org.apache.abdera.model.Service; +import org.apache.abdera.parser.Parser; +import org.apache.abdera.protocol.Response.ResponseType; +import org.apache.abdera.protocol.client.AbderaClient; +import org.apache.abdera.protocol.client.ClientResponse; +import org.apache.tuscany.sca.binding.atom.collection.Collection; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * 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. + * + * @version $Rev$ $Date$ + */ +public class ProviderServiceDocumentTestCase extends AbstractProviderConsumerTestCase { + public final static String providerURI = "http://localhost:8084/customer"; + + protected static CustomerClient testService; + protected static Abdera abdera; + protected static AbderaClient client; + protected static Parser abderaParser; + + @BeforeClass + public static void init() throws Exception { + initTestEnvironment(AtomPostTestCase.class); + + testService = scaConsumerNode.getService(CustomerClient.class, "CustomerClient"); + + abdera = new Abdera(); + client = new AbderaClient(abdera); + abderaParser = Abdera.getNewParser(); + } + + @AfterClass + public static void destroy() throws Exception { + destroyTestEnvironment(); + } + + @Test + public void testPrelim() throws Exception { + Assert.assertNotNull(scaProviderNode); + Assert.assertNotNull(scaConsumerNode); + 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<Feed> doc = res.getDocument(); + String body = read( res.getInputStream() ); + // RFC 4287 requires non-null id, title, updated elements + Assert.assertTrue( -1 != body.indexOf( "</id>" )); + Assert.assertTrue( -1 != body.indexOf( "</title>" )); + Assert.assertTrue( -1 != body.indexOf( "</updated>" )); + } 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<Service> 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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/Aggregator.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/Aggregator.java new file mode 100644 index 0000000000..e10d442953 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/Aggregator.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.aggregator; + +import org.apache.tuscany.sca.data.collection.Collection; +import org.apache.tuscany.sca.data.collection.Item; +import org.oasisopen.sca.annotation.Remotable; + + +/** + * Aggreator interface + * + * @version $Rev$ $Date$ + */ +@Remotable +public interface Aggregator extends Collection<String, Item>{ + +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/AggregatorImpl.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/AggregatorImpl.java new file mode 100644 index 0000000000..781a670f57 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/AggregatorImpl.java @@ -0,0 +1,135 @@ +/* + * 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.aggregator; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.tuscany.sca.data.collection.Collection; +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.data.collection.Item; +import org.apache.tuscany.sca.data.collection.NotFoundException; +import org.oasisopen.sca.annotation.Property; +import org.oasisopen.sca.annotation.Reference; + + +/** + * Aggregator impl + * + * @version $Rev$ $Date$ + */ +public class AggregatorImpl implements Aggregator { + + @Reference(required = false) + public Collection<String, Item> atomFeed1; + + @Reference(required = false) + public Collection<String, Item> atomFeed2; + + @Reference(required = false) + public Sort sort; + + @Property + public String feedTitle = "Aggregated Feed"; + @Property + public String feedDescription = "Anonymous Aggregated Feed"; + @Property + public String feedAuthor = "anonymous"; + + public Item get(String id) { + + try { + if (atomFeed1.get(id) != null) + return atomFeed1.get(id); + } catch (NotFoundException ex) { + ex.printStackTrace(); + } + + try { + if (atomFeed2.get(id) != null) + return atomFeed2.get(id); + } catch (NotFoundException ex) { + ex.printStackTrace(); + } + + return null; + + } + + @SuppressWarnings("unchecked") + public Entry<String, Item>[] getAll() { + + // Aggregate entries from atomFeed1, atomFeed2, rssFeed1 and rssFeed2 + List<Entry> entries = new ArrayList<Entry>(); + if (atomFeed1 != null) { + try { + for (Entry entry : atomFeed1.getAll()) { + entries.add(entry); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + if (atomFeed2 != null) { + try { + for (Entry entry : atomFeed2.getAll()) { + entries.add(entry); + } + } catch (Exception e) { + } + } + + // Sort entries by published date + if (sort != null) { + entries = sort.sort(entries); + } + + return entries.toArray(new Entry[entries.size()]); + + } + + public String post(String key, Item entry) { + return null; + } + + public void put(String id, Item entry) { + } + + @SuppressWarnings("unchecked") + public Entry<String, Item>[] query(String queryString) { + ArrayList<Entry> entries = new ArrayList<Entry>(); + + Entry<String, Item>[] allFeed = getAll(); + if (queryString.startsWith("title=")) { + String title = queryString.substring(6); + + for (Entry<String, Item> entry : allFeed) { + if (entry.getData().getTitle().contains(title)) { + entries.add(entry); + } + } + } + return entries.toArray(new Entry[entries.size()]); + } + + public void delete(String id) { + + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/AggregatorTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/AggregatorTestCase.java new file mode 100644 index 0000000000..45d4c94768 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/AggregatorTestCase.java @@ -0,0 +1,82 @@ +/* + * 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.aggregator; + +import java.net.Socket; + +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.data.collection.Item; +import org.apache.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Agregattor test case + * + * @version $Rev$ $Date$ + */ +public class AggregatorTestCase { + private static Node node; + + private static Aggregator aggregatorService; + + @BeforeClass + public static void init() throws Exception { + try { + String contribution = ContributionLocationHelper.getContributionLocation(AggregatorTestCase.class); + node = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/aggregator/FeedAggregator.composite", new Contribution("test", contribution)); + node.start(); + + aggregatorService = node.getService(Aggregator.class, "AtomAggregator/Aggregator"); + Assert.assertNotNull(aggregatorService); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + if(node != null) { + node.stop(); + } + } + + @Test + public void testPing() throws Exception { + new Socket("127.0.0.1", 8085); + } + + @Test + public void testAggregator() throws Exception { + Entry<String, Item>[] entries = aggregatorService.getAll(); + + Assert.assertNotNull(entries); + Assert.assertTrue(entries.length > 0); + + for(int pos = 0; pos < entries.length; pos++) { + System.out.println(">>> Entry[" + pos + "] - " + entries[pos].getData().getTitle()); + } + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/Sort.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/Sort.java new file mode 100644 index 0000000000..28ea465968 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/Sort.java @@ -0,0 +1,39 @@ +/*
+ * 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.aggregator;
+
+import java.util.List;
+
+import org.apache.tuscany.sca.data.collection.Entry;
+
+/**
+ * The Sort service business interface.
+ *
+ * @version $Rev$ $Date$
+ */
+public interface Sort {
+
+ /**
+ * Sort feed entries by published date.
+ * @param entries
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ List<Entry> sort(List<Entry> entries);
+}
diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/SortImpl.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/SortImpl.java new file mode 100644 index 0000000000..022b1269fd --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/aggregator/SortImpl.java @@ -0,0 +1,62 @@ +/*
+ * 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.aggregator;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.tuscany.sca.data.collection.Entry;
+import org.apache.tuscany.sca.data.collection.Item;
+import org.oasisopen.sca.annotation.Property;
+
+/**
+ * Implementation of a Feed Sort service component.
+ *
+ * @version $Rev$ $Date$
+ */
+public class SortImpl implements Sort {
+
+ @Property
+ public boolean newFirst = true;
+
+ @SuppressWarnings("unchecked")
+ public List<Entry> sort(List<Entry> entries) {
+ Entry[] entriesArray = new Entry[entries.size()];
+ entriesArray = (Entry<String,Item>[])entries.toArray(entriesArray);
+ Arrays.sort(entriesArray, new Comparator() {
+ public int compare(final Object xObj, final Object yObj) {
+ Entry x = (Entry) xObj;
+ Entry y = (Entry) yObj;
+ Item xItem = (Item) x.getData();
+ Item yItem = (Item) y.getData();
+ Date xDate = xItem.getDate();
+ Date yDate = yItem.getDate();
+ if (xDate == null)
+ return -1;
+ if (newFirst)
+ return yDate.compareTo(xDate);
+ else
+ return xDate.compareTo(yDate);
+ }
+ });
+ return Arrays.asList(entriesArray);
+ }
+}
diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/Headline.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/Headline.java new file mode 100644 index 0000000000..95152af73d --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/Headline.java @@ -0,0 +1,54 @@ +/* + * 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.news; + +import java.io.Serializable; + +public class Headline implements Serializable { + private static final long serialVersionUID = 2516853242307046775L; + + private String text; + private String source; + + public Headline() { + + } + + public Headline(String text, String source) { + this.text = text; + this.source = text; + } + + public void setText(String text) { + this.text = text; + } + + public String getText() { + return text; + } + + public void setSoure(String source) { + this.source = source; + } + + public String getSource() { + return source; + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsService.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsService.java new file mode 100644 index 0000000000..ab97f4e340 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsService.java @@ -0,0 +1,28 @@ +/* + * 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.news; + +import org.apache.tuscany.sca.data.collection.Collection; +import org.oasisopen.sca.annotation.Remotable; + +@Remotable +public interface NewsService extends Collection<String, Headline>{ + +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsServiceImpl.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsServiceImpl.java new file mode 100644 index 0000000000..7e40bb797d --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsServiceImpl.java @@ -0,0 +1,90 @@ +/* + * 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.news; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.data.collection.NotFoundException; +import org.oasisopen.sca.annotation.Init; +import org.oasisopen.sca.annotation.Service; + +@Service(NewsService.class) +public class NewsServiceImpl implements NewsService { + private Map<String, Headline> headlines = new HashMap<String,Headline>(); + + @Init + public void init() { + Headline headline; + + headline = new Headline(); + headline.setSoure("http://www.domain.com/source1"); + headline.setText("headline text 1111"); + + headlines.put("h1", headline); + + + headline = new Headline(); + headline.setSoure("http://www.domain.com/source2"); + headline.setText("headline text 2222"); + + headlines.put("h2", headline); + } + + public Entry<String, Headline>[] getAll() { + Entry<String, Headline>[] entries = new Entry[headlines.size()]; + int i = 0; + for (Map.Entry<String, Headline> e: headlines.entrySet()) { + entries[i++] = new Entry<String, Headline>(e.getKey(), e.getValue()); + } + return entries; + } + + public Headline get(String key) throws NotFoundException { + Headline item = headlines.get(key); + if (item == null) { + throw new NotFoundException(key); + } else { + return item; + } + } + + public String post(String key, Headline item) { + // TODO Auto-generated method stub + return null; + } + + public void put(String key, Headline item) throws NotFoundException { + // TODO Auto-generated method stub + + } + + public Entry<String, Headline>[] query(String queryString) { + // TODO Auto-generated method stub + return null; + } + + public void delete(String key) throws NotFoundException { + // TODO Auto-generated method stub + + } + +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsServiceTestCase.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsServiceTestCase.java new file mode 100644 index 0000000000..b19f477912 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/news/NewsServiceTestCase.java @@ -0,0 +1,77 @@ +/* + * 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.news; + +import java.net.Socket; + +import org.apache.tuscany.sca.binding.atom.aggregator.AggregatorTestCase; +import org.apache.tuscany.sca.data.collection.Entry; +import org.apache.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class NewsServiceTestCase { + private static Node node; + private static NewsService newsService; + + @BeforeClass + public static void init() throws Exception { + try { + String contribution = ContributionLocationHelper.getContributionLocation(AggregatorTestCase.class); + node = NodeFactory.newInstance().createNode("org/apache/tuscany/sca/binding/atom/news/news.composite", new Contribution("test", contribution)); + node.start(); + + newsService = node.getService(NewsService.class, "NewsService"); + Assert.assertNotNull(newsService); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + if(node != null) { + node.stop(); + } + } + + @Test + public void testPing() throws Exception { + new Socket("127.0.0.1", 8085); + // System.in.read(); + } + + @Test + public void testNewsService() throws Exception { + Entry<String, Headline>[] entries = newsService.getAll(); + + Assert.assertNotNull(entries); + Assert.assertTrue(entries.length > 0); + + for(int pos = 0; pos < entries.length; pos++) { + System.out.println(">>> Entry[" + pos + "] - " + entries[pos].getData().getText()); + } + } +} diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/util/AtomTestCaseUtils.java b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/util/AtomTestCaseUtils.java new file mode 100644 index 0000000000..1afad216e3 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/java/org/apache/tuscany/sca/binding/atom/util/AtomTestCaseUtils.java @@ -0,0 +1,98 @@ +/* + * 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.util; + +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. + * + * @version $Rev$ $Date$ + */ +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/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/ReceiptToms.gif b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/ReceiptToms.gif Binary files differnew file mode 100644 index 0000000000..bfeee9b2f4 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/ReceiptToms.gif diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/ReceiptValue.jpg b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/ReceiptValue.jpg Binary files differnew file mode 100644 index 0000000000..584f39ea8d --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/ReceiptValue.jpg diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/Consumer.composite b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/Consumer.composite new file mode 100644 index 0000000000..43d8a7feda --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/Consumer.composite @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://customer" + name="Consumer"> + + <component name="CustomerClient"> + <implementation.java class="org.apache.tuscany.sca.binding.atom.CustomerClientImpl"/> + <reference name="customerCollection"> + <tuscany:binding.atom uri="http://localhost:8084/customer"/> + </reference> + </component> + +</composite> diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/Provider.composite b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/Provider.composite new file mode 100644 index 0000000000..819e19a801 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/Provider.composite @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace = "http://customer" + name="Provider">
+
+ <!-- service name="customer" promote="CustomerCollection">
+ <tuscany:binding.atom uri = "http://localhost:8084/customer"/>
+ </service-->
+
+ <component name="CustomerCollection">
+ <implementation.java class="org.apache.tuscany.sca.binding.atom.CustomerCollectionImpl"/>
+ <service name="Collection">
+ <tuscany:binding.atom uri = "http://localhost:8084/customer"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/ProviderNonCollection.composite b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/ProviderNonCollection.composite new file mode 100644 index 0000000000..580e0ac324 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/ProviderNonCollection.composite @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace = "http://customer" + name="ProviderNonCollection">
+
+ <!-- service name="customer" promote="CustomerCollection">
+ <tuscany:binding.atom uri = "http://localhost:8084/customer"
+ title="Atom binding Non Collection" description="Feed used for unit testing"/>
+ </service-->
+
+ <component name="CustomerCollection">
+ <implementation.java class="org.apache.tuscany.sca.binding.atom.CustomerNonCollectionImpl"/>
+ <service name="CustomerNonCollectionImpl">
+ <tuscany:binding.atom uri = "http://localhost:8084/customer"
+ title="Atom binding Non Collection" description="Feed used for unit testing"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite new file mode 100644 index 0000000000..bac14b5b8b --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/ReceiptProvider.composite @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1"
+ targetNamespace = "http://receipt" + name="ReceiptProvider">
+
+ <!-- service name="receipt" promote="MediaCollection">
+ <tuscany:binding.atom uri = "http://localhost:8084/receipt"/>
+ </service-->
+
+ <component name="MediaCollection">
+ <implementation.java class="org.apache.tuscany.sca.binding.atom.MediaCollectionImpl"/>
+ <service name="MediaCollection">
+ <tuscany:binding.atom uri = "http://localhost:8084/receipt"/>
+ </service>
+ </component>
+
+</composite>
diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/aggregator/FeedAggregator.composite b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/aggregator/FeedAggregator.composite new file mode 100644 index 0000000000..63085b3ea6 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/aggregator/FeedAggregator.composite @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://aggregator" + name="FeedAggregator"> + + <!-- service name="atomSample" promote="AtomAggregator"> + <tuscany:binding.atom uri="http://localhost:8085/atomAggregator"/> + </service--> + + <component name="AtomAggregator"> + <implementation.java class="org.apache.tuscany.sca.binding.atom.aggregator.AggregatorImpl"/> + <service name="Aggregator"> + <tuscany:binding.atom uri="http://localhost:8085/atomAggregator"/> + </service> + <reference name="sort" target="Sort"/> + <reference name="atomFeed1"> + <tuscany:binding.atom uri="http://www.ibm.com/developerworks/blogs/rss/lresende?flavor=atomdw"/> + </reference> + <reference name="atomFeed2"> + <tuscany:binding.atom uri="https://blogs.apache.org/foundation/feed/entries/atom"/> + </reference> + <property name="feedTitle">Atom Aggregator Sample</property> + <property name="feedDescription">Anonymous Aggregated Feed</property> + <property name="feedAuthor">anonymous</property> + </component> + + <component name="Sort"> + <implementation.java class="org.apache.tuscany.sca.binding.atom.aggregator.SortImpl"/> + <property name="newFirst">true</property> + </component> + +</composite> diff --git a/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/news/news.composite b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/news/news.composite new file mode 100644 index 0000000000..bc74509dc5 --- /dev/null +++ b/sandbox/sebastien/java/vhost/modules/binding-atom-runtime/src/test/resources/org/apache/tuscany/sca/binding/atom/news/news.composite @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://news" + name="newsService"> + + <component name="NewsService"> + <implementation.java class="org.apache.tuscany.sca.binding.atom.news.NewsServiceImpl"/> + <service name="NewsService"> + <tuscany:binding.atom uri="http://localhost:8085/news"/> + </service> + </component> + +</composite> |