blob: 07d0ebfa4065e8a585b13199e4e03ea94e43741e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.server.internal;
import com.google.gson.Gson;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.sirius.server.api.ISiriusServerService;
import org.eclipse.sirius.server.api.SiriusServerPath;
import org.eclipse.sirius.server.api.SiriusServerResponse;
import org.eclipse.sirius.server.internal.services.SiriusServerCSPViolationReportService;
/**
* Filter used to dynamically dispatch request to the appropriate service.
*
* @author sbegaudeau
*/
public class SiriusServerFilter implements Filter {
/**
* The default character encoding.
*/
private static final String UTF_8 = "UTF-8"; //$NON-NLS-1$
/**
* {@inheritDoc}
*
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// Do nothing
}
/**
* {@inheritDoc}
*
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
if (req instanceof HttpServletRequest && resp instanceof HttpServletResponse) {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
Optional<SiriusServerResponse> optionalResponse = this.processRequest(request);
if (optionalResponse.isPresent()) {
SiriusServerResponse httpServiceResponse = optionalResponse.get();
response.setStatus(httpServiceResponse.getStatus());
response.setContentType(httpServiceResponse.getContentType());
response.setCharacterEncoding(UTF_8);
Optional<Object> optionalPayload = Optional.ofNullable(httpServiceResponse.getPayload());
Optional<String> optionalJsonPayload = optionalPayload.map(new Gson()::toJson);
optionalJsonPayload.ifPresent(response.getWriter()::write);
}
}
}
/**
* Process the given request by invoking the proper Sirius server service.
*
* @param request
* The request
* @return A proper response if a service supporting the given request has
* been found, an error response otherwise
*/
private Optional<SiriusServerResponse> processRequest(HttpServletRequest request) {
List<SiriusServerServiceDescriptor> descriptors = this.getDescriptors(request);
descriptors.sort((descriptor1, descriptor2) -> descriptor2.getResult().getVariables().size() - descriptor1.getResult().getVariables().size());
Optional<SiriusServerServiceDescriptor> optionalDescriptor = descriptors.stream().findFirst();
return optionalDescriptor.flatMap(descriptor -> {
Optional<ISiriusServerService> optionalServiceInstance = this.createServiceInstance(descriptor.getServiceClass());
Map<String, String> variables = descriptor.getResult().getVariables();
String remainingPart = descriptor.getResult().getRemainingPart();
return optionalServiceInstance.map(service -> service.process(request, variables, remainingPart));
});
}
/**
* Computes the best {@link SiriusServerServiceDescriptor} for the given
* request.
*
* @param request
* The request
* @return The list of {@link SiriusServerServiceDescriptor} which can
* handle the given request
*/
private List<SiriusServerServiceDescriptor> getDescriptors(HttpServletRequest request) {
List<Class<? extends ISiriusServerService>> serviceClasses = new ArrayList<>();
// Default services
serviceClasses.add(SiriusServerCSPViolationReportService.class);
// @formatter:off
SiriusServerPlugin.getPlugin().getSiriusServerServices().stream()
.map(ISiriusServerService::getClass)
.forEach(serviceClasses::add);
// @formatter:on
List<SiriusServerServiceDescriptor> descriptors = new ArrayList<>();
for (Class<? extends ISiriusServerService> serviceClass : serviceClasses) {
SiriusServerPath siriusServerPath = serviceClass.getAnnotation(SiriusServerPath.class);
String path = siriusServerPath.value();
SiriusServerPathMatcher matcher = new SiriusServerPathMatcher(path);
SiriusServerMatchResult result = matcher.match(request.getServletPath());
if (result.hasMatched()) {
descriptors.add(new SiriusServerServiceDescriptor(serviceClass, result));
}
}
return descriptors;
}
/**
* Creates the service instance from the given service class.
*
* @param serviceClass
* The service class
* @return The service instance created
*/
private Optional<ISiriusServerService> createServiceInstance(Class<? extends ISiriusServerService> serviceClass) {
try {
ISiriusServerService httpService = serviceClass.newInstance();
return Optional.of(httpService);
} catch (InstantiationException | IllegalAccessException exception) {
IStatus status = new Status(IStatus.ERROR, SiriusServerPlugin.PLUGIN_ID, exception.getMessage(), exception);
SiriusServerPlugin.getPlugin().log(status);
}
return Optional.empty();
}
/**
* {@inheritDoc}
*
* @see javax.servlet.Filter#destroy()
*/
@Override
public void destroy() {
// Do nothing
}
/**
* Descriptor containing both a service class and a match result.
*
* @author sbegaudeau
*/
private static class SiriusServerServiceDescriptor {
/**
* The service class.
*/
private Class<? extends ISiriusServerService> serviceClass;
/**
* The match result.
*/
private SiriusServerMatchResult result;
/**
* The constructor.
*
* @param serviceClass
* The service class
* @param matcher
* The match result
*/
SiriusServerServiceDescriptor(Class<? extends ISiriusServerService> serviceClass, SiriusServerMatchResult result) {
this.serviceClass = serviceClass;
this.result = result;
}
/**
* Return the serviceClass.
*
* @return the serviceClass
*/
public Class<? extends ISiriusServerService> getServiceClass() {
return this.serviceClass;
}
/**
* Return the match result.
*
* @return the match result
*/
public SiriusServerMatchResult getResult() {
return this.result;
}
}
}