[sleepiq] Use constructor injection for ClientBuilder (#11700)
Fixes #11696 Signed-off-by: Mark Hilbush <mark@hilbush.com>
This commit is contained in:
parent
d0bf1e3313
commit
c1de380771
|
@ -17,6 +17,8 @@ package org.openhab.binding.sleepiq.api;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
|
||||
import org.openhab.binding.sleepiq.api.impl.SleepIQImpl;
|
||||
import org.openhab.binding.sleepiq.api.model.Bed;
|
||||
import org.openhab.binding.sleepiq.api.model.FamilyStatus;
|
||||
|
@ -29,8 +31,7 @@ import org.openhab.binding.sleepiq.api.model.Sleeper;
|
|||
*
|
||||
* @author Gregory Moyer
|
||||
*/
|
||||
public interface SleepIQ
|
||||
{
|
||||
public interface SleepIQ {
|
||||
/**
|
||||
* Login to the {@link Configuration configured} account. This method is not
|
||||
* required to be called before other methods because all methods must
|
||||
|
@ -94,8 +95,7 @@ public interface SleepIQ
|
|||
* the configuration to use for the new instance
|
||||
* @return a concrete implementation of this interface
|
||||
*/
|
||||
public static SleepIQ create(Configuration config)
|
||||
{
|
||||
return new SleepIQImpl(config);
|
||||
public static SleepIQ create(Configuration config, ClientBuilder clientBuilder) {
|
||||
return new SleepIQImpl(config, clientBuilder);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,8 +49,7 @@ import org.openhab.binding.sleepiq.api.model.Sleeper;
|
|||
import org.openhab.binding.sleepiq.api.model.SleepersResponse;
|
||||
import org.openhab.binding.sleepiq.internal.GsonProvider;
|
||||
|
||||
public class SleepIQImpl extends AbstractClient implements SleepIQ
|
||||
{
|
||||
public class SleepIQImpl extends AbstractClient implements SleepIQ {
|
||||
protected static final String PARAM_KEY = "_k";
|
||||
|
||||
protected static final String DATA_BED_ID = "bedId";
|
||||
|
@ -59,47 +58,36 @@ public class SleepIQImpl extends AbstractClient implements SleepIQ
|
|||
|
||||
private volatile LoginInfo loginInfo;
|
||||
|
||||
public SleepIQImpl(Configuration config)
|
||||
{
|
||||
private final ClientBuilder clientBuilder;
|
||||
|
||||
public SleepIQImpl(Configuration config, ClientBuilder clientBuilder) {
|
||||
this.config = config;
|
||||
this.clientBuilder = clientBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginInfo login() throws LoginException
|
||||
{
|
||||
if (loginInfo == null)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (loginInfo == null)
|
||||
{
|
||||
Response response = getClient().target(config.getBaseUri())
|
||||
.path(Endpoints.login())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE)
|
||||
.put(Entity.json(new LoginRequest().withLogin(config.getUsername())
|
||||
.withPassword(config.getPassword())));
|
||||
public LoginInfo login() throws LoginException {
|
||||
if (loginInfo == null) {
|
||||
synchronized (this) {
|
||||
if (loginInfo == null) {
|
||||
Response response = getClient().target(config.getBaseUri()).path(Endpoints.login())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE).put(Entity.json(new LoginRequest()
|
||||
.withLogin(config.getUsername()).withPassword(config.getPassword())));
|
||||
|
||||
if (isUnauthorized(response))
|
||||
{
|
||||
if (isUnauthorized(response)) {
|
||||
throw new UnauthorizedException(response.readEntity(Failure.class));
|
||||
}
|
||||
|
||||
if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily()))
|
||||
{
|
||||
if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) {
|
||||
throw new LoginException(response.readEntity(Failure.class));
|
||||
}
|
||||
|
||||
// add the received cookies to all future requests
|
||||
getClient().register(new ClientRequestFilter()
|
||||
{
|
||||
getClient().register(new ClientRequestFilter() {
|
||||
@Override
|
||||
public void filter(ClientRequestContext requestContext) throws IOException
|
||||
{
|
||||
List<Object> cookies = response.getCookies()
|
||||
.values()
|
||||
.stream()
|
||||
.map(newCookie -> newCookie.toCookie())
|
||||
.collect(Collectors.toList());
|
||||
public void filter(ClientRequestContext requestContext) throws IOException {
|
||||
List<Object> cookies = response.getCookies().values().stream()
|
||||
.map(newCookie -> newCookie.toCookie()).collect(Collectors.toList());
|
||||
requestContext.getHeaders().put("Cookie", cookies);
|
||||
}
|
||||
});
|
||||
|
@ -113,106 +101,76 @@ public class SleepIQImpl extends AbstractClient implements SleepIQ
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<Bed> getBeds()
|
||||
{
|
||||
public List<Bed> getBeds() {
|
||||
return getSessionResponse(this::getBedsResponse).readEntity(BedsResponse.class).getBeds();
|
||||
}
|
||||
|
||||
protected Response getBedsResponse(Map<String, Object> data) throws LoginException
|
||||
{
|
||||
protected Response getBedsResponse(Map<String, Object> data) throws LoginException {
|
||||
LoginInfo login = login();
|
||||
return getClient().target(config.getBaseUri())
|
||||
.path(Endpoints.bed())
|
||||
.queryParam(PARAM_KEY, login.getKey())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get();
|
||||
return getClient().target(config.getBaseUri()).path(Endpoints.bed()).queryParam(PARAM_KEY, login.getKey())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Sleeper> getSleepers()
|
||||
{
|
||||
return getSessionResponse(this::getSleepersResponse).readEntity(SleepersResponse.class)
|
||||
.getSleepers();
|
||||
public List<Sleeper> getSleepers() {
|
||||
return getSessionResponse(this::getSleepersResponse).readEntity(SleepersResponse.class).getSleepers();
|
||||
}
|
||||
|
||||
protected Response getSleepersResponse(Map<String, Object> data) throws LoginException
|
||||
{
|
||||
protected Response getSleepersResponse(Map<String, Object> data) throws LoginException {
|
||||
LoginInfo login = login();
|
||||
return getClient().target(config.getBaseUri())
|
||||
.path(Endpoints.sleeper())
|
||||
.queryParam(PARAM_KEY, login.getKey())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get();
|
||||
return getClient().target(config.getBaseUri()).path(Endpoints.sleeper()).queryParam(PARAM_KEY, login.getKey())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FamilyStatus getFamilyStatus()
|
||||
{
|
||||
public FamilyStatus getFamilyStatus() {
|
||||
return getSessionResponse(this::getFamilyStatusResponse).readEntity(FamilyStatus.class);
|
||||
}
|
||||
|
||||
protected Response getFamilyStatusResponse(Map<String, Object> data) throws LoginException
|
||||
{
|
||||
protected Response getFamilyStatusResponse(Map<String, Object> data) throws LoginException {
|
||||
LoginInfo login = login();
|
||||
return getClient().target(config.getBaseUri())
|
||||
.path(Endpoints.bed())
|
||||
.path(Endpoints.familyStatus())
|
||||
.queryParam(PARAM_KEY, login.getKey())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get();
|
||||
return getClient().target(config.getBaseUri()).path(Endpoints.bed()).path(Endpoints.familyStatus())
|
||||
.queryParam(PARAM_KEY, login.getKey()).request(MediaType.APPLICATION_JSON_TYPE).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PauseMode getPauseMode(String bedId) throws BedNotFoundException
|
||||
{
|
||||
public PauseMode getPauseMode(String bedId) throws BedNotFoundException {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put(DATA_BED_ID, bedId);
|
||||
|
||||
Response response = getSessionResponse(this::getPauseModeResponse, data);
|
||||
|
||||
if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily()))
|
||||
{
|
||||
if (!Status.Family.SUCCESSFUL.equals(response.getStatusInfo().getFamily())) {
|
||||
throw new BedNotFoundException(response.readEntity(Failure.class));
|
||||
}
|
||||
|
||||
return response.readEntity(PauseMode.class);
|
||||
}
|
||||
|
||||
protected Response getPauseModeResponse(Map<String, Object> data) throws LoginException
|
||||
{
|
||||
protected Response getPauseModeResponse(Map<String, Object> data) throws LoginException {
|
||||
LoginInfo login = login();
|
||||
return getClient().target(config.getBaseUri())
|
||||
.path(Endpoints.bed())
|
||||
.path(data.get(DATA_BED_ID).toString())
|
||||
.path(Endpoints.pauseMode())
|
||||
.queryParam(PARAM_KEY, login.getKey())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get();
|
||||
return getClient().target(config.getBaseUri()).path(Endpoints.bed()).path(data.get(DATA_BED_ID).toString())
|
||||
.path(Endpoints.pauseMode()).queryParam(PARAM_KEY, login.getKey())
|
||||
.request(MediaType.APPLICATION_JSON_TYPE).get();
|
||||
}
|
||||
|
||||
protected boolean isUnauthorized(Response response)
|
||||
{
|
||||
protected boolean isUnauthorized(Response response) {
|
||||
return Status.UNAUTHORIZED.getStatusCode() == response.getStatusInfo().getStatusCode();
|
||||
}
|
||||
|
||||
protected synchronized void resetLogin()
|
||||
{
|
||||
protected synchronized void resetLogin() {
|
||||
loginInfo = null;
|
||||
}
|
||||
|
||||
protected Response getSessionResponse(Request request)
|
||||
{
|
||||
protected Response getSessionResponse(Request request) {
|
||||
return getSessionResponse(request, Collections.emptyMap());
|
||||
}
|
||||
|
||||
protected Response getSessionResponse(Request request, Map<String, Object> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
protected Response getSessionResponse(Request request, Map<String, Object> data) {
|
||||
try {
|
||||
Response response = request.execute(data);
|
||||
|
||||
if (isUnauthorized(response))
|
||||
{
|
||||
if (isUnauthorized(response)) {
|
||||
// session timed out
|
||||
response.close();
|
||||
resetLogin();
|
||||
|
@ -220,35 +178,27 @@ public class SleepIQImpl extends AbstractClient implements SleepIQ
|
|||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
catch (LoginException e)
|
||||
{
|
||||
} catch (LoginException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Client createClient()
|
||||
{
|
||||
ClientBuilder builder = ClientBuilder.newBuilder();
|
||||
|
||||
protected Client createClient() {
|
||||
// setup Gson (de)serialization
|
||||
GsonProvider<Object> gsonProvider = new GsonProvider<>(getGson());
|
||||
builder.register(gsonProvider);
|
||||
clientBuilder.register(gsonProvider);
|
||||
|
||||
// turn on logging if requested
|
||||
if (config.isLogging())
|
||||
{
|
||||
builder.register(new LoggingFilter(Logger.getLogger(SleepIQImpl.class.getName()),
|
||||
true));
|
||||
if (config.isLogging()) {
|
||||
clientBuilder.register(new LoggingFilter(Logger.getLogger(SleepIQImpl.class.getName()), true));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
return clientBuilder.build();
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface Request
|
||||
{
|
||||
public static interface Request {
|
||||
public Response execute(Map<String, Object> data) throws LoginException;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,46 +16,37 @@
|
|||
package org.openhab.binding.sleepiq.api.model;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TimeSince
|
||||
{
|
||||
public class TimeSince {
|
||||
private static final Pattern PATTERN = Pattern.compile("(([0-9]+) d )?([0-9]{2}):([0-9]{2}):([0-9]{2})",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private Duration duration;
|
||||
|
||||
public Duration getDuration()
|
||||
{
|
||||
public Duration getDuration() {
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void setDuration(Duration duration)
|
||||
{
|
||||
public void setDuration(Duration duration) {
|
||||
this.duration = duration == null ? null : duration.abs();
|
||||
}
|
||||
|
||||
public TimeSince withDuration(long days, long hours, long minutes, long seconds)
|
||||
{
|
||||
return withDuration(Duration.ofSeconds(TimeUnit.DAYS.toSeconds(days)
|
||||
+ TimeUnit.HOURS.toSeconds(hours)
|
||||
+ TimeUnit.MINUTES.toSeconds(minutes)
|
||||
+ seconds));
|
||||
public TimeSince withDuration(long days, long hours, long minutes, long seconds) {
|
||||
return withDuration(Duration.ofSeconds(TimeUnit.DAYS.toSeconds(days) + TimeUnit.HOURS.toSeconds(hours)
|
||||
+ TimeUnit.MINUTES.toSeconds(minutes) + seconds));
|
||||
}
|
||||
|
||||
public TimeSince withDuration(Duration duration)
|
||||
{
|
||||
public TimeSince withDuration(Duration duration) {
|
||||
setDuration(duration);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((duration == null) ? 0 : duration.hashCode());
|
||||
|
@ -63,38 +54,29 @@ public class TimeSince
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof TimeSince))
|
||||
{
|
||||
if (!(obj instanceof TimeSince)) {
|
||||
return false;
|
||||
}
|
||||
TimeSince other = (TimeSince)obj;
|
||||
if (duration == null)
|
||||
{
|
||||
if (other.duration != null)
|
||||
{
|
||||
TimeSince other = (TimeSince) obj;
|
||||
if (duration == null) {
|
||||
if (other.duration != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!duration.equals(other.duration))
|
||||
{
|
||||
} else if (!duration.equals(other.duration)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
long totalDays = duration.toDays();
|
||||
long totalHours = duration.toHours();
|
||||
long totalMinutes = duration.toMinutes();
|
||||
|
@ -104,22 +86,19 @@ public class TimeSince
|
|||
long minutes = totalMinutes - TimeUnit.HOURS.toMinutes(totalHours);
|
||||
long seconds = totalSeconds - TimeUnit.MINUTES.toSeconds(totalMinutes);
|
||||
|
||||
if (totalDays > 0)
|
||||
{
|
||||
if (totalDays > 0) {
|
||||
return String.format("%d d %02d:%02d:%02d", totalDays, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
}
|
||||
|
||||
public static TimeSince parse(CharSequence text)
|
||||
{
|
||||
public static TimeSince parse(CharSequence text) {
|
||||
Objects.requireNonNull(text, "text");
|
||||
|
||||
Matcher matcher = PATTERN.matcher(text);
|
||||
if (!matcher.matches())
|
||||
{
|
||||
throw new DateTimeParseException("Text cannot be parsed", text, 0);
|
||||
if (!matcher.matches()) {
|
||||
return new TimeSince().withDuration(Duration.ZERO);
|
||||
}
|
||||
|
||||
String dayMatch = matcher.group(2);
|
||||
|
@ -128,17 +107,10 @@ public class TimeSince
|
|||
String secondMatch = matcher.group(5);
|
||||
|
||||
StringBuilder sb = new StringBuilder("P");
|
||||
if (dayMatch != null)
|
||||
{
|
||||
if (dayMatch != null) {
|
||||
sb.append(dayMatch).append('D');
|
||||
}
|
||||
sb.append('T')
|
||||
.append(hourMatch)
|
||||
.append('H')
|
||||
.append(minuteMatch)
|
||||
.append('M')
|
||||
.append(secondMatch)
|
||||
.append('S');
|
||||
sb.append('T').append(hourMatch).append('H').append(minuteMatch).append('M').append(secondMatch).append('S');
|
||||
|
||||
return new TimeSince().withDuration(Duration.parse(sb.toString()));
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import java.util.Set;
|
|||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
|
||||
import org.openhab.binding.sleepiq.internal.discovery.SleepIQBedDiscoveryService;
|
||||
import org.openhab.binding.sleepiq.internal.handler.SleepIQCloudHandler;
|
||||
import org.openhab.binding.sleepiq.internal.handler.SleepIQDualBedHandler;
|
||||
|
@ -32,7 +34,9 @@ import org.openhab.core.thing.binding.BaseThingHandlerFactory;
|
|||
import org.openhab.core.thing.binding.ThingHandler;
|
||||
import org.openhab.core.thing.binding.ThingHandlerFactory;
|
||||
import org.osgi.framework.ServiceRegistration;
|
||||
import org.osgi.service.component.annotations.Activate;
|
||||
import org.osgi.service.component.annotations.Component;
|
||||
import org.osgi.service.component.annotations.Reference;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -49,8 +53,15 @@ public class SleepIQHandlerFactory extends BaseThingHandlerFactory {
|
|||
|
||||
private final Logger logger = LoggerFactory.getLogger(SleepIQHandlerFactory.class);
|
||||
|
||||
private final ClientBuilder clientBuilder;
|
||||
|
||||
private final Map<ThingUID, ServiceRegistration<?>> discoveryServiceReg = new HashMap<>();
|
||||
|
||||
@Activate
|
||||
public SleepIQHandlerFactory(@Reference ClientBuilder clientBuilder) {
|
||||
this.clientBuilder = clientBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsThingType(final ThingTypeUID thingTypeUID) {
|
||||
return SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID);
|
||||
|
@ -62,7 +73,7 @@ public class SleepIQHandlerFactory extends BaseThingHandlerFactory {
|
|||
|
||||
if (SleepIQCloudHandler.SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID)) {
|
||||
logger.debug("Creating SleepIQ cloud thing handler");
|
||||
SleepIQCloudHandler cloudHandler = new SleepIQCloudHandler((Bridge) thing);
|
||||
SleepIQCloudHandler cloudHandler = new SleepIQCloudHandler((Bridge) thing, clientBuilder);
|
||||
registerBedDiscoveryService(cloudHandler);
|
||||
return cloudHandler;
|
||||
} else if (SleepIQDualBedHandler.SUPPORTED_THING_TYPE_UIDS.contains(thingTypeUID)) {
|
||||
|
|
|
@ -25,6 +25,8 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
|
||||
import org.openhab.binding.sleepiq.api.Configuration;
|
||||
import org.openhab.binding.sleepiq.api.LoginException;
|
||||
import org.openhab.binding.sleepiq.api.SleepIQ;
|
||||
|
@ -67,8 +69,11 @@ public class SleepIQCloudHandler extends ConfigStatusBridgeHandler {
|
|||
|
||||
private SleepIQ cloud;
|
||||
|
||||
public SleepIQCloudHandler(final Bridge bridge) {
|
||||
private ClientBuilder clientBuilder;
|
||||
|
||||
public SleepIQCloudHandler(final Bridge bridge, ClientBuilder clientBuilder) {
|
||||
super(bridge);
|
||||
this.clientBuilder = clientBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,6 +105,8 @@ public class SleepIQCloudHandler extends ConfigStatusBridgeHandler {
|
|||
/**
|
||||
* Create a new SleepIQ cloud service connection. If a connection already exists, it will be lost.
|
||||
*
|
||||
* @param clientBuilder2
|
||||
*
|
||||
* @throws LoginException if there is an error while authenticating to the service
|
||||
*/
|
||||
private void createCloudConnection() throws LoginException {
|
||||
|
@ -109,7 +116,7 @@ public class SleepIQCloudHandler extends ConfigStatusBridgeHandler {
|
|||
logger.debug("Creating SleepIQ client");
|
||||
Configuration cloudConfig = new Configuration().withUsername(bindingConfig.username)
|
||||
.withPassword(bindingConfig.password).withLogging(logger.isDebugEnabled());
|
||||
cloud = SleepIQ.create(cloudConfig);
|
||||
cloud = SleepIQ.create(cloudConfig, clientBuilder);
|
||||
|
||||
logger.debug("Authenticating at the SleepIQ cloud service");
|
||||
cloud.login();
|
||||
|
|
Loading…
Reference in New Issue