You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/10/15 20:37:00 UTC
[isis] branch v2 updated: ISIS-2006: initial prototype
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/v2 by this push:
new 1310040 ISIS-2006: initial prototype
1310040 is described below
commit 13100407dbc33f3cb8108cad1b4f546bc757b7f4
Author: Andi Huber <ah...@apache.org>
AuthorDate: Mon Oct 15 22:35:56 2018 +0200
ISIS-2006: initial prototype
Task-Url: https://issues.apache.org/jira/browse/ISIS-2006
---
.../applib/client/ActionParameterListBuilder.java | 102 +++++++++
.../apache/isis/applib/client/ResponseDigest.java | 174 +++++++++++++++
.../apache/isis/applib/client/RestfulClient.java | 237 +++++++++++++++++++++
.../isis/applib/client/RestfulClientConfig.java | 87 ++++++++
.../isis/applib/client/RestfulClientException.java | 40 ++++
.../apache/isis/applib/client/SuppressionType.java | 88 ++++++++
.../isis/applib/client/auth/BasicAuthFilter.java | 95 +++++++++
.../isis/commons/internal/base/_Strings.java | 18 ++
.../ContentNegotiationServiceOrgApacheIsisV1.java | 50 +----
9 files changed, 843 insertions(+), 48 deletions(-)
diff --git a/core/applib/src/main/java/org/apache/isis/applib/client/ActionParameterListBuilder.java b/core/applib/src/main/java/org/apache/isis/applib/client/ActionParameterListBuilder.java
new file mode 100644
index 0000000..2ed05be
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/client/ActionParameterListBuilder.java
@@ -0,0 +1,102 @@
+/*
+ * 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.isis.applib.client;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.client.Entity;
+
+/**
+ *
+ * @since 2.0.0-M2
+ */
+public class ActionParameterListBuilder {
+
+ private final Map<String, String> actionParameters = new LinkedHashMap<>();
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, String parameterValue) {
+ actionParameters.put(parameterName, parameterValue != null
+ ? value("\"" + parameterValue + "\"")
+ : value(JSON_NULL_LITERAL));
+ return this;
+ }
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, int parameterValue) {
+ actionParameters.put(parameterName, value(""+parameterValue));
+ return this;
+ }
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, long parameterValue) {
+ actionParameters.put(parameterName, value(""+parameterValue));
+ return this;
+ }
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, byte parameterValue) {
+ actionParameters.put(parameterName, value(""+parameterValue));
+ return this;
+ }
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, short parameterValue) {
+ actionParameters.put(parameterName, value(""+parameterValue));
+ return this;
+ }
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, double parameterValue) {
+ actionParameters.put(parameterName, value(""+parameterValue));
+ return this;
+ }
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, float parameterValue) {
+ actionParameters.put(parameterName, value(""+parameterValue));
+ return this;
+ }
+
+ public ActionParameterListBuilder addActionParameter(String parameterName, boolean parameterValue) {
+ actionParameters.put(parameterName, value(""+parameterValue));
+ return this;
+ }
+
+ public Entity<String> build() {
+
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{\n")
+ .append(actionParameters.entrySet().stream()
+ .map(this::toJson)
+ .collect(Collectors.joining(",\n")))
+ .append("\n}");
+
+ return Entity.json(sb.toString());
+ }
+
+ // -- HELPER
+
+ private final static String JSON_NULL_LITERAL = "null";
+
+ private String value(String valueLiteral) {
+ return "{\"value\" : " + valueLiteral + "}";
+ }
+
+ private String toJson(Map.Entry<String, String> entry) {
+ return " \""+entry.getKey()+"\": "+entry.getValue();
+ }
+
+
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/client/ResponseDigest.java b/core/applib/src/main/java/org/apache/isis/applib/client/ResponseDigest.java
new file mode 100644
index 0000000..9623c8c
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/client/ResponseDigest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.isis.applib.client;
+
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.NoSuchElementException;
+import java.util.concurrent.Future;
+import java.util.function.Function;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status.Family;
+
+import org.apache.isis.commons.internal.base._Strings;
+
+/**
+ *
+ * @since 2.0.0-M2
+ */
+public class ResponseDigest<T> {
+
+ /** synchronous response processing */
+ public static <T> ResponseDigest<T> of(Response response, Class<T> entityType) {
+ return new ResponseDigest<>(response, entityType).digest();
+ }
+
+ /** a-synchronous response failure processing */
+ public static <T> ResponseDigest<T> ofAsyncFailure(
+ Future<Response> asyncResponse,
+ Class<T> entityType,
+ Exception failure) {
+
+ Response response;
+ try {
+ response = asyncResponse.isDone() ? asyncResponse.get() : null;
+ } catch (Exception e) {
+ response = null;
+ }
+
+ final ResponseDigest<T> failureDigest = new ResponseDigest<>(response, entityType);
+ return failureDigest.digestAsyncFailure(asyncResponse.isCancelled(), failure);
+ }
+
+ private final Response response;
+ private final Class<T> entityType;
+
+ private T entity;
+ private Exception failureCause;
+
+
+ protected ResponseDigest(Response response, Class<T> entityType) {
+ this.response = response;
+ this.entityType = entityType;
+ }
+
+ public boolean isSuccess() {
+ return !isFailure();
+ }
+
+ public boolean isFailure() {
+ return failureCause!=null;
+ }
+
+ public T get(){
+ return entity;
+ }
+
+ public Exception getFailureCause(){
+ return failureCause;
+ }
+
+ public T ifSuccessGetOrElseMap(Function<Exception, T> failureMapper) {
+ return isSuccess()
+ ? get()
+ : failureMapper.apply(getFailureCause());
+ }
+
+ public <X> X ifSuccessMapOrElseMap(Function<T, X> successMapper, Function<Exception, X> failureMapper) {
+ return isSuccess()
+ ? successMapper.apply(get())
+ : failureMapper.apply(getFailureCause());
+ }
+
+ // -- HELPER
+
+ private ResponseDigest<T> digest() {
+
+ if(response==null) {
+ entity = null;
+ failureCause = new NoSuchElementException();
+ return this;
+ }
+
+ if(!response.hasEntity()) {
+ entity = null;
+ failureCause = new NoSuchElementException(defaultFailureMessage(response));
+ return this;
+ }
+
+ if(response.getStatusInfo().getFamily() != Family.SUCCESSFUL) {
+ entity = null;
+ failureCause = new RestfulClientException(defaultFailureMessage(response));
+ return this;
+ }
+
+ try {
+ entity = response.readEntity(entityType);
+ } catch (Exception e) {
+ entity = null;
+ failureCause = new RestfulClientException("failed to read JAX-RS response content", e);
+ }
+
+ return this;
+ }
+
+ private ResponseDigest<T> digestAsyncFailure(boolean isCancelled, Exception failure) {
+
+ entity = null;
+
+
+ if(isCancelled) {
+ failureCause = new RestfulClientException("Async JAX-RS request was canceled", failure);
+ return this;
+ }
+
+ if(response==null) {
+ failureCause = new RestfulClientException("Async JAX-RS request failed", failure);
+ return this;
+ }
+
+ failureCause = new RestfulClientException("Async JAX-RS request failed "
+ + defaultFailureMessage(response), failure);
+ return this;
+
+ }
+
+ private String defaultFailureMessage(Response response) {
+ String failureMessage = "non-successful JAX-RS response: " +
+ String.format("%s (Http-Status-Code: %d)",
+ response.getStatusInfo().getReasonPhrase(),
+ response.getStatus());
+
+ if(response.hasEntity()) {
+ try {
+ String jsonContent = _Strings.read((InputStream) response.getEntity(), StandardCharsets.UTF_8);
+ return failureMessage + "\nContent:\n" + jsonContent;
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ return failureMessage;
+ }
+
+
+
+
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClient.java b/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClient.java
new file mode 100644
index 0000000..a96cbae
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClient.java
@@ -0,0 +1,237 @@
+/*
+ * 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.isis.applib.client;
+
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+
+import java.util.EnumSet;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Invocation.Builder;
+import javax.ws.rs.core.Response;
+
+import org.apache.isis.applib.client.auth.BasicAuthFilter;
+import org.apache.isis.applib.client.auth.BasicAuthFilter.Credentials;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.context._Context;
+
+/**
+ * Setup the Restful Client with Basic-Auth:
+ * <pre>
+RestfulClientConfig clientConfig = new RestfulClientConfig();
+clientConfig.setRestfulBase("http://localhost:8080/helloworld/restful/");
+// setup basic-auth
+clientConfig.setUseBasicAuth(true);
+clientConfig.setRestfulAuthUser("sven");
+clientConfig.setRestfulAuthPassword("pass");
+
+RestfulClient client = RestfulClient.ofConfig(clientConfig);
+ * </pre>
+ *
+ * Synchronous example:
+ * <pre>
+
+Builder request = client.request(
+ "services/myService/actions/lookupMyObjectById/invoke",
+ SuppressionType.setOf(SuppressionType.RO));
+
+Entity<String> args = client.arguments()
+ .addActionParameter("id", "12345")
+ .build();
+
+Response response = request.post(args);
+
+ResponseDigest<MyObject> digest = client.digest(response, MyObject.class);
+
+if(digest.isSuccess()) {
+ System.out.println("result: "+ digest.get().get$$instanceId());
+} else {
+ digest.getFailureCause().printStackTrace();
+}
+ * </pre>
+ * Asynchronous example:
+ * <pre>
+
+Builder request = client.request(
+ "services/myService/actions/lookupMyObjectById/invoke",
+ SuppressionType.setOf(SuppressionType.RO));
+
+Entity<String> args = client.arguments()
+ .addActionParameter("id", "12345")
+ .build();
+
+Response response = request
+ .async()
+ .post(args);
+
+CompletableFuture<ResponseDigest<MyObject>> digestFuture =
+ client.digest(response, MyObject.class);
+
+ResponseDigest<MyObject> digest = digestFuture.get(); // blocking
+
+if(digest.isSuccess()) {
+ System.out.println("result: "+ digest.get().get$$instanceId());
+} else {
+ digest.getFailureCause().printStackTrace();
+}
+ * </pre>
+ *
+ * Maven Setup:
+ * <pre>{@code
+<dependency>
+ <groupId>org.apache.isis.core</groupId>
+ <artifactId>isis-core-applib</artifactId>
+ <version>2.0.0-M2-SNAPSHOT</version>
+</dependency>
+<dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <version>2.1.1</version>
+</dependency>
+<dependency>
+ <groupId>org.glassfish.jersey.core</groupId>
+ <artifactId>jersey-client</artifactId>
+ <version>2.25.1</version>
+</dependency>
+<dependency>
+ <groupId>org.eclipse.persistence</groupId>
+ <artifactId>org.eclipse.persistence.moxy</artifactId>
+ <version>2.6.0</version>
+</dependency>}
+ * </pre>
+ *
+ * @since 2.0.0-M2
+ */
+public class RestfulClient {
+
+ public static String DEFAULT_RESPONSE_CONTENT_TYPE = "application/json;profile=urn:org.apache.isis/v1";
+
+ private RestfulClientConfig clientConfig;
+ private Client client;
+
+ public static RestfulClient ofConfig(RestfulClientConfig clientConfig) {
+ RestfulClient restClient = new RestfulClient();
+ restClient.init(clientConfig);
+ return restClient;
+ }
+
+ public void init(RestfulClientConfig clientConfig) {
+ this.clientConfig = clientConfig;
+ client = ClientBuilder.newClient();
+
+ if(clientConfig.isUseBasicAuth()){
+ final Credentials credentials = Credentials.of(
+ clientConfig.getRestfulAuthUser(),
+ clientConfig.getRestfulAuthPassword());
+ client.register(BasicAuthFilter.of(credentials));
+ }
+
+ try {
+ Class<?> MOXyJsonProvider = _Context.loadClass("org.eclipse.persistence.jaxb.rs.MOXyJsonProvider");
+ client.register(MOXyJsonProvider);
+ } catch (Exception e) {
+ // this is just provided for convenience
+ }
+
+ }
+
+ public RestfulClientConfig getConfig() {
+ return clientConfig;
+ }
+
+ public Client getJaxRsClient() {
+ return client;
+ }
+
+ // -- REQUEST BUILDER
+
+ public Builder request(String path, SuppressionType ... suppressionTypes) {
+ return request(path, SuppressionType.setOf(suppressionTypes));
+ }
+
+ public Builder request(String path, EnumSet<SuppressionType> suppressionTypes) {
+ final String responseContentType = DEFAULT_RESPONSE_CONTENT_TYPE
+ + toSuppressionLiteral(suppressionTypes);
+
+ return client.target(relativePathToUri(path)).request(responseContentType);
+ }
+
+ // -- ARGUMENT BUILDER
+
+ public ActionParameterListBuilder arguments() {
+ return new ActionParameterListBuilder();
+ }
+
+ // -- RESPONSE PROCESSING (SYNC)
+
+ public <T> ResponseDigest<T> digest(Response response, Class<T> entityType) {
+ return ResponseDigest.of(response, entityType);
+ }
+
+ // -- RESPONSE PROCESSING (ASYNC)
+
+ public <T> CompletableFuture<ResponseDigest<T>> digest(
+ final Future<Response> asyncResponse,
+ final Class<T> entityType) {
+
+ final CompletableFuture<ResponseDigest<T>> completableFuture = CompletableFuture.supplyAsync(()->{
+ try {
+ Response response = asyncResponse.get();
+ ResponseDigest<T> digest = digest(response, entityType);
+
+ return digest;
+
+ } catch (Exception e) {
+ return ResponseDigest.ofAsyncFailure(asyncResponse, entityType, e);
+ }
+ });
+
+ return completableFuture;
+ }
+
+ // -- HELPER
+
+ private String relativePathToUri(String path) {
+ final String baseUri = _Strings.suffix(clientConfig.getRestfulBase(), "/");
+ while(path.startsWith("/")) {
+ path = path.substring(1);
+ }
+ return baseUri + path;
+ }
+
+ private String toSuppressionLiteral(EnumSet<SuppressionType> suppressionTypes) {
+ final String suppressionSetLiteral = stream(suppressionTypes)
+ .map(SuppressionType::name)
+ .collect(Collectors.joining(","));
+
+ if(_Strings.isNotEmpty(suppressionSetLiteral)) {
+ return ";suppress=" + suppressionSetLiteral;
+ }
+
+ return "";
+ }
+
+
+
+
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClientConfig.java b/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClientConfig.java
new file mode 100644
index 0000000..c0578b1
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClientConfig.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.isis.applib.client;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ *
+ * @since 2.0.0-M2
+ */
+@XmlRootElement(name="restful-client-config")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RestfulClientConfig {
+
+ // --
+
+ @XmlElement(name="restfulBase")
+ private String restfulBase;
+
+ public String getRestfulBase() {
+ return restfulBase;
+ }
+
+ public void setRestfulBase(String restfulBase) {
+ this.restfulBase = restfulBase;
+ }
+
+ // --
+
+ @XmlElement(name="useBasicAuth")
+ private boolean useBasicAuth;
+
+ public boolean isUseBasicAuth() {
+ return useBasicAuth;
+ }
+
+ public void setUseBasicAuth(boolean useBasicAuth) {
+ this.useBasicAuth = useBasicAuth;
+ }
+
+ // --
+
+ @XmlElement(name="restfulAuthUser")
+ private String restfulAuthUser;
+
+ public String getRestfulAuthUser() {
+ return restfulAuthUser;
+ }
+
+ public void setRestfulAuthUser(String restfulAuthUser) {
+ this.restfulAuthUser = restfulAuthUser;
+ }
+
+ // --
+
+ @XmlElement(name="restfulAuthPassword")
+ private String restfulAuthPassword;
+
+ public String getRestfulAuthPassword() {
+ return restfulAuthPassword;
+ }
+
+ public void setRestfulAuthPassword(String restfulAuthPassword) {
+ this.restfulAuthPassword = restfulAuthPassword;
+ }
+
+
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClientException.java b/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClientException.java
new file mode 100644
index 0000000..2aaa99a
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/client/RestfulClientException.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * 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.isis.applib.client;
+
+public class RestfulClientException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public RestfulClientException() {
+ }
+
+ public RestfulClientException(final String message) {
+ super(message);
+ }
+
+ public RestfulClientException(final Throwable cause) {
+ super(cause);
+ }
+
+ public RestfulClientException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/core/applib/src/main/java/org/apache/isis/applib/client/SuppressionType.java b/core/applib/src/main/java/org/apache/isis/applib/client/SuppressionType.java
new file mode 100644
index 0000000..78467f1
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/client/SuppressionType.java
@@ -0,0 +1,88 @@
+/*
+ * 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.isis.applib.client;
+
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+
+import java.util.EnumSet;
+import java.util.List;
+
+import org.apache.isis.commons.internal.base._NullSafe;
+
+/**
+ *
+ * @since 2.0.0-M2
+ */
+public enum SuppressionType {
+
+ /** suppress '$$RO', RO Spec representation*/
+ RO,
+
+ /** suppress '$$href', hyperlink to the representation*/
+ HREF,
+
+ /** suppress '$$instanceId', instance id of the domain object*/
+ ID,
+
+ /** suppress '$$title', title of the domain object*/
+ TITLE,
+
+ /** suppress all '$$...' entries*/
+ ALL
+ ;
+
+ public static EnumSet<SuppressionType> setOf(SuppressionType ... types){
+ final EnumSet<SuppressionType> set = EnumSet.noneOf(SuppressionType.class);
+ stream(types).forEach(set::add);
+ return set;
+ }
+
+ public static class ParseUtil {
+
+ public static EnumSet<SuppressionType> parse(List<String> parameterList) {
+ final EnumSet<SuppressionType> set = EnumSet.noneOf(SuppressionType.class);
+ parameterList.stream()
+ .map(SuppressionType.ParseUtil::parseOrElseNull)
+ .filter(_NullSafe::isPresent)
+ .forEach(set::add);
+ if(set.contains(ALL)) {
+ return EnumSet.allOf(SuppressionType.class);
+ }
+ return set;
+ }
+
+ private static SuppressionType parseOrElseNull(String literal) {
+
+ // honor pre v2 behavior
+ if("true".equalsIgnoreCase(literal)) {
+ return SuppressionType.RO;
+ }
+
+ try {
+ return SuppressionType.valueOf(literal.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/core/applib/src/main/java/org/apache/isis/applib/client/auth/BasicAuthFilter.java b/core/applib/src/main/java/org/apache/isis/applib/client/auth/BasicAuthFilter.java
new file mode 100644
index 0000000..3f4f39e
--- /dev/null
+++ b/core/applib/src/main/java/org/apache/isis/applib/client/auth/BasicAuthFilter.java
@@ -0,0 +1,95 @@
+/*
+ * 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.isis.applib.client.auth;
+
+import static org.apache.isis.commons.internal.base._With.requires;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.isis.commons.internal.base._Strings;
+
+/**
+ *
+ * @since 2.0.0-M2
+ */
+public class BasicAuthFilter implements ClientRequestFilter {
+
+ /**
+ *
+ * @since 2.0.0-M2
+ */
+ public static class Credentials {
+ final String user;
+ final String pass;
+ public static Credentials empty() {
+ return new Credentials("anonymous", null);
+ }
+ public static Credentials of(String user, String pass) {
+ if(_Strings.isNullOrEmpty(user)) {
+ return empty();
+ }
+ return new Credentials(user, pass);
+ }
+ private Credentials(String user, String pass) {
+ this.user = user;
+ this.pass = pass;
+ }
+ @Override
+ public String toString() {
+ return "" + user + ":" + pass;
+ }
+ }
+
+ public static BasicAuthFilter of(Credentials credentials) {
+ BasicAuthFilter filter = new BasicAuthFilter();
+ filter.setCredentials(credentials);
+ return filter;
+ }
+
+ private Credentials credentials = Credentials.empty();
+
+ public Credentials getCredentials() {
+ return credentials;
+ }
+
+ public void setCredentials(Credentials credentials) {
+ this.credentials = requires(credentials, "credentials");
+ }
+
+ @Override
+ public void filter(ClientRequestContext requestContext) throws IOException {
+ requestContext.getHeaders().add("Authorization", getAuthorizationValue());
+ }
+
+ // -- HELPER
+
+ private String getAuthorizationValue() {
+ try {
+ return "Basic " + DatatypeConverter.printBase64Binary(credentials.toString().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException ex) {
+ throw new IllegalStateException("Cannot encode with UTF-8", ex);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java
index d75b615..38fd902 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java
@@ -24,9 +24,11 @@ import static org.apache.isis.commons.internal.base._Strings_SplitIterator.split
import static org.apache.isis.commons.internal.base._With.mapIfPresentElse;
import static org.apache.isis.commons.internal.base._With.requires;
+import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Map;
+import java.util.Scanner;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.UnaryOperator;
@@ -332,6 +334,20 @@ public final class _Strings {
requires(replacement, "replacement");
return mapIfPresentElse(input, __->input.replaceAll("\\s+", replacement), null);
}
+
+ // -- READ FROM INPUT STREAM
+
+ public static String read(@Nullable final InputStream input, Charset charset) {
+ requires(charset, "charset");
+ if(input==null) {
+ return "";
+ }
+ // see https://stackoverflow.com/questions/309424/how-to-read-convert-an-inputstream-into-a-string-in-java
+ try(Scanner scanner = new Scanner(input, charset.name())){
+ scanner.useDelimiter("\\A");
+ return scanner.hasNext() ? scanner.next() : "";
+ }
+ }
// -- BYTE ARRAY CONVERSION
@@ -424,5 +440,7 @@ public final class _Strings {
return suffix(fileName, prefix(fileExtension, "."));
}
+
+
}
diff --git a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java
index 94ba534..2534fc9 100644
--- a/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java
+++ b/core/viewer-restfulobjects-rendering/src/main/java/org/apache/isis/viewer/restfulobjects/rendering/service/conneg/ContentNegotiationServiceOrgApacheIsisV1.java
@@ -29,7 +29,7 @@ import org.apache.isis.applib.annotation.DomainService;
import org.apache.isis.applib.annotation.NatureOfService;
import org.apache.isis.applib.annotation.Programmatic;
import org.apache.isis.applib.annotation.Where;
-import org.apache.isis.commons.internal.base._NullSafe;
+import org.apache.isis.applib.client.SuppressionType;
import org.apache.isis.core.metamodel.adapter.ObjectAdapter;
import org.apache.isis.core.metamodel.consent.Consent;
import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
@@ -54,52 +54,6 @@ import org.apache.isis.viewer.restfulobjects.rendering.service.RepresentationSer
)
public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiationServiceAbstract {
- public static enum SuppressionType {
-
- /** suppress '$$RO', RO Spec representation*/
- RO,
-
- /** suppress '$$href', hyperlink to the representation*/
- HREF,
-
- /** suppress '$$instanceId', instance id of the domain object*/
- ID,
-
- /** suppress '$$title', title of the domain object*/
- TITLE,
-
- /** suppress all '$$...' entries*/
- ALL
- ;
-
- public static EnumSet<SuppressionType> parse(List<String> parameterList) {
- final EnumSet<SuppressionType> set = EnumSet.noneOf(SuppressionType.class);
- parameterList.stream()
- .map(SuppressionType::parseOrElseNull)
- .filter(_NullSafe::isPresent)
- .forEach(set::add);
- if(set.contains(ALL)) {
- return EnumSet.allOf(SuppressionType.class);
- }
- return set;
- }
-
- private static SuppressionType parseOrElseNull(String literal) {
-
- // honor pre v2 behavior
- if("true".equalsIgnoreCase(literal)) {
- return SuppressionType.RO;
- }
-
- try {
- return SuppressionType.valueOf(literal.toUpperCase());
- } catch (IllegalArgumentException e) {
- return null;
- }
- }
-
- }
-
/**
* Unlike RO v1.0, use a single content-type of <code>application/json;profile="urn:org.apache.isis/v1"</code>.
*
@@ -344,7 +298,7 @@ public class ContentNegotiationServiceOrgApacheIsisV1 extends ContentNegotiation
protected EnumSet<SuppressionType> suppress(
final RepresentationService.Context2 rendererContext) {
final List<MediaType> acceptableMediaTypes = rendererContext.getAcceptableMediaTypes();
- return SuppressionType.parse(mediaTypeParameterList(acceptableMediaTypes, "suppress"));
+ return SuppressionType.ParseUtil.parse(mediaTypeParameterList(acceptableMediaTypes, "suppress"));
}
private void appendObjectTo(