mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-02-23 06:50:49 +01:00
Merge
This commit is contained in:
20
scm-webapp/src/main/java/sonia/scm/ResteasyModule.java
Normal file
20
scm-webapp/src/main/java/sonia/scm/ResteasyModule.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package sonia.scm;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.inject.servlet.ServletModule;
|
||||
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
|
||||
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Map;
|
||||
|
||||
public class ResteasyModule extends ServletModule {
|
||||
|
||||
@Override
|
||||
protected void configureServlets() {
|
||||
bind(HttpServletDispatcher.class).in(Singleton.class);
|
||||
|
||||
Map<String, String> initParams = ImmutableMap.of(ResteasyContextParameters.RESTEASY_SERVLET_MAPPING_PREFIX, "/api");
|
||||
serve("/api/*").with(HttpServletDispatcher.class, initParams);
|
||||
}
|
||||
}
|
||||
@@ -126,6 +126,7 @@ public class ScmContextListener extends GuiceResteasyBootstrapServletContextList
|
||||
ClassOverrides overrides = ClassOverrides.findOverrides(pluginLoader.getUberClassLoader());
|
||||
List<Module> moduleList = Lists.newArrayList();
|
||||
|
||||
moduleList.add(new ResteasyModule());
|
||||
moduleList.add(new ScmInitializerModule());
|
||||
moduleList.add(new ScmEventBusModule());
|
||||
moduleList.add(new EagerSingletonModule());
|
||||
|
||||
@@ -57,5 +57,9 @@ public class TemplatingPushStateDispatcher implements PushStateDispatcher {
|
||||
return request.getContextPath();
|
||||
}
|
||||
|
||||
public String getLiveReloadURL() {
|
||||
return System.getProperty("livereload.url");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package sonia.scm.api;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.MDC;
|
||||
import sonia.scm.api.v2.resources.ErrorDto;
|
||||
import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
import java.util.Collections;
|
||||
|
||||
@Provider
|
||||
public class FallbackExceptionMapper implements ExceptionMapper<Exception> {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(FallbackExceptionMapper.class);
|
||||
|
||||
private static final String ERROR_CODE = "CmR8GCJb31";
|
||||
|
||||
private final ExceptionWithContextToErrorDtoMapper mapper;
|
||||
|
||||
@Inject
|
||||
public FallbackExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response toResponse(Exception exception) {
|
||||
logger.debug("map {} to status code 500", exception);
|
||||
ErrorDto errorDto = new ErrorDto();
|
||||
errorDto.setMessage("internal server error");
|
||||
errorDto.setContext(Collections.emptyList());
|
||||
errorDto.setErrorCode(ERROR_CODE);
|
||||
errorDto.setTransactionId(MDC.get("transaction_id"));
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(errorDto)
|
||||
.type(VndMediaType.ERROR_TYPE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public class ContextualExceptionMapper<E extends ExceptionWithContext> implement
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ContextualExceptionMapper.class);
|
||||
|
||||
private ExceptionWithContextToErrorDtoMapper mapper;
|
||||
private final ExceptionWithContextToErrorDtoMapper mapper;
|
||||
|
||||
private final Response.Status status;
|
||||
private final Class<E> type;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package sonia.scm.api.v2;
|
||||
|
||||
import sonia.scm.NotSupportedFeatureException;
|
||||
import sonia.scm.api.rest.ContextualExceptionMapper;
|
||||
import sonia.scm.api.v2.resources.ExceptionWithContextToErrorDtoMapper;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
@Provider
|
||||
public class NotSupportedFeatureExceptionMapper extends ContextualExceptionMapper<NotSupportedFeatureException> {
|
||||
@Inject
|
||||
public NotSupportedFeatureExceptionMapper(ExceptionWithContextToErrorDtoMapper mapper) {
|
||||
super(NotSupportedFeatureException.class, Response.Status.BAD_REQUEST, mapper);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@@ -94,8 +95,12 @@ public class ChangesetRootResource {
|
||||
.setStartChangeset(id)
|
||||
.setEndChangeset(id)
|
||||
.getChangesets();
|
||||
if (changesets != null && changesets.getChangesets() != null && changesets.getChangesets().size() == 1) {
|
||||
return Response.ok(changesetToChangesetDtoMapper.map(changesets.getChangesets().get(0), repository)).build();
|
||||
if (changesets != null && changesets.getChangesets() != null && !changesets.getChangesets().isEmpty()) {
|
||||
Optional<Changeset> changeset = changesets.getChangesets().stream().filter(ch -> ch.getId().equals(id)).findFirst();
|
||||
if (!changeset.isPresent()) {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
return Response.ok(changesetToChangesetDtoMapper.map(changeset.get(), repository)).build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public class ContentResource {
|
||||
private void appendContentHeader(String path, byte[] head, Response.ResponseBuilder responseBuilder) {
|
||||
ContentType contentType = ContentTypes.detect(path, head);
|
||||
responseBuilder.header("Content-Type", contentType.getRaw());
|
||||
contentType.getLanguage().ifPresent(language -> responseBuilder.header("Language", language));
|
||||
contentType.getLanguage().ifPresent(language -> responseBuilder.header("X-Programming-Language", language));
|
||||
}
|
||||
|
||||
private byte[] getHead(String revision, String path, RepositoryService repositoryService) throws IOException {
|
||||
|
||||
@@ -4,22 +4,29 @@ import com.webcohesion.enunciate.metadata.rs.ResponseCode;
|
||||
import com.webcohesion.enunciate.metadata.rs.StatusCodes;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.api.DiffFormat;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.util.HttpUtil;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.ws.rs.DefaultValue;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.StreamingOutput;
|
||||
|
||||
public class DiffRootResource {
|
||||
|
||||
public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";
|
||||
|
||||
private static final String DIFF_FORMAT_VALUES_REGEX = "NATIVE|GIT|UNIFIED";
|
||||
|
||||
private final RepositoryServiceFactory serviceFactory;
|
||||
|
||||
@Inject
|
||||
@@ -48,12 +55,14 @@ public class DiffRootResource {
|
||||
@ResponseCode(code = 404, condition = "not found, no revision with the specified param for the repository available or repository not found"),
|
||||
@ResponseCode(code = 500, condition = "internal server error")
|
||||
})
|
||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision){
|
||||
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision , @Pattern(regexp = DIFF_FORMAT_VALUES_REGEX) @DefaultValue("NATIVE") @QueryParam("format") String format ){
|
||||
HttpUtil.checkForCRLFInjection(revision);
|
||||
DiffFormat diffFormat = DiffFormat.valueOf(format);
|
||||
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
|
||||
StreamingOutput responseEntry = output -> {
|
||||
repositoryService.getDiffCommand()
|
||||
.setRevision(revision)
|
||||
.setFormat(diffFormat)
|
||||
.retriveContent(output);
|
||||
};
|
||||
return Response.ok(responseEntry)
|
||||
|
||||
@@ -17,7 +17,7 @@ public class ErrorDto {
|
||||
private List<ContextEntry> context;
|
||||
private String message;
|
||||
|
||||
@XmlElement(name = "violation")
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@XmlElementWrapper(name = "violations")
|
||||
private List<ConstraintViolationDto> violations;
|
||||
|
||||
|
||||
@@ -34,22 +34,19 @@ package sonia.scm.boot;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.github.legman.Subscribe;
|
||||
|
||||
import com.google.inject.servlet.GuiceFilter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.Stage;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -65,6 +62,8 @@ public class BootstrapContextFilter extends GuiceFilter
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
private final BootstrapContextListener listener = new BootstrapContextListener();
|
||||
|
||||
/**
|
||||
* Restart the whole webapp context.
|
||||
*
|
||||
@@ -85,29 +84,20 @@ public class BootstrapContextFilter extends GuiceFilter
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
logger.warn(
|
||||
"destroy filter pipeline, because of a received restart event");
|
||||
logger.warn("destroy filter pipeline, because of a received restart event");
|
||||
destroy();
|
||||
logger.warn(
|
||||
"reinitialize filter pipeline, because of a received restart event");
|
||||
super.init(filterConfig);
|
||||
|
||||
logger.warn("reinitialize filter pipeline, because of a received restart event");
|
||||
initGuice();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param filterConfig
|
||||
*
|
||||
* @throws ServletException
|
||||
*/
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
this.filterConfig = filterConfig;
|
||||
super.init(filterConfig);
|
||||
|
||||
initGuice();
|
||||
|
||||
if (SCMContext.getContext().getStage() == Stage.DEVELOPMENT)
|
||||
{
|
||||
@@ -116,6 +106,19 @@ public class BootstrapContextFilter extends GuiceFilter
|
||||
}
|
||||
}
|
||||
|
||||
public void initGuice() throws ServletException {
|
||||
super.init(filterConfig);
|
||||
|
||||
listener.contextInitialized(new ServletContextEvent(filterConfig.getServletContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
listener.contextDestroyed(new ServletContextEvent(filterConfig.getServletContext()));
|
||||
ServletContextCleaner.cleanup(filterConfig.getServletContext());
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
|
||||
@@ -148,13 +148,15 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
{
|
||||
context = sce.getServletContext();
|
||||
|
||||
PluginIndex index = readCorePluginIndex(context);
|
||||
|
||||
File pluginDirectory = getPluginDirectory();
|
||||
|
||||
try
|
||||
{
|
||||
extractCorePlugins(context, pluginDirectory, index);
|
||||
if (!isCorePluginExtractionDisabled()) {
|
||||
extractCorePlugins(context, pluginDirectory);
|
||||
} else {
|
||||
logger.info("core plugin extraction is disabled");
|
||||
}
|
||||
|
||||
ClassLoader cl =
|
||||
ClassLoaders.getContextClassLoader(BootstrapContextListener.class);
|
||||
@@ -181,31 +183,8 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the whole webapp context.
|
||||
*
|
||||
*
|
||||
* @param event restart event
|
||||
*/
|
||||
@Subscribe
|
||||
public void handleRestartEvent(RestartEvent event)
|
||||
{
|
||||
logger.warn("received restart event from {} with reason: {}",
|
||||
event.getCause(), event.getReason());
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
logger.error("context is null, scm-manager is not initialized");
|
||||
}
|
||||
else
|
||||
{
|
||||
ServletContextEvent sce = new ServletContextEvent(context);
|
||||
|
||||
logger.warn("destroy context, because of a received restart event");
|
||||
contextDestroyed(sce);
|
||||
logger.warn("reinitialize context, because of a received restart event");
|
||||
contextInitialized(sce);
|
||||
}
|
||||
private boolean isCorePluginExtractionDisabled() {
|
||||
return Boolean.getBoolean("sonia.scm.boot.disable-core-plugin-extraction");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,7 +193,6 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @param context
|
||||
* @param pluginDirectory
|
||||
* @param name
|
||||
* @param entry
|
||||
*
|
||||
* @throws IOException
|
||||
@@ -269,17 +247,15 @@ public class BootstrapContextListener implements ServletContextListener
|
||||
*
|
||||
* @param context
|
||||
* @param pluginDirectory
|
||||
* @param lines
|
||||
* @param index
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private void extractCorePlugins(ServletContext context, File pluginDirectory,
|
||||
PluginIndex index)
|
||||
throws IOException
|
||||
private void extractCorePlugins(ServletContext context, File pluginDirectory) throws IOException
|
||||
{
|
||||
IOUtil.mkdirs(pluginDirectory);
|
||||
|
||||
PluginIndex index = readCorePluginIndex(context);
|
||||
|
||||
for (PluginIndexEntry entry : index)
|
||||
{
|
||||
extractCorePlugin(context, pluginDirectory, entry);
|
||||
|
||||
97
scm-webapp/src/main/java/sonia/scm/boot/RestartServlet.java
Normal file
97
scm-webapp/src/main/java/sonia/scm/boot/RestartServlet.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.Priority;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.Stage;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.filter.WebElement;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* This servlet sends a {@link RestartEvent} to the {@link ScmEventBus} which causes scm-manager to restart the context.
|
||||
* The {@link RestartServlet} can be used for reloading java code or for installing plugins without a complete restart.
|
||||
* At the moment the Servlet accepts only request, if scm-manager was started in the {@link Stage#DEVELOPMENT} stage.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Priority(0)
|
||||
@WebElement("/restart")
|
||||
public class RestartServlet extends HttpServlet {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RestartServlet.class);
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
private final AtomicBoolean restarting = new AtomicBoolean();
|
||||
|
||||
private final ScmEventBus eventBus;
|
||||
private final Stage stage;
|
||||
|
||||
@Inject
|
||||
public RestartServlet() {
|
||||
this(ScmEventBus.getInstance(), SCMContext.getContext().getStage());
|
||||
}
|
||||
|
||||
RestartServlet(ScmEventBus eventBus, Stage stage) {
|
||||
this.eventBus = eventBus;
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
|
||||
LOG.info("received sendRestartEvent request");
|
||||
|
||||
if (isRestartAllowed()) {
|
||||
|
||||
try (InputStream requestInput = req.getInputStream()) {
|
||||
Reason reason = objectMapper.readValue(requestInput, Reason.class);
|
||||
sendRestartEvent(resp, reason);
|
||||
} catch (IOException ex) {
|
||||
LOG.warn("failed to trigger sendRestartEvent event", ex);
|
||||
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG.debug("received restart event in non development stage");
|
||||
resp.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRestartAllowed() {
|
||||
return stage == Stage.DEVELOPMENT;
|
||||
}
|
||||
|
||||
private void sendRestartEvent(HttpServletResponse response, Reason reason) {
|
||||
if ( restarting.compareAndSet(false, true) ) {
|
||||
LOG.info("trigger sendRestartEvent, because of {}", reason.getMessage());
|
||||
eventBus.post(new RestartEvent(RestartServlet.class, reason.getMessage()));
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||
} else {
|
||||
LOG.warn("scm-manager restarts already");
|
||||
response.setStatus(HttpServletResponse.SC_CONFLICT);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Reason {
|
||||
|
||||
private String message;
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Remove cached resources from {@link ServletContext} to allow a clean restart of scm-manager without stale or
|
||||
* duplicated data.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
final class ServletContextCleaner {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ServletContextCleaner.class);
|
||||
|
||||
private static final Set<String> REMOVE_PREFIX = ImmutableSet.of(
|
||||
"org.jboss.resteasy",
|
||||
"resteasy",
|
||||
"org.apache.shiro",
|
||||
"sonia.scm"
|
||||
);
|
||||
|
||||
private ServletContextCleaner() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove cached attributes from {@link ServletContext}.
|
||||
*
|
||||
* @param servletContext servlet context
|
||||
*/
|
||||
static void cleanup(ServletContext servletContext) {
|
||||
LOG.info("remove cached attributes from context");
|
||||
|
||||
Enumeration<String> attributeNames = servletContext.getAttributeNames();
|
||||
while( attributeNames.hasMoreElements()) {
|
||||
String name = attributeNames.nextElement();
|
||||
if (shouldRemove(name)) {
|
||||
LOG.info("remove attribute {} from servlet context", name);
|
||||
servletContext.removeAttribute(name);
|
||||
} else {
|
||||
LOG.info("keep attribute {} in servlet context", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldRemove(String name) {
|
||||
for (String prefix : REMOVE_PREFIX) {
|
||||
if (name.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableList.Builder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.SCMContext;
|
||||
import sonia.scm.Stage;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -69,19 +71,21 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param servletContext
|
||||
* @param plugins
|
||||
*/
|
||||
public DefaultUberWebResourceLoader(ServletContext servletContext,
|
||||
Iterable<PluginWrapper> plugins)
|
||||
{
|
||||
public DefaultUberWebResourceLoader(ServletContext servletContext, Iterable<PluginWrapper> plugins) {
|
||||
this(servletContext, plugins, SCMContext.getContext().getStage());
|
||||
}
|
||||
|
||||
public DefaultUberWebResourceLoader(ServletContext servletContext, Iterable<PluginWrapper> plugins, Stage stage) {
|
||||
this.servletContext = servletContext;
|
||||
this.plugins = plugins;
|
||||
this.cache = CacheBuilder.newBuilder().build();
|
||||
this.cache = createCache(stage);
|
||||
}
|
||||
|
||||
private Cache<String, URL> createCache(Stage stage) {
|
||||
if (stage == Stage.DEVELOPMENT) {
|
||||
return CacheBuilder.newBuilder().maximumSize(0).build(); // Disable caching
|
||||
}
|
||||
return CacheBuilder.newBuilder().build();
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
@@ -97,7 +101,7 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader
|
||||
@Override
|
||||
public URL getResource(String path)
|
||||
{
|
||||
URL resource = cache.getIfPresent(path);
|
||||
URL resource = getFromCache(path);
|
||||
|
||||
if (resource == null)
|
||||
{
|
||||
@@ -105,7 +109,7 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader
|
||||
|
||||
if (resource != null)
|
||||
{
|
||||
cache.put(path, resource);
|
||||
addToCache(path, resource);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -116,6 +120,14 @@ public class DefaultUberWebResourceLoader implements UberWebResourceLoader
|
||||
return resource;
|
||||
}
|
||||
|
||||
private URL getFromCache(String path) {
|
||||
return cache.getIfPresent(path);
|
||||
}
|
||||
|
||||
private void addToCache(String path, URL url) {
|
||||
cache.put(path, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -65,9 +65,6 @@
|
||||
<logger name="sonia.scm.event.LegmanScmEventBus" level="DEBUG" />
|
||||
<logger name="sonia.scm.plugin.ext.DefaultAnnotationScanner" level="INFO" />
|
||||
<logger name="sonia.scm.security.ConfigurableLoginAttemptHandler" level="DEBUG" />
|
||||
|
||||
<!-- event bus -->
|
||||
<logger name="sonia.scm.event.LegmanScmEventBus" level="INFO" />
|
||||
|
||||
<!-- cgi -->
|
||||
<logger name="sonia.scm.web.cgi.DefaultCGIExecutor" level="DEBUG" />
|
||||
@@ -93,7 +90,9 @@
|
||||
<logger name="net.sf.ehcache" level="DEBUG" />
|
||||
-->
|
||||
|
||||
<logger name="org.jboss.resteasy" level="DEBUG" />
|
||||
<logger name="org.jboss.resteasy" level="INFO" />
|
||||
|
||||
<logger name="sonia.scm.boot.RestartServlet" level="TRACE" />
|
||||
|
||||
<root level="WARN">
|
||||
<appender-ref ref="STDOUT" />
|
||||
|
||||
@@ -41,10 +41,6 @@
|
||||
|
||||
<!-- bootstraping -->
|
||||
|
||||
<listener>
|
||||
<listener-class>sonia.scm.boot.BootstrapContextListener</listener-class>
|
||||
</listener>
|
||||
|
||||
<filter>
|
||||
<filter-name>BootstrapFilter</filter-name>
|
||||
<filter-class>sonia.scm.boot.BootstrapContextFilter</filter-class>
|
||||
@@ -55,25 +51,6 @@
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
<!-- rest -->
|
||||
|
||||
<context-param>
|
||||
<param-name>resteasy.servlet.mapping.prefix</param-name>
|
||||
<param-value>/api</param-value>
|
||||
</context-param>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>Resteasy</servlet-name>
|
||||
<servlet-class>
|
||||
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
|
||||
</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Resteasy</servlet-name>
|
||||
<url-pattern>/api/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- capture sessions -->
|
||||
<!--
|
||||
TODO remove, we need no longer a session
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -45,6 +46,23 @@ public class TemplatingPushStateDispatcherTest {
|
||||
|
||||
@Test
|
||||
public void testDispatch() throws IOException {
|
||||
TemplatingPushStateDispatcher.IndexHtmlModel model = dispatch();
|
||||
assertEquals("/scm", model.getContextPath());
|
||||
assertNull(model.getLiveReloadURL());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDispatchWithLiveReloadURL() throws IOException {
|
||||
System.setProperty("livereload.url", "/livereload.js");
|
||||
try {
|
||||
TemplatingPushStateDispatcher.IndexHtmlModel model = dispatch();
|
||||
assertEquals("/livereload.js", model.getLiveReloadURL());
|
||||
} finally {
|
||||
System.clearProperty("livereload.url");
|
||||
}
|
||||
}
|
||||
|
||||
private TemplatingPushStateDispatcher.IndexHtmlModel dispatch() throws IOException {
|
||||
when(request.getContextPath()).thenReturn("/scm");
|
||||
when(templateEngine.getTemplate(TemplatingPushStateDispatcher.TEMPLATE)).thenReturn(template);
|
||||
|
||||
@@ -59,8 +77,7 @@ public class TemplatingPushStateDispatcherTest {
|
||||
|
||||
verify(template).execute(any(Writer.class), captor.capture());
|
||||
|
||||
TemplatingPushStateDispatcher.IndexHtmlModel model = (TemplatingPushStateDispatcher.IndexHtmlModel) captor.getValue();
|
||||
assertEquals("/scm", model.getContextPath());
|
||||
return (TemplatingPushStateDispatcher.IndexHtmlModel) captor.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ public class ChangesetRootResourceTest extends RepositoryTestBase {
|
||||
when(logCommandBuilder.setStartChangeset(anyString())).thenReturn(logCommandBuilder);
|
||||
when(logCommandBuilder.getChangesets()).thenReturn(changesetPagingResult);
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get(CHANGESET_URL + "id")
|
||||
.get(CHANGESET_URL + id)
|
||||
.accept(VndMediaType.CHANGESET);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
@@ -92,7 +92,7 @@ public class ContentResourceTest {
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "SomeGoCode.go");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assertEquals("GO", response.getHeaderString("Language"));
|
||||
assertEquals("GO", response.getHeaderString("X-Programming-Language"));
|
||||
assertEquals("text/x-go", response.getHeaderString("Content-Type"));
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ public class ContentResourceTest {
|
||||
Response response = contentResource.get(NAMESPACE, REPO_NAME, REV, "Dockerfile");
|
||||
assertEquals(200, response.getStatus());
|
||||
|
||||
assertEquals("DOCKERFILE", response.getHeaderString("Language"));
|
||||
assertEquals("DOCKERFILE", response.getHeaderString("X-Programming-Language"));
|
||||
assertEquals("text/plain", response.getHeaderString("Content-Type"));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,18 +20,22 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.api.rest.AuthorizationExceptionMapper;
|
||||
import sonia.scm.api.rest.ContextualExceptionMapper;
|
||||
import sonia.scm.api.rest.IllegalArgumentExceptionMapper;
|
||||
import sonia.scm.api.v2.NotFoundExceptionMapper;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.DiffCommandBuilder;
|
||||
import sonia.scm.repository.api.DiffFormat;
|
||||
import sonia.scm.repository.api.RepositoryService;
|
||||
import sonia.scm.repository.api.RepositoryServiceFactory;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -74,6 +78,7 @@ public class DiffResourceTest extends RepositoryTestBase {
|
||||
dispatcher.getProviderFactory().register(new NotFoundExceptionMapper(mapper));
|
||||
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(CRLFInjectionExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class);
|
||||
when(service.getDiffCommand()).thenReturn(diffCommandBuilder);
|
||||
subjectThreadState.bind();
|
||||
ThreadContext.bind(subject);
|
||||
@@ -88,19 +93,17 @@ public class DiffResourceTest extends RepositoryTestBase {
|
||||
@Test
|
||||
public void shouldGetDiffs() throws Exception {
|
||||
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.retriveContent(any())).thenReturn(diffCommandBuilder);
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get(DIFF_URL + "revision")
|
||||
.accept(VndMediaType.DIFF);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
assertEquals(200, response.getStatus());
|
||||
log.info("Response :{}", response.getContentAsString());
|
||||
|
||||
assertThat(response.getStatus())
|
||||
.isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.isNotNull();
|
||||
String expectedHeader = "Content-Disposition";
|
||||
String expectedValue = "attachment; filename=\"repo-revision.diff\"; filename*=utf-8''repo-revision.diff";
|
||||
assertThat(response.getOutputHeaders().containsKey(expectedHeader)).isTrue();
|
||||
@@ -122,6 +125,7 @@ public class DiffResourceTest extends RepositoryTestBase {
|
||||
@Test
|
||||
public void shouldGet404OnMissingRevision() throws Exception {
|
||||
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.retriveContent(any())).thenThrow(new NotFoundException("Text", "x"));
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
@@ -137,6 +141,7 @@ public class DiffResourceTest extends RepositoryTestBase {
|
||||
@Test
|
||||
public void shouldGet400OnCrlfInjection() throws Exception {
|
||||
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.retriveContent(any())).thenThrow(new NotFoundException("Text", "x"));
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
@@ -147,6 +152,47 @@ public class DiffResourceTest extends RepositoryTestBase {
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGet400OnUnknownFormat() throws Exception {
|
||||
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.retriveContent(any())).thenThrow(new NotFoundException("Test", "test"));
|
||||
|
||||
MockHttpRequest request = MockHttpRequest
|
||||
.get(DIFF_URL + "revision?format=Unknown")
|
||||
.accept(VndMediaType.DIFF);
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
dispatcher.invoke(request, response);
|
||||
assertEquals(400, response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptDiffFormats() throws Exception {
|
||||
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
|
||||
when(diffCommandBuilder.retriveContent(any())).thenReturn(diffCommandBuilder);
|
||||
|
||||
Arrays.stream(DiffFormat.values()).map(DiffFormat::name).forEach(
|
||||
this::assertRequestOk
|
||||
);
|
||||
}
|
||||
|
||||
private void assertRequestOk(String format) {
|
||||
MockHttpRequest request = null;
|
||||
try {
|
||||
request = MockHttpRequest
|
||||
.get(DIFF_URL + "revision?format=" + format)
|
||||
.accept(VndMediaType.DIFF);
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
fail("got exception: " + e);
|
||||
}
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus())
|
||||
.withFailMessage("diff format from DiffFormat enum must be accepted: " + format)
|
||||
.isEqualTo(200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import sonia.scm.api.rest.AuthorizationExceptionMapper;
|
||||
import sonia.scm.api.rest.ConcurrentModificationExceptionMapper;
|
||||
import sonia.scm.api.rest.IllegalArgumentExceptionMapper;
|
||||
import sonia.scm.api.v2.NotFoundExceptionMapper;
|
||||
import sonia.scm.api.v2.NotSupportedFeatureExceptionMapper;
|
||||
|
||||
public class DispatcherMock {
|
||||
public static Dispatcher createDispatcher(Object resource) {
|
||||
@@ -21,6 +22,7 @@ public class DispatcherMock {
|
||||
dispatcher.getProviderFactory().register(new ChangePasswordNotAllowedExceptionMapper(mapper));
|
||||
dispatcher.getProviderFactory().register(new InvalidPasswordExceptionMapper(mapper));
|
||||
dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class);
|
||||
dispatcher.getProviderFactory().register(new NotSupportedFeatureExceptionMapper(mapper));
|
||||
return dispatcher;
|
||||
}
|
||||
}
|
||||
|
||||
133
scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java
Normal file
133
scm-webapp/src/test/java/sonia/scm/boot/RestartServletTest.java
Normal file
@@ -0,0 +1,133 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.github.legman.Subscribe;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.Stage;
|
||||
import sonia.scm.event.ScmEventBus;
|
||||
import sonia.scm.event.ScmTestEventBus;
|
||||
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RestartServletTest {
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
|
||||
private RestartServlet restartServlet;
|
||||
|
||||
private EventListener listener;
|
||||
|
||||
private void setUpObjectUnderTest(Stage stage) {
|
||||
listener = new EventListener();
|
||||
ScmEventBus eventBus = ScmTestEventBus.getInstance();
|
||||
eventBus.register(listener);
|
||||
|
||||
restartServlet = new RestartServlet(eventBus, stage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestart() throws IOException {
|
||||
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||
setRequestInputReason("something changed");
|
||||
|
||||
restartServlet.doPost(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||
|
||||
RestartEvent restartEvent = listener.restartEvent;
|
||||
assertThat(restartEvent).isNotNull();
|
||||
assertThat(restartEvent.getCause()).isEqualTo(RestartServlet.class);
|
||||
assertThat(restartEvent.getReason()).isEqualTo("something changed");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartCalledTwice() throws IOException {
|
||||
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||
|
||||
setRequestInputReason("initial change");
|
||||
restartServlet.doPost(request, response);
|
||||
verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
|
||||
|
||||
setRequestInputReason("changed again");
|
||||
restartServlet.doPost(request, response);
|
||||
verify(response).setStatus(HttpServletResponse.SC_CONFLICT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartWithInvalidContent() throws IOException {
|
||||
setUpObjectUnderTest(Stage.DEVELOPMENT);
|
||||
|
||||
setRequestInputContent("invalid json");
|
||||
|
||||
restartServlet.doPost(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestartInProductionStage() throws IOException {
|
||||
setUpObjectUnderTest(Stage.PRODUCTION);
|
||||
|
||||
setRequestInputReason("initial change");
|
||||
|
||||
restartServlet.doPost(request, response);
|
||||
|
||||
verify(response).setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
|
||||
}
|
||||
|
||||
private void setRequestInputReason(String message) throws IOException {
|
||||
String content = createReason(message);
|
||||
setRequestInputContent(content);
|
||||
}
|
||||
|
||||
private void setRequestInputContent(String content) throws IOException {
|
||||
InputStream input = createReasonAsInputStream(content);
|
||||
when(request.getInputStream()).thenReturn(createServletInputStream(input));
|
||||
}
|
||||
|
||||
private ServletInputStream createServletInputStream(final InputStream inputStream) {
|
||||
return new ServletInputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return inputStream.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private InputStream createReasonAsInputStream(String content) {
|
||||
return new ByteArrayInputStream(content.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
private String createReason(String message) {
|
||||
return String.format("{\"message\": \"%s\"}", message);
|
||||
}
|
||||
|
||||
public static class EventListener {
|
||||
|
||||
private RestartEvent restartEvent;
|
||||
|
||||
@Subscribe(async = false)
|
||||
public void store(RestartEvent restartEvent) {
|
||||
this.restartEvent = restartEvent;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ServletContextCleanerTest {
|
||||
|
||||
@Mock
|
||||
private ServletContext servletContext;
|
||||
|
||||
@Test
|
||||
public void testCleanup() {
|
||||
Set<String> names = ImmutableSet.of(
|
||||
"org.jboss.resteasy.Dispatcher",
|
||||
"resteasy.Deployment",
|
||||
"sonia.scm.Context",
|
||||
"org.eclipse.jetty.HttpServer",
|
||||
"javax.servlet.Context",
|
||||
"org.apache.shiro.SecurityManager"
|
||||
);
|
||||
|
||||
when(servletContext.getAttributeNames()).thenReturn(toEnumeration(names));
|
||||
|
||||
ServletContextCleaner.cleanup(servletContext);
|
||||
|
||||
verify(servletContext).removeAttribute("org.jboss.resteasy.Dispatcher");
|
||||
verify(servletContext).removeAttribute("resteasy.Deployment");
|
||||
verify(servletContext).removeAttribute("sonia.scm.Context");
|
||||
verify(servletContext, never()).removeAttribute("org.eclipse.jetty.HttpServer");
|
||||
verify(servletContext, never()).removeAttribute("javax.servlet.Context");
|
||||
verify(servletContext).removeAttribute("org.apache.shiro.SecurityManager");
|
||||
}
|
||||
|
||||
private <T> Enumeration<T> toEnumeration(Collection<T> collection) {
|
||||
return new Vector<>(collection).elements();
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.Stage;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.io.File;
|
||||
@@ -96,14 +97,12 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws MalformedURLException
|
||||
*/
|
||||
@Test
|
||||
public void testGetResourceFromCache() throws MalformedURLException
|
||||
{
|
||||
public void testGetResourceFromCache() {
|
||||
DefaultUberWebResourceLoader resourceLoader =
|
||||
new DefaultUberWebResourceLoader(servletContext,
|
||||
new ArrayList<PluginWrapper>());
|
||||
new ArrayList<PluginWrapper>(), Stage.PRODUCTION);
|
||||
|
||||
resourceLoader.getCache().put("/myresource", GITHUB);
|
||||
|
||||
@@ -112,6 +111,15 @@ public class DefaultUberWebResourceLoaderTest extends WebResourceLoaderTestBase
|
||||
assertSame(GITHUB, resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceCacheIsDisableInStageDevelopment() throws MalformedURLException {
|
||||
DefaultUberWebResourceLoader resourceLoader = new DefaultUberWebResourceLoader(servletContext, new ArrayList<>(), Stage.DEVELOPMENT);
|
||||
when(servletContext.getResource("/scm")).thenAnswer(invocation -> new URL("https://scm-manager.org"));
|
||||
URL url = resourceLoader.getResource("/scm");
|
||||
URL secondUrl = resourceLoader.getResource("/scm");
|
||||
assertNotSame(url, secondUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user