diff --git a/services/tools.descartes.teastore.persistence/src/main/java/tools/descartes/teastore/persistence/repository/EMFManager.java b/services/tools.descartes.teastore.persistence/src/main/java/tools/descartes/teastore/persistence/repository/EMFManager.java index 3288e0aac..30bf4dd57 100644 Binary files a/services/tools.descartes.teastore.persistence/src/main/java/tools/descartes/teastore/persistence/repository/EMFManager.java and b/services/tools.descartes.teastore.persistence/src/main/java/tools/descartes/teastore/persistence/repository/EMFManager.java differ diff --git a/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/algorithm/RecommenderSelector.java b/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/algorithm/RecommenderSelector.java index a02208b45..95ee388a1 100644 --- a/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/algorithm/RecommenderSelector.java +++ b/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/algorithm/RecommenderSelector.java @@ -1,184 +1,184 @@ -/** - * 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. - */ -package tools.descartes.teastore.recommender.algorithm; - -import java.lang.reflect.InvocationTargetException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.naming.InitialContext; -import javax.naming.NamingException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import tools.descartes.teastore.recommender.algorithm.impl.UseFallBackException; -import tools.descartes.teastore.recommender.algorithm.impl.cf.PreprocessedSlopeOneRecommender; -import tools.descartes.teastore.recommender.algorithm.impl.cf.SlopeOneRecommender; -import tools.descartes.teastore.recommender.algorithm.impl.orderbased.OrderBasedRecommender; -import tools.descartes.teastore.recommender.algorithm.impl.pop.PopularityBasedRecommender; -import tools.descartes.teastore.entities.Order; -import tools.descartes.teastore.entities.OrderItem; - -/** - * A strategy selector for the Recommender functionality. - * - * @author Johannes Grohmann - * - */ -public final class RecommenderSelector implements IRecommender { - - /** - * This map lists all currently available recommending approaches and assigns - * them their "name" for the environment variable. - */ - private static Map> recommenders = new HashMap<>(); - - static { - recommenders = new HashMap>(); - recommenders.put("Popularity", PopularityBasedRecommender.class); - recommenders.put("SlopeOne", SlopeOneRecommender.class); - recommenders.put("PreprocessedSlopeOne", PreprocessedSlopeOneRecommender.class); - recommenders.put("OrderBased", OrderBasedRecommender.class); - } - - /** - * The default recommender to choose, if no other recommender was set. - */ - private static final Class DEFAULT_RECOMMENDER = SlopeOneRecommender.class; - - private static final Logger LOG = LoggerFactory.getLogger(RecommenderSelector.class); - - private static RecommenderSelector instance; - - private IRecommender fallbackrecommender; - - private IRecommender recommender; - - /** - * Private Constructor. - */ - private RecommenderSelector() { - fallbackrecommender = new PopularityBasedRecommender(); - try { - String recommendername = (String) new InitialContext().lookup("java:comp/env/recommenderAlgorithm"); - // if a specific algorithm is set, we can use that algorithm - if (recommenders.containsKey(recommendername)) { - try { - recommender = recommenders.get(recommendername).getDeclaredConstructor().newInstance(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (SecurityException e) { - e.printStackTrace(); - } - } else { - LOG.warn("Recommendername: " + recommendername - + " was not found. Using default recommender (SlopeOneRecommeder)."); - try { - recommender = DEFAULT_RECOMMENDER.getDeclaredConstructor().newInstance(); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (SecurityException e) { - e.printStackTrace(); - } - } - } catch (InstantiationException | IllegalAccessException e) { - // if creating a new instance fails - e.printStackTrace(); - LOG.warn("Could not create an instance of the requested recommender. Using fallback."); - recommender = fallbackrecommender; - } catch (NamingException e) { - // if nothing was set - LOG.info("Recommender not set. Using default recommender (SlopeOneRecommeder)."); - try { - try { - recommender = DEFAULT_RECOMMENDER.getDeclaredConstructor().newInstance(); - } catch (IllegalArgumentException e1) { - e1.printStackTrace(); - } catch (InvocationTargetException e1) { - e1.printStackTrace(); - } catch (NoSuchMethodException e1) { - e1.printStackTrace(); - } catch (SecurityException e1) { - e1.printStackTrace(); - } - } catch (InstantiationException | IllegalAccessException e1) { - // also the default algorithm could fail - e1.printStackTrace(); - LOG.warn("Could not create an instance of DEFAULT_RECOMMENDER " + DEFAULT_RECOMMENDER.getName() + "."); - recommender = fallbackrecommender; - } - } - } - - @Override - public List recommendProducts(Long userid, List currentItems) - throws UnsupportedOperationException { - try { - return recommender.recommendProducts(userid, currentItems); - } catch (UseFallBackException e) { - // a UseFallBackException is usually ignored (as it is conceptual and might - // occur quite often) - LOG.trace("Executing " + recommender.getClass().getName() - + " as recommender failed. Using fallback recommender. Reason:\n" + e.getMessage()); - return fallbackrecommender.recommendProducts(userid, currentItems); - } catch (UnsupportedOperationException e) { - // if algorithm is not yet trained, we throw the error - LOG.error("Executing " + recommender.getClass().getName() - + " threw an UnsupportedOperationException. The recommender was not finished with training."); - throw e; - } catch (Exception e) { - // any other exception is just reported - LOG.warn("Executing " + recommender.getClass().getName() - + " threw an unexpected error. Using fallback recommender. Reason:\n" + e.getMessage()); - return fallbackrecommender.recommendProducts(userid, currentItems); - } - } - - /** - * Returns the instance of this Singleton or creates a new one, if this is the - * first call of this method. - * - * @return The instance of this class. - */ - public static synchronized RecommenderSelector getInstance() { - if (instance == null) { - instance = new RecommenderSelector(); - } - return instance; - } - - /* - * (non-Javadoc) - * - * @see - * tools.descartes.teastore.recommender.IRecommender#train(java.util.List, - * java.util.List) - */ - @Override - public void train(List orderItems, List orders) { - recommender.train(orderItems, orders); - fallbackrecommender.train(orderItems, orders); - } - -} +/** + * 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. + */ +package tools.descartes.teastore.recommender.algorithm; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.naming.InitialContext; +import javax.naming.NamingException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import tools.descartes.teastore.recommender.algorithm.impl.UseFallBackException; +import tools.descartes.teastore.recommender.algorithm.impl.cf.PreprocessedSlopeOneRecommender; +import tools.descartes.teastore.recommender.algorithm.impl.cf.SlopeOneRecommender; +import tools.descartes.teastore.recommender.algorithm.impl.orderbased.OrderBasedRecommender; +import tools.descartes.teastore.recommender.algorithm.impl.pop.PopularityBasedRecommender; +import tools.descartes.teastore.entities.Order; +import tools.descartes.teastore.entities.OrderItem; + +/** + * A strategy selector for the Recommender functionality. + * + * @author Johannes Grohmann + * + */ +public final class RecommenderSelector implements IRecommender { + + /** + * This map lists all currently available recommending approaches and assigns + * them their "name" for the environment variable. + */ + private static Map> recommenders = new HashMap<>(); + + static { + recommenders = new HashMap>(); + recommenders.put("Popularity", PopularityBasedRecommender.class); + recommenders.put("SlopeOne", SlopeOneRecommender.class); + recommenders.put("PreprocessedSlopeOne", PreprocessedSlopeOneRecommender.class); + recommenders.put("OrderBased", OrderBasedRecommender.class); + } + + /** + * The default recommender to choose, if no other recommender was set. + */ + private static final Class DEFAULT_RECOMMENDER = SlopeOneRecommender.class; + + private static final Logger LOG = LoggerFactory.getLogger(RecommenderSelector.class); + + private IRecommender fallbackrecommender; + + private IRecommender recommender; + + /** + * Private Constructor. + */ + private RecommenderSelector() { + fallbackrecommender = new PopularityBasedRecommender(); + try { + String recommendername = (String) new InitialContext().lookup("java:comp/env/recommenderAlgorithm"); + // if a specific algorithm is set, we can use that algorithm + if (recommenders.containsKey(recommendername)) { + try { + recommender = recommenders.get(recommendername).getDeclaredConstructor().newInstance(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + } else { + LOG.warn("Recommendername: " + recommendername + + " was not found. Using default recommender (SlopeOneRecommeder)."); + try { + recommender = DEFAULT_RECOMMENDER.getDeclaredConstructor().newInstance(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } + } + } catch (InstantiationException | IllegalAccessException e) { + // if creating a new instance fails + e.printStackTrace(); + LOG.warn("Could not create an instance of the requested recommender. Using fallback."); + recommender = fallbackrecommender; + } catch (NamingException e) { + // if nothing was set + LOG.info("Recommender not set. Using default recommender (SlopeOneRecommeder)."); + try { + try { + recommender = DEFAULT_RECOMMENDER.getDeclaredConstructor().newInstance(); + } catch (IllegalArgumentException e1) { + e1.printStackTrace(); + } catch (InvocationTargetException e1) { + e1.printStackTrace(); + } catch (NoSuchMethodException e1) { + e1.printStackTrace(); + } catch (SecurityException e1) { + e1.printStackTrace(); + } + } catch (InstantiationException | IllegalAccessException e1) { + // also the default algorithm could fail + e1.printStackTrace(); + LOG.warn("Could not create an instance of DEFAULT_RECOMMENDER " + DEFAULT_RECOMMENDER.getName() + "."); + recommender = fallbackrecommender; + } + } + } + + @Override + public List recommendProducts(Long userid, List currentItems) + throws UnsupportedOperationException { + try { + return recommender.recommendProducts(userid, currentItems); + } catch (UseFallBackException e) { + // a UseFallBackException is usually ignored (as it is conceptual and might + // occur quite often) + LOG.trace("Executing " + recommender.getClass().getName() + + " as recommender failed. Using fallback recommender. Reason:\n" + e.getMessage()); + return fallbackrecommender.recommendProducts(userid, currentItems); + } catch (UnsupportedOperationException e) { + // if algorithm is not yet trained, we throw the error + LOG.error("Executing " + recommender.getClass().getName() + + " threw an UnsupportedOperationException. The recommender was not finished with training."); + throw e; + } catch (Exception e) { + // any other exception is just reported + LOG.warn("Executing " + recommender.getClass().getName() + + " threw an unexpected error. Using fallback recommender. Reason:\n" + e.getMessage()); + return fallbackrecommender.recommendProducts(userid, currentItems); + } + } + + /** + * Returns the instance of this Singleton. + * + * Uses the initialization-on-demand holder idiom to avoid synchronizing on every call. + * + * @return The instance of this class. + */ + public static RecommenderSelector getInstance() { + return Holder.INSTANCE; + } + + private static final class Holder { + private static final RecommenderSelector INSTANCE = new RecommenderSelector(); + } + + /* + * (non-Javadoc) + * + * @see + * tools.descartes.teastore.recommender.IRecommender#train(java.util.List, + * java.util.List) + */ + @Override + public void train(List orderItems, List orders) { + recommender.train(orderItems, orders); + fallbackrecommender.train(orderItems, orders); + } + +} diff --git a/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/servlet/TrainingSynchronizer.java b/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/servlet/TrainingSynchronizer.java index 58cb7feb1..8842110e0 100644 --- a/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/servlet/TrainingSynchronizer.java +++ b/services/tools.descartes.teastore.recommender/src/main/java/tools/descartes/teastore/recommender/servlet/TrainingSynchronizer.java @@ -19,10 +19,11 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; -import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; @@ -97,6 +98,8 @@ public static synchronized TrainingSynchronizer getInstance() { private static final Logger LOG = LoggerFactory.getLogger(TrainingSynchronizer.class); + private static final ZoneId SYSTEM_ZONE = ZoneId.systemDefault(); + /** * The maximum considered time in milliseconds. DEFAULT_MAX_TIME_VALUE signals * no entry, e.g. all orders are used for training. @@ -139,8 +142,8 @@ private void waitForPersistence() { client -> client.getService().path(client.getApplicationURI()).path(client.getEndpointURI()) .path("finished").request().get()); - if (result != null && Boolean.parseBoolean(result.readEntity(String.class))) { - break; + if (result != null && Boolean.parseBoolean(result.readEntity(String.class))) { + break; } } catch (NullPointerException | NotFoundException | LoadBalancerTimeoutException e) { // continue waiting as usual @@ -218,18 +221,24 @@ private void filterLists(List orderItems, List orders) { for (Response response : maxTimeResponses) { if (response == null) { LOG.warn("One service response was null and is therefore not available for time-check."); - } else if (response.getStatus() == Response.Status.OK.getStatusCode()) { - // only consider if status was fine - long milliTS = response.readEntity(Long.class); - if (maxTime != TrainingSynchronizer.DEFAULT_MAX_TIME_VALUE && maxTime != milliTS) { - LOG.warn("Services disagree about timestamp: " + maxTime + " vs " + milliTS - + ". Therfore using the minimum."); + continue; + } + try { + if (response.getStatus() == Response.Status.OK.getStatusCode()) { + // only consider if status was fine + long milliTS = response.readEntity(Long.class); + if (maxTime != TrainingSynchronizer.DEFAULT_MAX_TIME_VALUE && maxTime != milliTS) { + LOG.warn("Services disagree about timestamp: " + maxTime + " vs " + milliTS + + ". Therfore using the minimum."); + } + maxTime = Math.min(maxTime, milliTS); + } else { + // release connection by buffering entity + response.bufferEntity(); + LOG.warn("Service " + response + "was not available for time-check."); } - maxTime = Math.min(maxTime, milliTS); - } else { - // release connection by buffering entity - response.bufferEntity(); - LOG.warn("Service " + response + "was not available for time-check."); + } finally { + response.close(); } } if (maxTime == Long.MIN_VALUE) { @@ -243,34 +252,22 @@ private void filterLists(List orderItems, List orders) { } private void filterForMaxtimeStamp(List orderItems, List orders) { - // filter orderItems and orders and ignore newer entries. - List remove = new ArrayList<>(); + // Filter orders (in place) and build set of remaining order IDs. + orders.removeIf(or -> toMillis(or.getTime()) > maxTime); + + Set validOrderIds = new HashSet<>(Math.max(16, (int) (orders.size() / 0.75f) + 1)); for (Order or : orders) { - if (toMillis(or.getTime()) > maxTime) { - remove.add(or); - } + validOrderIds.add(or.getId()); } - orders.removeAll(remove); - List removeItems = new ArrayList<>(); - for (OrderItem orderItem : orderItems) { - boolean contained = false; - for (Order or : orders) { - if (or.getId() == orderItem.getOrderId()) { - contained = true; - } - } - if (!contained) { - removeItems.add(orderItem); - } - } - orderItems.removeAll(removeItems); + // Filter orderItems (in place) based on the orderId set. + orderItems.removeIf(orderItem -> !validOrderIds.contains(orderItem.getOrderId())); } private long toMillis(String date) { TemporalAccessor temporalAccessor = DateTimeFormatter.ISO_LOCAL_DATE_TIME.parse(date); LocalDateTime localDateTime = LocalDateTime.from(temporalAccessor); - ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.systemDefault()); + ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, SYSTEM_ZONE); Instant instant = Instant.from(zonedDateTime); return instant.toEpochMilli(); } diff --git a/services/tools.descartes.teastore.webui/src/main/java/tools/descartes/teastore/webui/servlet/AbstractUIServlet.java b/services/tools.descartes.teastore.webui/src/main/java/tools/descartes/teastore/webui/servlet/AbstractUIServlet.java index d39af623d..f144bdc44 100644 --- a/services/tools.descartes.teastore.webui/src/main/java/tools/descartes/teastore/webui/servlet/AbstractUIServlet.java +++ b/services/tools.descartes.teastore.webui/src/main/java/tools/descartes/teastore/webui/servlet/AbstractUIServlet.java @@ -45,6 +45,13 @@ public abstract class AbstractUIServlet extends HttpServlet { private static final long serialVersionUID = 1L; + + /** + * Jackson ObjectMapper is thread-safe after configuration and expensive to + * instantiate. Reuse a single instance to reduce per-request allocations/CPU. + */ + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private static final String UTF_8 = "UTF-8"; /** * Text for message cookie. */ @@ -102,12 +109,12 @@ protected SessionBlob getSessionBlob(HttpServletRequest request) { if (request.getCookies() != null) { for (Cookie cook : request.getCookies()) { if (cook.getName().equals(BLOB)) { - ObjectMapper o = new ObjectMapper(); try { - SessionBlob blob = o.readValue(URLDecoder.decode(cook.getValue(), "UTF-8"), SessionBlob.class); + SessionBlob blob = OBJECT_MAPPER.readValue(URLDecoder.decode(cook.getValue(), UTF_8), + SessionBlob.class); if (blob != null) { - return blob; - } + return blob; + } } catch (IOException e) { throw new IllegalStateException("Cookie corrupted!"); } @@ -125,9 +132,8 @@ protected SessionBlob getSessionBlob(HttpServletRequest request) { * @param response servlet response */ protected void saveSessionBlob(SessionBlob blob, HttpServletResponse response) { - ObjectMapper o = new ObjectMapper(); try { - Cookie cookie = new Cookie(BLOB, URLEncoder.encode(o.writeValueAsString(blob), "UTF-8")); + Cookie cookie = new Cookie(BLOB, URLEncoder.encode(OBJECT_MAPPER.writeValueAsString(blob), UTF_8)); response.addCookie(cookie); } catch (JsonProcessingException | UnsupportedEncodingException e) { throw new IllegalStateException("Could not save blob!"); @@ -142,9 +148,8 @@ protected void saveSessionBlob(SessionBlob blob, HttpServletResponse response) { * @param response servlet response */ protected void destroySessionBlob(SessionBlob blob, HttpServletResponse response) { - ObjectMapper o = new ObjectMapper(); try { - Cookie cookie = new Cookie(BLOB, URLEncoder.encode(o.writeValueAsString(blob), "UTF-8")); + Cookie cookie = new Cookie(BLOB, URLEncoder.encode(OBJECT_MAPPER.writeValueAsString(blob), UTF_8)); cookie.setMaxAge(0); response.addCookie(cookie); } catch (JsonProcessingException | UnsupportedEncodingException e) { @@ -196,13 +201,13 @@ protected void checkforCookie(HttpServletRequest request, HttpServletResponse re if (request.getCookies() != null) { for (Cookie cook : request.getCookies()) { if (cook.getName().equals(MESSAGECOOKIE)) { - request.setAttribute("message", cook.getValue().replaceAll("_", " ")); + request.setAttribute("message", cook.getValue().replace('_', ' ')); cook.setMaxAge(0); response.addCookie(cook); } else if (cook.getName().equals(PRODUCTCOOKIE)) { request.setAttribute("numberProducts", cook.getValue()); } else if (cook.getName().equals(ERRORMESSAGECOOKIE)) { - request.setAttribute("errormessage", cook.getValue().replaceAll("_", " ")); + request.setAttribute("errormessage", cook.getValue().replace('_', ' ')); cook.setMaxAge(0); response.addCookie(cook); } diff --git a/utilities/tools.descartes.teastore.registryclient/src/main/java/tools/descartes/teastore/registryclient/util/RESTClient.java b/utilities/tools.descartes.teastore.registryclient/src/main/java/tools/descartes/teastore/registryclient/util/RESTClient.java index 874d7e3ea..72fec6898 100644 --- a/utilities/tools.descartes.teastore.registryclient/src/main/java/tools/descartes/teastore/registryclient/util/RESTClient.java +++ b/utilities/tools.descartes.teastore.registryclient/src/main/java/tools/descartes/teastore/registryclient/util/RESTClient.java @@ -17,11 +17,10 @@ import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider; +import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.HostnameVerifier; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; import jakarta.ws.rs.client.WebTarget; @@ -55,6 +54,41 @@ public class RESTClient { private static int readTimeout = DEFAULT_READ_TIMEOUT; private static int connectTimeout = DEFAULT_CONNECT_TIMEOUT; + /** + * When USE_HTTPS is enabled, TeaStore historically disabled all certificate and + * hostname verification. Keep behavior for compatibility, but avoid mutating JVM-wide + * HttpsURLConnection defaults (global mutable state). + */ + private static final HostnameVerifier TRUST_ALL_HOSTNAME_VERIFIER = (hostname, session) -> true; + private static final TrustManager[] TRUST_ALL_CERTS = new TrustManager[] { new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return null; + } + } }; + + private static final SSLContext TRUST_ALL_SSL_CONTEXT = createTrustAllSslContext(); + + private static SSLContext createTrustAllSslContext() { + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, TRUST_ALL_CERTS, new java.security.SecureRandom()); + return sslContext; + } catch (NoSuchAlgorithmException | KeyManagementException e) { + // Preserve legacy behavior: print stack trace and fall back. + e.printStackTrace(); + return null; + } + } + private String applicationURI; private String endpointURI; @@ -94,33 +128,12 @@ public RESTClient(String hostURL, String application, String endpoint, final Cla config.connectorProvider(new GrizzlyConnectorProvider()); if (useHTTPS) { - try { - TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() { - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) { - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) { - } - - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - } - }; - SSLContext sslContext = SSLContext.getInstance("SSL"); - sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); - HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); - HostnameVerifier allHostsValid = (hostname, session) -> true; - HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid); - - ClientBuilder builder = ClientBuilder.newBuilder().withConfig(config); - builder.sslContext(sslContext); - client = builder.build(); - } catch (NoSuchAlgorithmException | KeyManagementException e) { - e.printStackTrace(); + ClientBuilder builder = ClientBuilder.newBuilder().withConfig(config); + if (TRUST_ALL_SSL_CONTEXT != null) { + builder.sslContext(TRUST_ALL_SSL_CONTEXT); + builder.hostnameVerifier(TRUST_ALL_HOSTNAME_VERIFIER); } + client = builder.build(); } else { client = ClientBuilder.newClient(config); } @@ -131,19 +144,20 @@ public java.security.cert.X509Certificate[] getAcceptedIssuers() { this.entityClass = entityClass; parameterizedGenericType = new ParameterizedType() { - public Type[] getActualTypeArguments() { - return new Type[] { entityClass }; - } - - public Type getRawType() { - return List.class; - } - - public Type getOwnerType() { - return List.class; - } - }; - genericListType = new GenericType>(parameterizedGenericType) { }; + public Type[] getActualTypeArguments() { + return new Type[] { entityClass }; + } + + public Type getRawType() { + return List.class; + } + + public Type getOwnerType() { + return List.class; + } + }; + genericListType = new GenericType>(parameterizedGenericType) { + }; } /**