44import java .net .URI ;
55import java .net .URISyntaxException ;
66import java .security .GeneralSecurityException ;
7+ import java .time .Duration ;
78import java .util .List ;
89
910import javax .net .ssl .SSLContext ;
1819import org .apache .hc .client5 .http .entity .mime .MultipartEntityBuilder ;
1920import org .apache .hc .client5 .http .impl .auth .BasicScheme ;
2021import org .apache .hc .client5 .http .impl .classic .CloseableHttpClient ;
22+ import org .apache .hc .client5 .http .impl .classic .CloseableHttpResponse ;
2123import org .apache .hc .client5 .http .impl .classic .HttpClientBuilder ;
2224import org .apache .hc .client5 .http .impl .classic .HttpClients ;
2325import org .apache .hc .client5 .http .impl .io .PoolingHttpClientConnectionManagerBuilder ;
2830import org .apache .hc .client5 .http .ssl .SSLConnectionSocketFactoryBuilder ;
2931import org .apache .hc .client5 .http .ssl .TrustAllStrategy ;
3032import org .apache .hc .core5 .http .ClassicHttpRequest ;
31- import org .apache .hc .core5 .http .ClassicHttpResponse ;
3233import org .apache .hc .core5 .http .ContentType ;
3334import org .apache .hc .core5 .http .HttpHeaders ;
3435import org .apache .hc .core5 .http .HttpHost ;
3738import org .apache .hc .core5 .http .io .entity .EntityUtils ;
3839import org .apache .hc .core5 .http .io .entity .StringEntity ;
3940import org .apache .hc .core5 .ssl .SSLContexts ;
41+ import org .awaitility .Awaitility ;
4042import org .slf4j .Logger ;
4143import org .slf4j .LoggerFactory ;
4244
5355import com .redis .enterprise .rest .Module ;
5456import com .redis .enterprise .rest .ModuleInstallResponse ;
5557
56- public class Admin {
58+ public class Admin implements AutoCloseable {
5759
5860 private static final Logger log = LoggerFactory .getLogger (Admin .class );
5961
60- public static final Object CONTENT_TYPE_JSON = "application/json" ;
62+ public static final String CONTENT_TYPE_JSON = "application/json" ;
6163 public static final String V1 = "/v1/" ;
6264 public static final String V2 = "/v2/" ;
6365 public static final String DEFAULT_PROTOCOL = "https" ;
@@ -73,12 +75,26 @@ public class Admin {
7375 private final ObjectMapper objectMapper = new ObjectMapper ()
7476 .configure (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES , false );
7577 private final UsernamePasswordCredentials credentials ;
78+ private final CloseableHttpClient client ;
7679 private String protocol = DEFAULT_PROTOCOL ;
7780 private String host = DEFAULT_HOST ;
7881 private int port = DEFAULT_PORT ;
7982
80- public Admin (String userName , final char [] password ) {
83+ public Admin (String userName , final char [] password ) throws GeneralSecurityException {
8184 this .credentials = new UsernamePasswordCredentials (userName , password );
85+ SSLContext sslcontext = SSLContexts .custom ().loadTrustMaterial (new TrustAllStrategy ()).build ();
86+ SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder .create ()
87+ .setSslContext (sslcontext ).setHostnameVerifier (NoopHostnameVerifier .INSTANCE ).build ();
88+ HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder .create ()
89+ .setSSLSocketFactory (sslSocketFactory ).build ();
90+ HttpClientBuilder clientBuilder = HttpClients .custom ();
91+ clientBuilder .setConnectionManager (cm );
92+ this .client = clientBuilder .build ();
93+ }
94+
95+ @ Override
96+ public void close () throws Exception {
97+ client .close ();
8298 }
8399
84100 public void setHost (String host ) {
@@ -113,97 +129,95 @@ private URI uri(String path) {
113129 }
114130 }
115131
116- private <T > T get (String path , Class <T > type ) throws ParseException , GeneralSecurityException , IOException {
132+ private <T > T get (String path , Class <T > type ) throws ParseException , IOException {
117133 return get (path , SimpleType .constructUnsafe (type ));
118134 }
119135
120- private <T > T get (String path , JavaType type ) throws ParseException , GeneralSecurityException , IOException {
121- return read (new HttpGet (uri (path )), type );
136+ private <T > T get (String path , JavaType type ) throws ParseException , IOException {
137+ return read (new HttpGet (uri (path )), type , HttpStatus . SC_OK );
122138 }
123139
124- private <T > T post (String path , Object request , Class <T > responseType )
125- throws ParseException , GeneralSecurityException , IOException {
126- return post (path , request , SimpleType .constructUnsafe (responseType ));
140+ private <T > T delete (String path , Class <T > type ) throws ParseException , IOException {
141+ return delete (path , SimpleType .constructUnsafe (type ));
142+ }
143+
144+ private <T > T delete (String path , JavaType type ) throws ParseException , IOException {
145+ return read (new HttpDelete (uri (path )), type , HttpStatus .SC_OK );
127146 }
128147
129- private ClassicHttpResponse delete (String path ) throws GeneralSecurityException , IOException {
130- return execute ( new HttpDelete ( uri ( path ) ));
148+ private < T > T post (String path , Object request , Class < T > responseType ) throws ParseException , IOException {
149+ return post ( path , request , SimpleType . constructUnsafe ( responseType ));
131150 }
132151
133- private <T > T post (String path , Object request , JavaType responseType )
134- throws ParseException , GeneralSecurityException , IOException {
152+ private <T > T post (String path , Object request , JavaType responseType ) throws ParseException , IOException {
135153 HttpPost post = new HttpPost (uri (path ));
136154 String json = objectMapper .writeValueAsString (request );
137155 post .setEntity (new StringEntity (json ));
138- log .info ("POST {}" , json );
139- return read (post , responseType );
140- }
141-
142- private <T > T read (ClassicHttpRequest request , JavaType type )
143- throws ParseException , GeneralSecurityException , IOException {
144- request .setHeader (HttpHeaders .CONTENT_TYPE , CONTENT_TYPE_JSON );
145- return read (request , type , HttpStatus .SC_OK );
156+ return read (post , responseType , HttpStatus .SC_OK );
146157 }
147158
148- private <T > T read (ClassicHttpRequest request , Class <T > type , int successCode )
149- throws ParseException , GeneralSecurityException , IOException {
159+ private <T > T read (ClassicHttpRequest request , Class <T > type , int successCode ) throws ParseException , IOException {
150160 return read (request , SimpleType .constructUnsafe (type ), successCode );
151161 }
152162
153- private <T > T read (ClassicHttpRequest request , JavaType type , int successCode )
154- throws GeneralSecurityException , IOException , ParseException {
155- ClassicHttpResponse response = execute (request );
163+ private <T > T read (ClassicHttpRequest request , JavaType type , int successCode ) throws IOException , ParseException {
164+ request .setHeader (HttpHeaders .CONTENT_TYPE , CONTENT_TYPE_JSON );
165+ HttpHost target = new HttpHost (protocol , host , port );
166+ HttpClientContext localContext = HttpClientContext .create ();
167+ if (credentials != null ) {
168+ BasicScheme basicAuth = new BasicScheme ();
169+ basicAuth .initPreemptive (credentials );
170+ localContext .resetAuthExchange (target , basicAuth );
171+ }
172+ CloseableHttpResponse response = client .execute (request , localContext );
156173 if (response .getCode () == successCode ) {
157174 return objectMapper .readValue (EntityUtils .toString (response .getEntity ()), type );
158175 }
159176 throw new HttpResponseException (response .getCode (), response .getReasonPhrase ());
160177 }
161178
162- private ClassicHttpResponse execute (ClassicHttpRequest request ) throws GeneralSecurityException , IOException {
163- SSLContext sslcontext = SSLContexts .custom ().loadTrustMaterial (new TrustAllStrategy ()).build ();
164- SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder .create ()
165- .setSslContext (sslcontext ).setHostnameVerifier (NoopHostnameVerifier .INSTANCE ).build ();
166- HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder .create ()
167- .setSSLSocketFactory (sslSocketFactory ).build ();
168- HttpClientBuilder clientBuilder = HttpClients .custom ();
169- clientBuilder .setConnectionManager (cm );
170- try (CloseableHttpClient client = clientBuilder .build ()) {
171- HttpHost target = new HttpHost (protocol , host , port );
172- HttpClientContext localContext = HttpClientContext .create ();
173- if (credentials != null ) {
174- BasicScheme basicAuth = new BasicScheme ();
175- basicAuth .initPreemptive (credentials );
176- localContext .resetAuthExchange (target , basicAuth );
177- }
178- return client .execute (request , localContext );
179- }
180- }
181-
182- public List <Module > getModules () throws ParseException , GeneralSecurityException , IOException {
179+ public List <Module > getModules () throws ParseException , IOException {
183180 CollectionType type = objectMapper .getTypeFactory ().constructCollectionType (List .class , Module .class );
184181 return get (v1 (MODULES ), type );
185182 }
186183
187- public Database createDatabase (Database database )
188- throws ParseException , GeneralSecurityException , IOException {
189- return post (v1 (BDBS ), database , Database .class );
184+ public Database createDatabase (Database database ) throws ParseException , IOException {
185+ Database response = post (v1 (BDBS ), database , Database .class );
186+ long uid = response .getUid ();
187+ Awaitility .await ().until (() -> {
188+ Command command = new Command ();
189+ command .setCommand ("PING" );
190+ try {
191+ return executeCommand (uid , command ).getResponse ().asBoolean ();
192+ } catch (HttpResponseException e ) {
193+ log .info ("PING unsuccessful, retrying..." );
194+ return false ;
195+ }
196+ });
197+ return response ;
190198 }
191199
192- public List <Database > getDatabases () throws ParseException , GeneralSecurityException , IOException {
200+ public List <Database > getDatabases () throws ParseException , IOException {
193201 CollectionType type = objectMapper .getTypeFactory ().constructCollectionType (List .class , Database .class );
194202 return get (v1 (BDBS ), type );
195203 }
196204
197- public void deleteDatabase (long uid ) throws GeneralSecurityException , IOException {
198- ClassicHttpResponse response = delete (v1 (BDBS , String .valueOf (uid )));
199- if (response .getCode () != HttpStatus .SC_OK ) {
200- throw new HttpResponseException (response .getCode (), response .getReasonPhrase ());
201- }
202- log .info ("Deleted database {}" , uid );
205+ public void deleteDatabase (long uid ) {
206+ Awaitility .await ().timeout (Duration .ofSeconds (30 )).pollInterval (Duration .ofSeconds (1 )).until (() -> {
207+ try {
208+ delete (v1 (BDBS , String .valueOf (uid )), Database .class );
209+ return true ;
210+ } catch (HttpResponseException e ) {
211+ if (e .getStatusCode () == HttpStatus .SC_CONFLICT ) {
212+ log .info ("Could not delete database {}, retrying..." , uid );
213+ return false ;
214+ }
215+ throw e ;
216+ }
217+ });
203218 }
204219
205- public ModuleInstallResponse installModule (String filename , byte [] bytes )
206- throws ParseException , GeneralSecurityException , IOException {
220+ public ModuleInstallResponse installModule (String filename , byte [] bytes ) throws ParseException , IOException {
207221 HttpPost post = new HttpPost (uri (v2 (MODULES )));
208222 MultipartEntityBuilder builder = MultipartEntityBuilder .create ();
209223 builder .setMode (HttpMultipartMode .STRICT );
@@ -212,16 +226,15 @@ public ModuleInstallResponse installModule(String filename, byte[] bytes)
212226 return read (post , ModuleInstallResponse .class , HttpStatus .SC_ACCEPTED );
213227 }
214228
215- public Bootstrap getBootstrap () throws ParseException , GeneralSecurityException , IOException {
229+ public Bootstrap getBootstrap () throws ParseException , IOException {
216230 return get (v1 (BOOTSTRAP ), Bootstrap .class );
217231 }
218232
219- public Action getAction (String uid ) throws ParseException , GeneralSecurityException , IOException {
233+ public Action getAction (String uid ) throws ParseException , IOException {
220234 return get (v1 (ACTIONS , uid ), Action .class );
221235 }
222236
223- public CommandResponse executeCommand (long bdb , Command command )
224- throws ParseException , GeneralSecurityException , IOException {
237+ public CommandResponse executeCommand (long bdb , Command command ) throws ParseException , IOException {
225238 return post (v1 (BDBS , String .valueOf (bdb ), COMMAND ), command , CommandResponse .class );
226239 }
227240
0 commit comments