You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:36:46 UTC
[sling-org-apache-sling-hapi] 01/24: SLING-5136 - HApi microdata
client, contributed by Andrei Dulvac, thanks!
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.hapi.client-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-hapi.git
commit 01c66077c21f293a2fbb37ec19c194c174e18e30
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Tue Oct 13 08:56:08 2015 +0000
SLING-5136 - HApi microdata client, contributed by Andrei Dulvac, thanks!
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/hapi/client@1708306 13f79535-47bb-0310-9956-ffa450edef68
---
.gitignore | 15 +
pom.xml | 97 +++++
.../apache/sling/hapi/client/ClientException.java | 32 ++
.../org/apache/sling/hapi/client/Document.java | 29 ++
.../org/apache/sling/hapi/client/HtmlClient.java | 72 ++++
.../java/org/apache/sling/hapi/client/Item.java | 82 +++++
.../java/org/apache/sling/hapi/client/Items.java | 35 ++
.../apache/sling/hapi/client/forms/FormValues.java | 116 ++++++
.../org/apache/sling/hapi/client/forms/Vals.java | 60 ++++
.../hapi/client/impl/AbstractHtmlClientImpl.java | 192 ++++++++++
.../hapi/client/microdata/MicrodataDocument.java | 399 +++++++++++++++++++++
.../hapi/client/microdata/MicrodataHtmlClient.java | 44 +++
src/test/java/FormTest.java | 114 ++++++
src/test/java/GetPostTest.java | 111 ++++++
src/test/java/ItemsTest.java | 121 +++++++
src/test/resources/items.html | 24 ++
src/test/resources/items_forms.html | 28 ++
src/test/resources/items_links.html | 20 ++
18 files changed, 1591 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..518c151
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+target/
+.project
+.classpath
+.settings/
+/sling/
+derby.log
+*.iml
+*.ipr
+*.iws
+coverage.em
+coverage.ec
+coverage/
+.DS_Store
+.idea/
+bundles/extensions/models/integration-tests/sling
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..9d95953
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+~ Licensed to the Apache Software Foundation (ASF) under one
+~ or more contributor license agreements. See the NOTICE file
+~ distributed with this work for additional information
+~ regarding copyright ownership. The ASF licenses this file
+~ to you under the Apache License, Version 2.0 (the
+~ "License"); you may not use this file except in compliance
+~ with the License. You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing,
+~ software distributed under the License is distributed on an
+~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+~ KIND, either express or implied. See the License for the
+~ specific language governing permissions and limitations
+~ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>sling</artifactId>
+ <version>20</version>
+ <relativePath/>
+ </parent>
+
+ <artifactId>org.apache.sling.hapi.client</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+
+ <name>HApi - Sling Hypermedia API client-side tools</name>
+ <description>Sling client-side tools for HApi</description>
+
+ <properties>
+ <sling.java.version>6</sling.java.version>
+ </properties>
+
+ <build>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>4.5</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.4</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.3.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jsoup</groupId>
+ <artifactId>jsoup</artifactId>
+ <version>1.8.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.12</version>
+ </dependency>
+ <!-- test scope -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.1</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>1.7.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/src/main/java/org/apache/sling/hapi/client/ClientException.java b/src/main/java/org/apache/sling/hapi/client/ClientException.java
new file mode 100644
index 0000000..9bda81e
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/ClientException.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.hapi.client;
+
+/**
+ * An HTTP client exception
+ */
+public class ClientException extends Exception {
+ public ClientException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ClientException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/Document.java b/src/main/java/org/apache/sling/hapi/client/Document.java
new file mode 100644
index 0000000..9344e5d
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/Document.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.hapi.client;
+
+/**
+ * An HTML document representation
+ */
+public interface Document {
+ Items link(String rel) throws ClientException;
+ Items form(String rel) throws ClientException;
+ Items item(String rel) throws ClientException;
+ Items items() throws ClientException;
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/HtmlClient.java b/src/main/java/org/apache/sling/hapi/client/HtmlClient.java
new file mode 100644
index 0000000..078f46b
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/HtmlClient.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.hapi.client;
+
+import org.apache.http.HttpEntity;
+
+public interface HtmlClient {
+
+ /**
+ * Enters a url and return a Document
+ *
+ * @param url
+ * @return
+ * @throws ClientException
+ */
+ <T extends Document> T enter(String url) throws ClientException;
+
+ /**
+ * Performs a GET request and returns a Document
+ *
+ * @param url the URL String to perform an HTTP GET on
+ * @return
+ * @throws ClientException
+ */
+ <T extends Document> T get(String url) throws ClientException;
+
+
+ /**
+ * Performs a POST request.
+ *
+ * @param url the URL String to perform an HTTP post on
+ * @param entity data to post
+ * @return
+ * @throws ClientException
+ */
+ <T extends Document> T post(String url, HttpEntity entity) throws ClientException;
+
+ /**
+ * Performs a DELETE request and returns a Document
+ *
+ * @param url the URL String to perform an HTTP DELETE on
+ * @return
+ * @throws ClientException
+ */
+ <T extends Document> T delete(String url) throws ClientException;
+
+ /**
+ * Method to create a new Document representation from an HTML content String
+ * @param html
+ * @param <T>
+ * @return
+ */
+ <T extends Document> T newDocument(String html);
+
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/Item.java b/src/main/java/org/apache/sling/hapi/client/Item.java
new file mode 100644
index 0000000..f1d8865
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/Item.java
@@ -0,0 +1,82 @@
+package org.apache.sling.hapi.client;
+
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+import org.apache.http.NameValuePair;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
+ */
+public interface Item {
+ /**
+ * Returns the property of the item having the given name.
+ */
+ Items prop(String name) throws ClientException;
+
+ /**
+ * Return a List of all the properties of this item
+ */
+ Set<String> props() throws ClientException;
+
+ /**
+ * Returns the child links that have the given relation
+ */
+ Items link(String rel) throws ClientException;
+
+ /**
+ * Returns the child forms that have the given relation
+ */
+ Items form(String rel) throws ClientException;
+
+ /**
+ * Returns the text value of the property.
+ */
+ String text() throws ClientException;
+
+ /**
+ * Returns he boolean value of the property
+ */
+ boolean bool() throws ClientException;
+
+ int number() throws ClientException;
+
+ /**
+ * Returns the href value of the property. This only makes sense for hyperlinks (<a> or <link>).
+ */
+ String href();
+
+ /**
+ * Returns the <i>src</i> value of the property.
+ */
+ String src();
+
+ /**
+ * Follow a hyperlink and get a new {@see Document} representation
+ */
+ Document follow() throws ClientException;
+
+ /**
+ * Submits this form item and returns a new {@see Document} representation
+ */
+ Document submit(Iterable<NameValuePair> data) throws ClientException;
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/Items.java b/src/main/java/org/apache/sling/hapi/client/Items.java
new file mode 100644
index 0000000..4ccbc4d
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/Items.java
@@ -0,0 +1,35 @@
+package org.apache.sling.hapi.client;
+
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+/**
+ * A composite object representing items in a {@see Document}
+ */
+public interface Items extends Iterable<Item>, Item {
+ /**
+ * Returns the item at one index
+ */
+ Item at(int index) throws IndexOutOfBoundsException;
+
+ /**
+ * Returns the amount of items contained in this collection.
+ */
+ int length();
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/forms/FormValues.java b/src/main/java/org/apache/sling/hapi/client/forms/FormValues.java
new file mode 100644
index 0000000..9a12cb1
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/forms/FormValues.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.hapi.client.forms;
+
+import java.io.UnsupportedEncodingException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.utils.URLEncodedUtils;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.message.BasicNameValuePair;
+import org.jsoup.nodes.Element;
+
+/**
+ *
+ */
+public class FormValues {
+ private Element form;
+
+ private Iterable<NameValuePair> values;
+
+ private Vals list = new Vals();
+
+ public FormValues(Element form, Iterable<NameValuePair> values) {
+ this.form = form;
+ this.values = values;
+
+ build();
+ resolve();
+ }
+
+ /**
+ * @return
+ * {@see http://www.w3.org/TR/html5/forms.html#constructing-the-form-data-set}
+ */
+ private FormValues build() {
+ for (Element input : form.select("button, input, select, textarea")) {
+ String type = input.attr("type");
+
+ if (input.hasAttr("disabled")) continue;
+ if (input.tagName().equalsIgnoreCase("button") && !type.equals("submit")) continue;
+ if (input.tagName().equalsIgnoreCase("input") && (type.equals("button") || type.equals("reset"))) continue;
+ if (type.equals("checkbox") && input.hasAttr("checked")) continue;
+ if (type.equals("radio") && input.hasAttr("checked")) continue;
+ if (!type.equals("image") && input.attr("name").length() == 0) continue;
+ if (input.parents().is("datalist")) continue;
+
+ if (type.equals("image") || type.equals("file")) continue; // don't support files for now
+ String name = input.attr("name");
+
+ if (input.tagName().equalsIgnoreCase("select")) {
+ for (Element o : input.select("option[selected]")) {
+ if (o.hasAttr("disabled")) continue;
+ list.add(name, new BasicNameValuePair(name, o.val()));
+ }
+ } else if (type.equals("checkbox") || type.equals("radio")) {
+ String value = input.hasAttr("value") ? input.val() : "on";
+ list.add(name, new BasicNameValuePair(name, value));
+ } else {
+ list.add(name, new BasicNameValuePair(name, input.val()));
+ }
+ }
+ return this;
+ }
+
+ private FormValues resolve() {
+ for (NameValuePair o : values) {
+ if (list.has(o.getName())) {
+ list.set(o);
+ } else {
+ // for now just set the value even if the form doesn't have a submittable input for the name.
+ // this is to support custom field that generate input dynamically
+ list.set(o);
+ }
+ }
+ return this;
+ }
+
+ public String toString() {
+ return URLEncodedUtils.format(list.flatten(), "UTF-8");
+ }
+
+ public HttpEntity toUrlEncodedEntity() {
+ try {
+ return new UrlEncodedFormEntity(list.flatten(), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public HttpEntity toMultipartEntity() {
+ MultipartEntityBuilder eb = MultipartEntityBuilder.create();
+ for (NameValuePair p : list.flatten()) {
+ eb.addTextBody(p.getName(), p.getValue());
+ }
+ return eb.build();
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/forms/Vals.java b/src/main/java/org/apache/sling/hapi/client/forms/Vals.java
new file mode 100644
index 0000000..9e728a6
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/forms/Vals.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.hapi.client.forms;
+
+import org.apache.http.NameValuePair;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Vals {
+ private Map<String, List<NameValuePair>> data = new LinkedHashMap<String, List<NameValuePair>>();
+
+ public void add(String name, NameValuePair pair) {
+ if (data.containsKey(name)) {
+ data.get(name).add(pair);
+ } else {
+ ArrayList<NameValuePair> list = new ArrayList<NameValuePair>();
+ list.add(pair);
+ data.put(name, list);
+ }
+ }
+
+ public boolean has(String name) {
+ return data.containsKey(name);
+ }
+
+ public void set(NameValuePair pair) {
+ ArrayList<NameValuePair> list = new ArrayList<NameValuePair>();
+ list.add(pair);
+ data.put(pair.getName(), list);
+ }
+
+ public List<? extends NameValuePair> flatten() {
+ List<NameValuePair> result = new ArrayList<NameValuePair>();
+
+ for (List<NameValuePair> c : data.values()) {
+ result.addAll(c);
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/impl/AbstractHtmlClientImpl.java b/src/main/java/org/apache/sling/hapi/client/impl/AbstractHtmlClientImpl.java
new file mode 100644
index 0000000..5c3444a
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/impl/AbstractHtmlClientImpl.java
@@ -0,0 +1,192 @@
+/*******************************************************************************
+ * 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.sling.hapi.client.impl;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+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.HttpUriRequest;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.apache.http.params.HttpParams;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.util.EntityUtils;
+import org.apache.sling.hapi.client.ClientException;
+import org.apache.sling.hapi.client.Document;
+import org.apache.sling.hapi.client.HtmlClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public abstract class AbstractHtmlClientImpl implements HttpClient, HtmlClient {
+ protected static final Logger LOG = LoggerFactory.getLogger(AbstractHtmlClientImpl.class);
+ protected CloseableHttpClient client;
+ protected URI baseUrl;
+
+ public AbstractHtmlClientImpl(CloseableHttpClient client, String baseUrl) throws URISyntaxException {
+ this.client = client;
+ this.baseUrl = new URI(baseUrl);
+ }
+
+ public AbstractHtmlClientImpl(String baseUrl) throws URISyntaxException {
+ this(HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build(), baseUrl);
+ }
+
+ public AbstractHtmlClientImpl(String baseUrl, String user, String password) throws URISyntaxException {
+ this.baseUrl = new URI(baseUrl);
+ HttpHost targetHost = URIUtils.extractHost(this.baseUrl);
+ BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
+ credsProvider.setCredentials(
+ new AuthScope(targetHost.getHostName(), targetHost.getPort()),
+ new UsernamePasswordCredentials(user, password));
+ this.client = HttpClientBuilder.create()
+ .setDefaultCredentialsProvider(credsProvider)
+ .setRedirectStrategy(new LaxRedirectStrategy())
+ .build();
+ }
+
+
+ @Override
+ public <T extends Document> T enter(String url) throws ClientException {
+ return get(url);
+ }
+
+ @Override
+ public <T extends Document> T get(String url) throws ClientException {
+ try {
+ URI absoluteUri = absoluteUri(url);
+ LOG.info("GET " + absoluteUri);
+ HttpResponse response = this.execute(new HttpGet(absoluteUri));
+ return newDocument(EntityUtils.toString(response.getEntity()));
+
+ } catch (URISyntaxException e) {
+ throw new ClientException("Invalid get url " + url, e);
+ } catch (Exception e) {
+ throw new ClientException("Could not execute GET request", e);
+ }
+ }
+
+ @Override
+ public <T extends Document> T post(String url, HttpEntity entity) throws ClientException {
+ try {
+ URI absoluteUri = absoluteUri(url);
+ LOG.info("POST " + absoluteUri);
+ HttpPost post = new HttpPost(absoluteUri);
+ post.setEntity(entity);
+ HttpResponse response = this.execute(post);
+ return newDocument(EntityUtils.toString(response.getEntity()));
+ } catch (URISyntaxException e) {
+ throw new ClientException("Invalid post url " + url, e);
+ } catch (Exception e) {
+ throw new ClientException("Could not execute POST request", e);
+ }
+ }
+
+ @Override
+ public <T extends Document> T delete(String url) throws ClientException {
+ try {
+ URI absoluteUri = absoluteUri(url);
+ LOG.info("DELETE " + absoluteUri);
+ HttpResponse response = this.execute(new HttpDelete(absoluteUri));
+ return newDocument(response.getEntity().toString());
+ } catch (URISyntaxException e) {
+ throw new ClientException("Invalid post url " + url, e);
+ } catch (Exception e) {
+ throw new ClientException("Could not execute DELETE request", e);
+ }
+ }
+
+ @Override
+ public abstract <T extends Document> T newDocument(String html);
+
+
+ private URI absoluteUri(String url) throws URISyntaxException {
+ URI getUrl = new URI(url);
+ return this.baseUrl.resolve(getUrl);
+ }
+
+ /*
+ Overrides of HttpClient methods
+ */
+ @Override
+ public HttpParams getParams() {
+ return client.getParams();
+ }
+
+ @Override
+ public ClientConnectionManager getConnectionManager() {
+ return client.getConnectionManager();
+ }
+
+ @Override
+ public HttpResponse execute(HttpUriRequest httpUriRequest) throws IOException, ClientProtocolException {
+ return client.execute(httpUriRequest);
+ }
+
+ @Override
+ public HttpResponse execute(HttpUriRequest httpUriRequest, HttpContext httpContext) throws IOException, ClientProtocolException {
+ return client.execute(httpUriRequest, httpContext);
+ }
+
+ @Override
+ public HttpResponse execute(HttpHost httpHost, HttpRequest httpRequest) throws IOException, ClientProtocolException {
+ return client.execute(httpHost, httpRequest);
+ }
+
+ @Override
+ public HttpResponse execute(HttpHost httpHost, HttpRequest httpRequest, HttpContext httpContext) throws IOException, ClientProtocolException {
+ return client.execute(httpHost, httpRequest, httpContext);
+ }
+
+ @Override
+ public <T> T execute(HttpUriRequest httpUriRequest, ResponseHandler<? extends T> responseHandler) throws IOException, ClientProtocolException {
+ return client.execute(httpUriRequest, responseHandler);
+ }
+
+ @Override
+ public <T> T execute(HttpUriRequest httpUriRequest, ResponseHandler<? extends T> responseHandler, HttpContext httpContext) throws IOException, ClientProtocolException {
+ return client.execute(httpUriRequest, responseHandler, httpContext);
+ }
+
+ @Override
+ public <T> T execute(HttpHost httpHost, HttpRequest httpRequest, ResponseHandler<? extends T> responseHandler) throws IOException, ClientProtocolException {
+ return client.execute(httpHost, httpRequest, responseHandler);
+ }
+
+ @Override
+ public <T> T execute(HttpHost httpHost, HttpRequest httpRequest, ResponseHandler<? extends T> responseHandler, HttpContext httpContext) throws IOException, ClientProtocolException {
+ return client.execute(httpHost, httpRequest, responseHandler, httpContext);
+ }
+}
diff --git a/src/main/java/org/apache/sling/hapi/client/microdata/MicrodataDocument.java b/src/main/java/org/apache/sling/hapi/client/microdata/MicrodataDocument.java
new file mode 100644
index 0000000..2f6ee91
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/microdata/MicrodataDocument.java
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.hapi.client.microdata;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.sling.hapi.client.*;
+import org.apache.sling.hapi.client.forms.FormValues;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+public class MicrodataDocument implements Document {
+ private org.jsoup.nodes.Document jsoupDocument;
+ private HtmlClient client;
+
+ public MicrodataDocument(String html, HtmlClient client, String baseUrl) {
+ this.jsoupDocument = Jsoup.parse(html, baseUrl);
+ this.client = client;
+ }
+
+
+ @Override
+ public Items link(String rel) throws ClientException {
+ Item me = toItem(jsoupDocument);
+ return me.link(rel);
+ }
+
+ @Override
+ public Items form(String rel) throws ClientException {
+ Item me = toItem(jsoupDocument);
+ return me.form(rel);
+ }
+
+ @Override
+ public Items item(String rel) {
+ List<Item> items = new ArrayList<Item>();
+
+ for (Item i : items()) {
+ if (((ItemImpl) i).el.hasClass(rel)) {
+ items.add(i);
+ }
+ }
+
+ return new ItemsImpl(items);
+ }
+
+ @Override
+ public Items items() {
+ return new ItemsImpl(selectItems(jsoupDocument, new ArrayList<Item>()));
+ }
+
+ @Override
+ public String toString() {
+ return this.jsoupDocument.toString();
+ }
+
+ private String toClass(String name) {
+ return "." + name;
+ }
+
+ private List<Item> toItems(Elements el) {
+ List<Item> items = new ArrayList<Item>();
+
+ for (Element e : el) {
+ items.add(toItem(e));
+ }
+
+ return items;
+ }
+
+ private Item toItem(Element el) {
+ return new ItemImpl(el, this);
+ }
+
+ private List<Item> selectItems(Element e, List<Item> items) {
+ if (e.hasAttr("itemscope") && !e.hasAttr("itemprop")) {
+ items.add(new ItemImpl(e, this));
+ return items;
+ }
+
+ for (Element c : e.children()) {
+ selectItems(c, items);
+ }
+
+ return items;
+ }
+
+
+ private class ItemImpl implements Item {
+
+ private Element el;
+ private MicrodataDocument document;
+ private ItemImpl followed;
+
+ public ItemImpl(Element element, MicrodataDocument document) {
+ if (element == null) throw new NullPointerException("element is mandatory");
+ this.el = element;
+ this.document = document;
+ }
+
+ @Override
+ public Items prop(String name) throws ClientException {
+ return new ItemsImpl(selectProps(getProxy().el, name, new ArrayList<Item>()));
+ }
+
+ @Override
+ public Set<String> props() throws ClientException {
+ return selectAllPropNames(getProxy().el, new HashSet<String>());
+ }
+
+ @Override
+ public Items link(String rel) throws ClientException {
+ return new ItemsImpl(toItems(getProxy().el.select("link[rel=" + rel + "], a[rel=" + rel + "]")));
+ }
+
+ @Override
+ public Items form(String rel) throws ClientException {
+ return new ItemsImpl(toItems(getProxy().el.select("form[data-rel=" + rel + "]")));
+ }
+
+
+ /* Private methods */
+
+ private List<Item> selectProps(Element e, String name, List<Item> items) {
+ for (Element c : e.children()) {
+ if (c.hasAttr("itemprop")) {
+ if (c.attr("itemprop").equals(name)) {
+ items.add(new ItemImpl(c, document));
+ }
+
+ if (c.hasAttr("itemscope")) {
+ continue;
+ }
+ }
+
+ selectProps(c, name, items);
+ }
+
+ return items;
+ }
+
+ private Set<String> selectAllPropNames(Element e, Set<String> items) {
+ for (Element c : e.children()) {
+ if (c.hasAttr("itemprop")) {
+ items.add(c.attr("itemprop"));
+ }
+
+ if (c.hasAttr("itemscope")) {
+ continue;
+ }
+
+ selectAllPropNames(c, items);
+ }
+ return items;
+ }
+
+ private ItemImpl getProxy() throws ClientException {
+ if (el.tagName().equalsIgnoreCase("a") && el.hasAttr("href")) {
+ if (followed == null) {
+ followed = follow(el, document);
+ }
+ return followed;
+ }
+
+ return this;
+ }
+
+ private ItemImpl follow(Element el, MicrodataDocument doc) throws ClientException {
+ if (el.hasAttr("itemscope")) {
+ return new ItemImpl(el, doc);
+ }
+
+ if (el.tagName().equalsIgnoreCase("a") && el.hasAttr("href")) {
+ String href = el.attr("href");
+
+ if (href.startsWith("#")) {
+ Element first = el.ownerDocument().select(href).first();
+ return first == null ? null : follow(first, doc);
+ }
+
+ String absHref = el.attr("abs:href");
+ MicrodataDocument d = (MicrodataDocument) doc.client.get(absHref);
+
+ try {
+ URI uri = new URI(absHref);
+
+ String fragment = uri.getRawFragment();
+ if (fragment != null) {
+ Element e = d.jsoupDocument.getElementById(fragment);
+ return e == null ? null : follow(e, d);
+ }
+ } catch (URISyntaxException ex) {
+ throw new ClientException("Error parsing URI: " + absHref, ex);
+ }
+
+ ItemsImpl items = (ItemsImpl) d.items();
+
+ if (items.length() == 1) {
+ return (ItemImpl) items.at(0);
+ }
+
+ throw new ClientException("Unable determine item: " + absHref);
+ }
+
+ return new ItemImpl(el, doc);
+ }
+
+ @Override
+ public String text() throws ClientException {
+ // resolve element
+ Element el = getProxy().el;
+
+ // if it's a meta, get the value of the content attr
+ if (el.tagName().equalsIgnoreCase("meta") && el.hasAttr("content")) {
+ return el.attr("content");
+ }
+
+ // else, get the text value using jsoup
+ return getProxy().el.text();
+ }
+
+ @Override
+ public boolean bool() throws ClientException {
+ return Boolean.parseBoolean(text());
+ }
+
+ @Override
+ public int number() throws ClientException {
+ return Integer.parseInt(text());
+ }
+
+ @Override
+ public String href() {
+ return el.attr("href");
+ }
+
+ @Override
+ public String src() {
+ return el.attr("src");
+ }
+
+ @Override
+ public Document follow() throws ClientException {
+ if ((el.tagName().equalsIgnoreCase("a") || el.tagName().equalsIgnoreCase("link")) && el.hasAttr("href")) {
+ String href = el.attr("href");
+
+ if (href.startsWith("#")) {
+ return document;
+ }
+
+ return document.client.enter(el.attr("abs:href"));
+ }
+
+ throw new ClientException("Unable to follow: " + el.toString());
+ }
+
+ @Override
+ public Document submit(Iterable<NameValuePair> values) throws ClientException {
+ if (el.tagName().equalsIgnoreCase("form")) {
+ String action = el.attr("abs:action");
+ if (action.length() == 0) {
+ action = el.baseUri();
+ }
+
+ String method = el.attr("method");
+
+ if (method.length() == 0 || method.equalsIgnoreCase("get")) {
+ FormValues query = new FormValues(el, values);
+ String url = action + (action.contains("?") ? "?" : "&") + query.toString();
+ return document.client.enter(url);
+ }
+
+ if (method.equalsIgnoreCase("post")) {
+ String enctype = el.attr("enctype");
+
+ FormValues v = new FormValues(el, values);
+ if (enctype.length() == 0 || enctype.equalsIgnoreCase("application/x-www-form-urlencoded")) {
+ return document.client.post(action, v.toUrlEncodedEntity());
+ } else if (enctype.equalsIgnoreCase("multipart/form-data")) {
+ return document.client.post(action, v.toMultipartEntity());
+ }
+
+ throw new ClientException("Unsupported form enctype: " + enctype);
+ }
+
+ throw new ClientException("Unsupported form method: " + method);
+ }
+
+ throw new ClientException("The item is not a form");
+ }
+
+
+ }
+
+ /**
+ * Items impl for microdata
+ */
+ private class ItemsImpl implements Items {
+ private List<Item> items;
+
+ public ItemsImpl(List<Item> items) {
+ this.items = items;
+ }
+
+ @Override
+ public Iterator<Item> iterator() {
+ return items.iterator();
+ }
+
+ @Override
+ public Item at(int index) {
+ return items.get(index);
+ }
+
+ @Override
+ public int length() {
+ return items.size();
+ }
+
+ @Override
+ public Items prop(String name) throws ClientException {
+ return items.get(0).prop(name);
+ }
+
+ @Override
+ public Set<String> props() throws ClientException {
+ return items.get(0).props();
+ }
+
+ @Override
+ public Items link(String rel) throws ClientException {
+ return items.get(0).link(rel);
+ }
+
+ @Override
+ public Items form(String rel) throws ClientException {
+ return items.get(0).form(rel);
+ }
+
+ @Override
+ public String text() throws ClientException {
+ return items.get(0).text();
+ }
+
+ @Override
+ public boolean bool() throws ClientException {
+ return items.get(0).bool();
+ }
+
+ @Override
+ public int number() throws ClientException {
+ return items.get(0).number();
+ }
+
+ @Override
+ public String href() {
+ return items.get(0).href();
+ }
+
+ @Override
+ public String src() {
+ return items.get(0).src();
+ }
+
+ @Override
+ public Document follow() throws ClientException {
+ return items.get(0).follow();
+ }
+
+ @Override
+ public Document submit(Iterable<NameValuePair> values) throws ClientException {
+ return items.get(0).submit(values);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/hapi/client/microdata/MicrodataHtmlClient.java b/src/main/java/org/apache/sling/hapi/client/microdata/MicrodataHtmlClient.java
new file mode 100644
index 0000000..807067a
--- /dev/null
+++ b/src/main/java/org/apache/sling/hapi/client/microdata/MicrodataHtmlClient.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.hapi.client.microdata;
+
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.sling.hapi.client.impl.AbstractHtmlClientImpl;
+
+import java.net.URISyntaxException;
+
+public class MicrodataHtmlClient extends AbstractHtmlClientImpl {
+
+ public MicrodataHtmlClient(CloseableHttpClient client, String baseUrl) throws URISyntaxException {
+ super(client, baseUrl);
+ }
+
+ public MicrodataHtmlClient(String baseUrl) throws URISyntaxException {
+ super(baseUrl);
+ }
+
+ public MicrodataHtmlClient(String baseUrl, String user, String password) throws URISyntaxException {
+ super(baseUrl, user, password);
+ }
+
+ @Override
+ public MicrodataDocument newDocument(String html) {
+ return new MicrodataDocument(html, this, this.baseUrl.toString());
+ }
+}
diff --git a/src/test/java/FormTest.java b/src/test/java/FormTest.java
new file mode 100644
index 0000000..3c9424d
--- /dev/null
+++ b/src/test/java/FormTest.java
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.*;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.localserver.LocalServerTestBase;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.sling.hapi.client.ClientException;
+import org.apache.sling.hapi.client.Document;
+import org.apache.sling.hapi.client.Items;
+import org.apache.sling.hapi.client.microdata.MicrodataHtmlClient;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+
+
+public class FormTest extends LocalServerTestBase {
+ public static final String GET_URL = "/test1";
+ public static final String POST_URL = "/testpost1";
+ public static final String OK_RESPONSE = "TEST_OK";
+ public static final String FAIL_RESPONSE = "TEST_FAIL";
+
+ public static String html;
+
+ private HttpHost host;
+ private URI uri;
+
+ @BeforeClass
+ public static void setUpClass() throws IOException {
+ FormTest.html = IOUtils.toString(ItemsTest.class.getResourceAsStream("items_forms.html"), "UTF-8");
+ }
+
+ @Before
+ public void setup() throws Exception {
+ super.setUp();
+ this.serverBootstrap.registerHandler(GET_URL, new HttpRequestHandler() {
+ @Override
+ public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext)
+ throws HttpException, IOException {
+ HttpEntity entity = new StringEntity(html, "UTF-8");
+ httpResponse.setEntity(entity);
+ }
+ }).registerHandler(POST_URL, new HttpRequestHandler() {
+ @Override
+ public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext)
+ throws HttpException, IOException {
+ if (!httpRequest.getRequestLine().getMethod().equals("POST")) {
+ httpResponse.setEntity(new StringEntity(FAIL_RESPONSE));
+ } else {
+ httpResponse.setEntity(new StringEntity(OK_RESPONSE));
+ }
+ httpResponse.setStatusCode(302);
+ httpResponse.setHeader("Location", GET_URL);
+ }
+ });
+
+ // start server
+ this.host = this.start();
+ this.uri = URIUtils.rewriteURI(new URI("/"), host);
+ }
+
+ @Test
+ public void testForm() throws ClientException, URISyntaxException {
+ MicrodataHtmlClient client = new MicrodataHtmlClient(uri.toString());
+ Document doc = client.enter(GET_URL);
+ Items items = doc.items();
+ Assert.assertThat(items.length(), equalTo(1));
+ Items form = doc.form("test");
+ Assert.assertThat(form.length(), equalTo(2));
+
+ List<NameValuePair> data = new ArrayList<NameValuePair>();
+ data.add(new BasicNameValuePair("f1", "val1"));
+
+ // url encode enctype
+ Document doc2 = form.at(0).submit(data);
+ Assert.assertThat(doc2.items().length(), equalTo(1));
+
+ // the multipart enctype
+ Document doc3 = form.at(1).submit(data);
+ Assert.assertThat(doc3.items().length(), equalTo(1));
+
+
+
+ }
+}
diff --git a/src/test/java/GetPostTest.java b/src/test/java/GetPostTest.java
new file mode 100644
index 0000000..e45b8b1
--- /dev/null
+++ b/src/test/java/GetPostTest.java
@@ -0,0 +1,111 @@
+
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+import org.apache.http.*;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.localserver.LocalServerTestBase;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.sling.hapi.client.ClientException;
+import org.apache.sling.hapi.client.Document;
+import org.apache.sling.hapi.client.microdata.MicrodataHtmlClient;
+import org.hamcrest.core.StringContains;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static org.hamcrest.core.StringContains.containsString;
+
+public class GetPostTest extends LocalServerTestBase {
+ public static final String GET_URL = "/test";
+ public static final String GET_AUTH_URL = "/testauth";
+ public static final String OK_RESPONSE = "TEST_OK";
+ public static final String FAIL_RESPONSE = "TEST_FAIL";
+ public static final String USER = "admin";
+ private static final String PASSWORD = "admin";
+ private static final String AUTH_STRING = "Basic YWRtaW46YWRtaW4=";
+ private static final String REDIRECT_URL = "/test_redirect";
+
+
+ private HttpHost host;
+ private URI uri;
+
+ @Before
+ public void setup() throws Exception {
+ super.setUp();
+ this.serverBootstrap.registerHandler(GET_URL, new HttpRequestHandler() {
+ @Override
+ public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext)
+ throws HttpException, IOException {
+ httpResponse.setEntity(new StringEntity(OK_RESPONSE));
+ }
+ }).registerHandler(GET_AUTH_URL, new HttpRequestHandler() {
+ @Override
+ public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext)
+ throws HttpException, IOException {
+ Header[] headers = httpRequest.getHeaders("Authorization");
+ if (null == headers || headers.length == 0 || !headers[0].getValue().equals(AUTH_STRING)) {
+ httpResponse.setStatusCode(401);
+ httpResponse.setHeader("WWW-Authenticate", "Basic realm=\"TEST\"");
+ } else {
+ httpResponse.setEntity(new StringEntity(OK_RESPONSE));
+ }
+ }
+ }).registerHandler(REDIRECT_URL, new HttpRequestHandler() {
+ @Override
+ public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException {
+ response.setStatusCode(307);
+ response.setHeader("Location", GET_URL);
+ }
+ });
+
+ // start server
+ this.host = this.start();
+ this.uri = URIUtils.rewriteURI(new URI("/"), host);
+ }
+
+ @Test
+ public void testValidGet() throws ClientException, URISyntaxException {
+ MicrodataHtmlClient client = new MicrodataHtmlClient(uri.toString());
+ Document doc = client.get(GET_URL);
+ Assert.assertThat("GET request failed", doc.toString(), new StringContains(OK_RESPONSE));
+ }
+
+ @Test
+ public void testValidAuthGet() throws ClientException, URISyntaxException {
+ MicrodataHtmlClient client = new MicrodataHtmlClient(uri.toString(), USER, PASSWORD);
+ Document doc = client.get(GET_AUTH_URL);
+ Assert.assertThat("GET request failed with basic auth", doc.toString(), containsString(OK_RESPONSE));
+ }
+
+ @Test
+ public void testRedirect() throws ClientException, URISyntaxException, UnsupportedEncodingException {
+ MicrodataHtmlClient client = new MicrodataHtmlClient(uri.toString(), USER, PASSWORD);
+ Document doc = client.post(REDIRECT_URL, new StringEntity("test"));
+ Assert.assertThat("POST request failed to redirect", doc.toString(), containsString(OK_RESPONSE));
+ }
+
+}
diff --git a/src/test/java/ItemsTest.java b/src/test/java/ItemsTest.java
new file mode 100644
index 0000000..9e0b962
--- /dev/null
+++ b/src/test/java/ItemsTest.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.*;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.localserver.LocalServerTestBase;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpRequestHandler;
+import org.apache.sling.hapi.client.ClientException;
+import org.apache.sling.hapi.client.Document;
+import org.apache.sling.hapi.client.Items;
+import org.apache.sling.hapi.client.microdata.MicrodataHtmlClient;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+
+
+public class ItemsTest extends LocalServerTestBase {
+ public static final String GET_URL = "/test";
+ public static final String GET_LINKS_URL = "/testlinks";
+ public static final String OK_RESPONSE = "TEST_OK";
+ public static final String FAIL_RESPONSE = "TEST_FAIL";
+
+ public static String html;
+ public static String htmlLinks;
+
+ private HttpHost host;
+ private URI uri;
+
+ @BeforeClass
+ public static void setUpClass() throws IOException {
+ ItemsTest.html = IOUtils.toString(ItemsTest.class.getResourceAsStream("items.html"), "UTF-8");
+ ItemsTest.htmlLinks = IOUtils.toString(ItemsTest.class.getResourceAsStream("items_links.html"), "UTF-8");
+ }
+
+ @Before
+ public void setup() throws Exception {
+ super.setUp();
+ this.serverBootstrap.registerHandler(GET_URL, new HttpRequestHandler() {
+ @Override
+ public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext)
+ throws HttpException, IOException {
+ HttpEntity entity = new StringEntity(html, "UTF-8");
+ httpResponse.setEntity(entity);
+ }
+ }).registerHandler(GET_LINKS_URL, new HttpRequestHandler() {
+ @Override
+ public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext)
+ throws HttpException, IOException {
+ HttpEntity entity = new StringEntity(htmlLinks, "UTF-8");
+ httpResponse.setEntity(entity);
+ }
+ });
+
+ // start server
+ this.host = this.start();
+ this.uri = URIUtils.rewriteURI(new URI("/"), host);
+ }
+
+ @Test
+ public void testItems() throws ClientException, URISyntaxException {
+ MicrodataHtmlClient client = new MicrodataHtmlClient(uri.toString());
+ Document doc = client.enter(GET_URL);
+ Items items = doc.items();
+ Assert.assertThat(items.length(), equalTo(2));
+ for (int i=0; i<2; i++) {
+ Assert.assertThat(items.at(i).prop("name").text(), equalTo("Avatar" + i));
+ Assert.assertThat(items.at(i).prop("genre").text(), equalTo("Science fiction" + i));
+ Assert.assertThat(items.at(i).prop("rank").number(), equalTo(i));
+ Assert.assertThat(items.at(i).prop("director").prop("name").text(), equalTo("James Cameron" + i));
+ Assert.assertThat(items.at(i).prop("director").prop("birthDate").text(), equalTo("August 16, 1954 - " + i));
+ }
+ }
+
+ @Test
+ public void testItemsLinks() throws ClientException, URISyntaxException {
+ MicrodataHtmlClient client = new MicrodataHtmlClient(uri.toString());
+ Document doc = client.enter(GET_LINKS_URL);
+ Items items = doc.items();
+ Assert.assertThat(items.length(), equalTo(1));
+ Assert.assertThat(items.prop("name").text(), equalTo("Avatar"));
+ Assert.assertThat(items.prop("genre").text(), equalTo("Science fiction"));
+ Assert.assertThat(items.prop("rank").number(), equalTo(2));
+ Assert.assertThat(items.prop("director").prop("name").text(), equalTo("James Cameron"));
+ Assert.assertThat(items.prop("director").prop("birthDate").text(), equalTo("August 16, 1954"));
+
+ Assert.assertThat(doc.link("test").length(), equalTo(2));
+ Assert.assertThat(doc.items().link("test").length(), equalTo(1));
+ Assert.assertThat(doc.items().prop("director").link("test").length(), equalTo(1));
+
+ Items otherMovies = doc.link("test").follow().items();
+ Assert.assertThat(otherMovies.length(), equalTo(2));
+
+ }
+
+}
diff --git a/src/test/resources/items.html b/src/test/resources/items.html
new file mode 100644
index 0000000..abe5d78
--- /dev/null
+++ b/src/test/resources/items.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+</head>
+</html>
+<body>
+<div itemscope itemtype ="http://schema.org/Movie">
+ <h1 itemprop="name">Avatar0</h1>
+ <div itemprop="director" itemscope itemtype="http://schema.org/Person">
+ Director: <span itemprop="name" itemscope itemtype="text">James Cameron0</span>
+ (born <span itemprop="birthDate" itemscope itemtype="text">August 16, 1954 - 0</span>)
+ </div>
+ <span itemprop="genre" itemscope itemtype="text">Science fiction0</span>
+ <span itemprop="rank" itemscope="true" itemtype="number">0</span>
+</div>
+<div itemscope itemtype ="http://schema.org/Movie">
+ <h1 itemprop="name">Avatar1</h1>
+ <div itemprop="director" itemscope itemtype="http://schema.org/Person">
+ Director: <span itemprop="name">James Cameron1</span> (born <span itemprop="birthDate">August 16, 1954 - 1</span>)
+ </div>
+ <span itemprop="genre">Science fiction1</span>
+ <span itemprop="rank">1</span>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/test/resources/items_forms.html b/src/test/resources/items_forms.html
new file mode 100644
index 0000000..fd14eb1
--- /dev/null
+++ b/src/test/resources/items_forms.html
@@ -0,0 +1,28 @@
+<html>
+<head>
+</head>
+</html>
+<body>
+<div itemscope itemtype ="http://schema.org/Movie">
+ <h1 itemprop="name">Avatar</h1>
+ <div itemprop="director" itemscope itemtype="http://schema.org/Person">
+ Director: <span itemprop="name" itemscope itemtype="text">James Cameron</span>
+ (born <span itemprop="birthDate" itemscope itemtype="text">August 16, 1954</span>)
+ <p>Also directed: <a rel="test" href="/test">test</a></p>
+ </div>
+ <span itemprop="genre" itemscope itemtype="text">Science fiction</span>
+ <span itemprop="rank" itemscope="true" itemtype="number">2</span>
+</div>
+<div>
+ <form data-rel="test" action="/testpost1" method="post">
+ <input type="text" name="f1" value="default1">
+ <textarea name="f2">val2</textarea>
+ </form>
+
+ <form data-rel="test" action="/testpost1" method="post" enctype="multipart/form-data">
+ <input type="text" name="f1" value="default1">
+ <textarea name="f2">val2</textarea>
+ </form>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/src/test/resources/items_links.html b/src/test/resources/items_links.html
new file mode 100644
index 0000000..1586116
--- /dev/null
+++ b/src/test/resources/items_links.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+</head>
+</html>
+<body>
+<div itemscope itemtype ="http://schema.org/Movie">
+ <h1 itemprop="name">Avatar</h1>
+ <div itemprop="director" itemscope itemtype="http://schema.org/Person">
+ Director: <span itemprop="name" itemscope itemtype="text">James Cameron</span>
+ (born <span itemprop="birthDate" itemscope itemtype="text">August 16, 1954</span>)
+ <p>Also directed: <a rel="test" href="/test">test</a></p>
+ </div>
+ <span itemprop="genre" itemscope itemtype="text">Science fiction</span>
+ <span itemprop="rank" itemscope="true" itemtype="number">2</span>
+</div>
+<div>
+ <a rel="test" href="/test">test</a>
+</div>
+</body>
+</html>
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.