package com.epam.wilma.webapp.service.external;
/*==========================================================================
Copyright 2013-2017 EPAM Systems
This file is part of Wilma.
Wilma is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Wilma is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Wilma. If not, see <http://www.gnu.org/licenses/>.
===========================================================================*/
import com.epam.wilma.domain.stubconfig.StubDescriptor;
import com.epam.wilma.domain.stubconfig.interceptor.InterceptorDescriptor;
import com.epam.wilma.domain.stubconfig.interceptor.RequestInterceptor;
import com.epam.wilma.domain.stubconfig.interceptor.ResponseInterceptor;
import com.epam.wilma.router.RoutingService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class that coordinates the activities of the external REST services.
*
* @author Tamas_Kohegyi
*/
@Component
public class ServiceMap {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceMap.class);
private final Object o = new Object();
@Autowired
private RoutingService routingService;
private Map<String, ExternalWilmaService> serviceMap = new ConcurrentHashMap<>();
/**
* Method to call the proper registered external service, based on the request URI.
* Expected URL: /public/service/requestedService (/ without query!)
* In requestedService, it is recommended to add the getClass().getSimpleName() as first entity identifier, like:
* ShortCircuitInterceptor/circuits. In this case this URL will call the service:
* http://localhost:1234/config/public/service/ShortCircuitInterceptor/circuits
*
* @param req is the original request
* @param requestedService is the request for the service
* @param resp is the response, default status of the response is SC_OK (200), and response type is application/json
* @return with the body of the response (a JSON response)
*/
public String callExternalService(final HttpServletRequest req, final String requestedService, HttpServletResponse resp) {
ExternalWilmaService service;
synchronized (o) {
service = serviceMap.get(requestedService);
if (service == null) {
//this part allows to use requestService/* type calls
for (String key : serviceMap.keySet()) {
String offeredServicePattern = key + "/";
if (requestedService.startsWith(offeredServicePattern)) {
service = serviceMap.get(key);
break;
}
}
}
}
String response = null;
if (service != null) {
//we found the service class that should be called, so call it
try {
response = service.handleRequest(req, requestedService, resp);
} catch (Exception e) {
logError(service, requestedService, e);
}
}
return response;
}
private void logError(final ExternalWilmaService service, final String requestedService, final Exception e) {
LOGGER.error("Error during call to external service: " + service.getClass().getCanonicalName()
+ " with requested service: \"" + requestedService
+ "\"! Reason:" + e.getMessage(), e);
}
/**
* Method that collects the available external service entry points. Right now only interceptors can be used for this purpose.
*/
public void detectServices() {
Map<String, ExternalWilmaService> newServiceMap = new ConcurrentHashMap<>();
Map<String, StubDescriptor> descriptors = routingService.getStubDescriptors();
for (String key : descriptors.keySet()) {
StubDescriptor stubDescriptor = descriptors.get(key);
if (stubDescriptor != null) {
for (InterceptorDescriptor interceptorDescriptor : stubDescriptor.getInterceptorDescriptors()) {
RequestInterceptor requestInterceptor = interceptorDescriptor.getRequestInterceptor();
if (requestInterceptor instanceof ExternalWilmaService) {
ExternalWilmaService service = (ExternalWilmaService) requestInterceptor;
for (String handler : service.getHandlers()) {
newServiceMap.putIfAbsent(handler, service);
}
}
ResponseInterceptor responseInterceptor = interceptorDescriptor.getResponseInterceptor();
if (responseInterceptor instanceof ExternalWilmaService) {
ExternalWilmaService service = (ExternalWilmaService) responseInterceptor;
for (String handler : service.getHandlers()) {
newServiceMap.putIfAbsent(handler, service);
}
}
}
}
}
//new service map created
synchronized (o) {
serviceMap.clear();
serviceMap = newServiceMap;
}
}
/**
* Method that generates the list of the registered services in JSON format.
*
* @return with the response body
*/
public String getMapAsResponse() {
StringBuilder response = new StringBuilder("{\n \"serviceMap\": [\n");
if (!serviceMap.isEmpty()) {
synchronized (o) {
String[] keySet = serviceMap.keySet().toArray(new String[serviceMap.size()]);
for (int i = 0; i < keySet.length; i++) {
String entryKey = keySet[i];
response.append(" { \"").append(entryKey).append("\": \"")
.append(serviceMap.get(entryKey).getClass().getCanonicalName()).append("\" }");
if (i < keySet.length - 1) {
response.append(",");
}
response.append("\n");
}
}
}
response.append(" ]\n}\n");
return response.toString();
}
}