]> git.basschouten.com Git - openhab-addons.git/blob
64db7f3b49a67d56863c783d9a5673f40421ad11
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.freeboxos.internal.api.rest;
14
15 import java.lang.reflect.Constructor;
16 import java.lang.reflect.InvocationTargetException;
17 import java.net.URI;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21
22 import javax.ws.rs.core.UriBuilder;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.eclipse.jdt.annotation.Nullable;
26 import org.eclipse.jetty.http.HttpMethod;
27 import org.openhab.binding.freeboxos.internal.api.ApiHandler;
28 import org.openhab.binding.freeboxos.internal.api.FreeboxException;
29 import org.openhab.binding.freeboxos.internal.api.PermissionException;
30 import org.openhab.binding.freeboxos.internal.api.Response;
31 import org.openhab.binding.freeboxos.internal.api.Response.ErrorCode;
32 import org.openhab.binding.freeboxos.internal.api.rest.LoginManager.Session;
33 import org.openhab.binding.freeboxos.internal.config.FreeboxOsConfiguration;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * The {@link FreeboxOsSession} is responsible for sending requests toward a given url and transform the answer in
39  * appropriate dto.
40  *
41  * @author GaĆ«l L'Hopital - Initial contribution
42  */
43 @NonNullByDefault
44 public class FreeboxOsSession {
45     private static final String API_VERSION_PATH = "api_version";
46
47     private final Logger logger = LoggerFactory.getLogger(FreeboxOsSession.class);
48     private final Map<Class<? extends RestManager>, RestManager> restManagers = new HashMap<>();
49     private final ApiHandler apiHandler;
50
51     private @NonNullByDefault({}) UriBuilder uriBuilder;
52     private @Nullable Session session;
53     private String appToken = "";
54     private int wsReconnectInterval;
55
56     public enum BoxModel {
57         FBXGW_R1_FULL, // Freebox Server (v6) revision 1
58         FBXGW_R2_FULL, // Freebox Server (v6) revision 2
59         FBXGW_R1_MINI, // Freebox Mini revision 1
60         FBXGW_R2_MINI, // Freebox Mini revision 2
61         FBXGW_R1_ONE, // Freebox One revision 1
62         FBXGW_R2_ONE, // Freebox One revision 2
63         FBXGW7_R1_FULL, // Freebox v7 revision 1
64         UNKNOWN
65     }
66
67     public static record ApiVersion(String apiBaseUrl, @Nullable String apiDomain, String apiVersion, BoxModel boxModel,
68             @Nullable String boxModelName, String deviceName, String deviceType, boolean httpsAvailable, int httpsPort,
69             String uid) {
70
71         /**
72          * @return a string like eg: '/api/v8'
73          */
74         private String baseUrl() {
75             return "%sv%s".formatted(apiBaseUrl, apiVersion.split("\\.")[0]);
76         }
77     }
78
79     public FreeboxOsSession(ApiHandler apiHandler) {
80         this.apiHandler = apiHandler;
81     }
82
83     public void initialize(FreeboxOsConfiguration config) throws FreeboxException, InterruptedException {
84         ApiVersion version = apiHandler.executeUri(config.getUriBuilder(API_VERSION_PATH).build(), HttpMethod.GET,
85                 ApiVersion.class, null, null);
86         this.uriBuilder = config.getUriBuilder(version.baseUrl());
87         this.wsReconnectInterval = config.wsReconnectInterval;
88         getManager(LoginManager.class);
89         getManager(NetShareManager.class);
90         getManager(LanManager.class);
91         getManager(WifiManager.class);
92         getManager(FreeplugManager.class);
93         getManager(AirMediaManager.class);
94     }
95
96     public void openSession(String appToken) throws FreeboxException {
97         Session newSession = getManager(LoginManager.class).openSession(appToken);
98         getManager(WebSocketManager.class).openSession(newSession.sessionToken(), wsReconnectInterval);
99         session = newSession;
100         this.appToken = appToken;
101     }
102
103     public String grant() throws FreeboxException {
104         return getManager(LoginManager.class).checkGrantStatus();
105     }
106
107     public void closeSession() {
108         Session currentSession = session;
109         if (currentSession != null) {
110             try {
111                 getManager(WebSocketManager.class).dispose();
112                 getManager(LoginManager.class).closeSession();
113                 session = null;
114             } catch (FreeboxException e) {
115                 logger.warn("Error closing session: {}", e.getMessage());
116             }
117         }
118         appToken = "";
119         restManagers.clear();
120     }
121
122     private synchronized <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
123             boolean retryAuth, int retryCount, @Nullable Object aPayload) throws FreeboxException {
124         try {
125             T response = apiHandler.executeUri(uri, method, clazz, getSessionToken(), aPayload);
126             if (response.getErrorCode() == ErrorCode.INTERNAL_ERROR && retryCount > 0) {
127                 return execute(uri, method, clazz, false, retryCount - 1, aPayload);
128             } else if (retryAuth && response.getErrorCode() == ErrorCode.AUTH_REQUIRED) {
129                 openSession(appToken);
130                 return execute(uri, method, clazz, false, retryCount, aPayload);
131             }
132             if (!response.isSuccess()) {
133                 throw new FreeboxException("Api request failed: %s", response.getMsg());
134             }
135             return response.getResult();
136         } catch (FreeboxException e) {
137             if (ErrorCode.AUTH_REQUIRED.equals(e.getErrorCode())) {
138                 openSession(appToken);
139                 return execute(uri, method, clazz, false, retryCount, aPayload);
140             }
141             throw e;
142         } catch (InterruptedException ignored) {
143             return List.of();
144         }
145     }
146
147     public <F, T extends Response<F>> List<F> execute(URI uri, HttpMethod method, Class<T> clazz,
148             @Nullable Object aPayload) throws FreeboxException {
149         return execute(uri, method, clazz, getSessionToken() != null, 3, aPayload);
150     }
151
152     @SuppressWarnings("unchecked")
153     public synchronized <T extends RestManager> T getManager(Class<T> clazz) throws FreeboxException {
154         RestManager manager = restManagers.get(clazz);
155         if (manager == null) {
156             try {
157                 Constructor<T> managerConstructor = clazz.getConstructor(FreeboxOsSession.class);
158                 manager = addManager(clazz, managerConstructor.newInstance(this));
159             } catch (InvocationTargetException e) {
160                 Throwable cause = e.getCause();
161                 if (cause instanceof PermissionException exception) {
162                     throw exception;
163                 }
164                 throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
165             } catch (ReflectiveOperationException e) {
166                 throw new FreeboxException(e, "Unable to call RestManager constructor for %s", clazz.getName());
167             }
168         }
169         return (T) manager;
170     }
171
172     public <T extends RestManager> T addManager(Class<T> clazz, T manager) {
173         restManagers.put(clazz, manager);
174         return manager;
175     }
176
177     boolean hasPermission(LoginManager.Permission required) {
178         Session currentSession = session;
179         return currentSession != null ? currentSession.hasPermission(required) : false;
180     }
181
182     private @Nullable String getSessionToken() {
183         Session currentSession = session;
184         return currentSession != null ? currentSession.sessionToken() : null;
185     }
186
187     public UriBuilder getUriBuilder() {
188         return uriBuilder.clone();
189     }
190
191     public ApiHandler getApiHandler() {
192         return apiHandler;
193     }
194 }