HalClient.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2014 wcm.io
* %%
* Licensed 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.
* #L%
*/
package io.wcm.caravan.pipeline.extensions.hal.client;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import org.osgi.annotation.versioning.ProviderType;
import org.slf4j.Logger;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import io.wcm.caravan.hal.resource.HalResource;
import io.wcm.caravan.hal.resource.Link;
import io.wcm.caravan.hal.resource.util.HalBuilder;
import io.wcm.caravan.io.http.request.CaravanHttpRequest;
import io.wcm.caravan.io.http.request.CaravanHttpRequestBuilder;
import io.wcm.caravan.pipeline.JsonPipeline;
import io.wcm.caravan.pipeline.JsonPipelineAction;
import io.wcm.caravan.pipeline.JsonPipelineExceptionHandler;
import io.wcm.caravan.pipeline.JsonPipelineFactory;
import io.wcm.caravan.pipeline.cache.CacheStrategy;
import io.wcm.caravan.pipeline.extensions.hal.action.BuildResource;
import io.wcm.caravan.pipeline.extensions.hal.action.CreateResource;
import io.wcm.caravan.pipeline.extensions.hal.action.ModifyResource;
import io.wcm.caravan.pipeline.extensions.hal.client.action.DeepEmbedLinks;
import io.wcm.caravan.pipeline.extensions.hal.client.action.EmbedLink;
import io.wcm.caravan.pipeline.extensions.hal.client.action.EmbedLinks;
import io.wcm.caravan.pipeline.extensions.hal.client.action.FollowLink;
import io.wcm.caravan.pipeline.extensions.hal.client.action.LoadLink;
import rx.functions.Action1;
import rx.functions.Func2;
/**
* Factory for HAL specific {@link JsonPipelineAction}s.
*/
@ProviderType
public final class HalClient {
private ServiceIdExtractor serviceIdExtractor;
private final CacheStrategy cacheStrategy;
private final Map<String, String> contextProperties;
private final CaravanHttpRequest entryPointRequest;
private List<JsonPipelineExceptionHandler> exceptionHandlers = Lists.newArrayList();
private Logger logger;
/**
* @param serviceId Service ID
* @param cacheStrategy default cache strategy to use for all actions that fetch additional resources
*/
public HalClient(String serviceId, CacheStrategy cacheStrategy) {
this(serviceId, cacheStrategy, ImmutableMap.of());
}
/**
* @param serviceId Service ID
* @param cacheStrategy default cache strategy to use for all actions that fetch additional resources
* @param contextProperties a Map of properties to pass on to
* {@link JsonPipelineFactory#create(CaravanHttpRequest, Map)}
*/
public HalClient(String serviceId, CacheStrategy cacheStrategy, Map<String, String> contextProperties) {
this.serviceIdExtractor = (path) -> serviceId;
this.cacheStrategy = cacheStrategy;
this.contextProperties = contextProperties;
this.entryPointRequest = new CaravanHttpRequestBuilder(serviceId).append("/").build();
}
/**
* @param entryPointRequest the request to be executed to fetch the HAL entry point
* @param cacheStrategy default cache strategy to use for all actions that fetch additional resources
* @param contextProperties a Map of properties to pass on to
* {@link JsonPipelineFactory#create(CaravanHttpRequest, Map)}
*/
public HalClient(CaravanHttpRequest entryPointRequest, CacheStrategy cacheStrategy, Map<String, String> contextProperties) {
this.serviceIdExtractor = (path) -> entryPointRequest.getServiceId();
this.cacheStrategy = cacheStrategy;
this.contextProperties = contextProperties;
this.entryPointRequest = entryPointRequest;
}
/**
* @param entryPointRequest the request to be executed to fetch the HAL entry point
* @param serviceIdExtractor Service ID extractor
* @param cacheStrategy default cache strategy to use for all actions that fetch additional resources
* @param contextProperties a Map of properties to pass on to
* {@link JsonPipelineFactory#create(CaravanHttpRequest, Map)}
*/
public HalClient(CaravanHttpRequest entryPointRequest, ServiceIdExtractor serviceIdExtractor, CacheStrategy cacheStrategy,
Map<String, String> contextProperties) {
this.serviceIdExtractor = (path) -> entryPointRequest.getServiceId();
this.cacheStrategy = cacheStrategy;
this.contextProperties = contextProperties;
this.entryPointRequest = entryPointRequest;
}
/**
* Adds an exception handler for the pipeline actions.
* @param newExceptionHandler The exceptionHandler to set.
* @return This HAL client
*/
public HalClient addExceptionHandler(JsonPipelineExceptionHandler newExceptionHandler) {
this.exceptionHandlers.add(newExceptionHandler);
return this;
}
/**
* Sets the logger for the pipeline actions.
* @param value Logger to set.
* @return This HAL client
*/
public HalClient setLogger(Logger value) {
this.logger = value;
return this;
}
/**
* Replaces the default service id extractor (that uses the same serviceId for all requests) with a custom logic
* @param extractor the ServiceIdExtractor to use
* @return this hal client
*/
public HalClient setServiceIdExtractor(ServiceIdExtractor extractor) {
this.serviceIdExtractor = extractor;
return this;
}
/**
* Creates a {@link JsonPipeline} that will fetch the entry point of the HAL service
* @param factory the factory to use to create the pipeline
* @return the pipeline
*/
public JsonPipeline getEntryPoint(JsonPipelineFactory factory) {
return create(factory, entryPointRequest);
}
/**
* Creates a JSON pipeline for a service entry point.
* @deprecated use {@link #getEntryPoint(JsonPipelineFactory)} instead
* @param factory Pipeline factory
* @return JSON pipeline
*/
@Deprecated
public JsonPipeline createEntryPoint(JsonPipelineFactory factory) {
return create(factory, "/");
}
/**
* Creates a JSON pipeline for a service and URL.
* @deprecated specify the entry point in the constructor and use {@link #getEntryPoint(JsonPipelineFactory)} instead
* @param factory Pipeline factory
* @param url URL to start
* @return JSON pipeline
*/
@Deprecated
public JsonPipeline create(JsonPipelineFactory factory, String url) {
return create(factory, new CaravanHttpRequestBuilder(serviceIdExtractor.getServiceId(url)).append(url).build());
}
/**
* Creates a JSON pipeline for a HTTP request.
* @deprecated specify the entry point in the constructor and use {@link #getEntryPoint(JsonPipelineFactory)} instead
* @param factory Pipeline factory
* @param request Pre-configured HTTP request
* @return JSON pipeline
*/
@Deprecated
public JsonPipeline create(JsonPipelineFactory factory, CaravanHttpRequest request) {
JsonPipeline entryPoint = factory.create(request, contextProperties);
if (cacheStrategy != null) {
entryPoint = entryPoint.addCachePoint(cacheStrategy);
}
return entryPoint;
}
/**
* Creates a follow link action for the first relation specific link
* @param relation Link relation
* @return Follow link action
*/
public FollowLink follow(String relation) {
return follow(relation, Collections.emptyMap(), 0);
}
/**
* Creates a follow link action for the first relation specific link with the given URL parameters.
* @param relation Link relation
* @param parameters URL parameters
* @return Follow link action
*/
public FollowLink follow(String relation, Map<String, Object> parameters) {
return follow(relation, parameters, 0);
}
/**
* Creates a follow link action for the {@code index} specified link.
* @param relation Link relation
* @param index Link index
* @return Follow link action
*/
public FollowLink follow(String relation, int index) {
return follow(relation, Collections.emptyMap(), index);
}
/**
* Creates a follow link action for the {@code index} specified link with the given URL parameters.
* @param relation Link relation
* @param parameters URL parameters
* @param index Link index
* @return Follow link action
*/
public FollowLink follow(String relation, Map<String, Object> parameters, int index) {
return (FollowLink)new FollowLink(serviceIdExtractor, relation, index, parameters)
.setCacheStrategy(cacheStrategy)
.setExceptionHandlers(exceptionHandlers)
.setLogger(logger);
}
/**
* Creates a follow link action for the forst link with the given relation and name
* @param relation Link relation
* @param parameters URL parameters
* @param name of the link to follow
* @return Follow link action
*/
public FollowLink follow(String relation, Map<String, Object> parameters, String name) {
return (FollowLink)new FollowLink(serviceIdExtractor, relation, name, parameters)
.setCacheStrategy(cacheStrategy)
.setExceptionHandlers(exceptionHandlers)
.setLogger(logger);
}
/**
* Creates an embed links action for all relation specific links.
* @param relation Link relation
* @return Embed links action
*/
public EmbedLinks embed(String relation) {
return embed(relation, Collections.emptyMap());
}
/**
* Creates an embed links action for all relation specific links with the given URL parameters.
* @param relation Link relation
* @param parameters URL parameters
* @return Embed links action
*/
public EmbedLinks embed(String relation, Map<String, Object> parameters) {
return (EmbedLinks)new EmbedLinks(serviceIdExtractor, relation, parameters)
.setCacheStrategy(cacheStrategy)
.setExceptionHandlers(exceptionHandlers)
.setLogger(logger);
}
/**
* Creates an embed link action for the {@code index} specified link.
* @param relation Link relation
* @param index Link index
* @return Embed links action
*/
public EmbedLink embed(String relation, int index) {
return embed(relation, Collections.emptyMap(), index);
}
/**
* Creates an embed link action for the {@code index} specified link with the given URL parameters.
* @param relation Link relation
* @param parameters URL parameters
* @param index Link index
* @return Embed links action
*/
public EmbedLink embed(String relation, Map<String, Object> parameters, int index) {
return (EmbedLink)new EmbedLink(serviceIdExtractor, relation, index, parameters)
.setCacheStrategy(cacheStrategy)
.setExceptionHandlers(exceptionHandlers)
.setLogger(logger);
}
/**
* Fetches the content of all links with the given relation in a HAL resource <strong>and all embedded
* resources</strong>, and replaces the links with the
* corresponding embedded resources.
* @param relation Link relation
* @return Deep Embed links action
*/
public DeepEmbedLinks deepEmbed(String relation) {
return deepEmbed(relation, Collections.emptyMap());
}
/**
* Fetches the content of all links with the given relation in a HAL resource <strong>and all embedded
* resources</strong>, and replaces the links with the
* corresponding embedded resources.
* @param relation Link relation
* @param parameters URL parameters
* @return Deep Embed links action
*/
public DeepEmbedLinks deepEmbed(String relation, Map<String, Object> parameters) {
return (DeepEmbedLinks)new DeepEmbedLinks(serviceIdExtractor, relation, parameters)
.setCacheStrategy(cacheStrategy)
.setExceptionHandlers(exceptionHandlers)
.setLogger(logger);
}
/**
* Allows to create a {@link BuildResource} action by specifying the output href and a lambda
* @param selfHref the path of the output resource to be used for the self helf
* @param buildFunc the lambda that gets the previous step's output and a {@link HalBuilder} with the specified
* self-link
* @return the action that executes the lambda build function
* @deprecated use createResource instead
**/
@Deprecated
public BuildResource buildResource(String selfHref, Func2<HalResource, HalBuilder, HalResource> buildFunc) {
return new BuildResource(selfHref) {
@Override
public HalResource build(HalResource input, HalBuilder outputBuilder) {
return buildFunc.call(input, outputBuilder);
}
};
}
/**
* Allows to create a {@link CreateResource} action by specifying the output href and a lambda.
* The {@link CreateResource} action will automatically generated a cache-key based on the given href, and also
* set the self-link of the resource returned by the lambda
* @param selfHref the path of the output resource to be used for the self helf
* @param buildFunc the lambda that gets the previous step's output and should return a new HalResource
* @return the action that executes the lambda build function
**/
public CreateResource createResource(String selfHref, Function<HalResource, HalResource> buildFunc) {
return new CreateResource(selfHref) {
@Override
public HalResource createOutput(HalResource input) {
return buildFunc.apply(input);
}
};
}
/**
* Allows to create a {@link ModifyResource} action by specifying the output href and a lambda
* @param selfHref the path of the output resource to be used for the self helf
* @param modifyFunc the lambda that gets a HalResource with the previous step's output and the specified self-link
* @return the action that executes the lambda build function
**/
public ModifyResource modifyResource(String selfHref, Action1<HalResource> modifyFunc) {
return new ModifyResource(selfHref) {
@Override
public void modify(HalResource output) {
modifyFunc.call(output);
}
};
}
/**
* Creates a {@link LoadLink} action for the given link.
* @param link Link to load
* @return Load link action
*/
public LoadLink load(Link link) {
return load(link, Collections.emptyMap());
}
/**
* Creates a {@link LoadLink} action for the given link and URI parameters.
* @param link Link to load
* @param parameters URI parameters
* @return Load link action
*/
public LoadLink load(Link link, Map<String, Object> parameters) {
return (LoadLink)new LoadLink(serviceIdExtractor, link, parameters)
.setCacheStrategy(cacheStrategy)
.setExceptionHandlers(exceptionHandlers)
.setLogger(logger);
}
}