/*
* Sonatype Nexus (TM) Open Source Version
* Copyright (c) 2008-present Sonatype, Inc.
* All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0,
* which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks
* of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the
* Eclipse Foundation. All other trademarks are the property of their respective owners.
*/
package com.google.inject.servlet;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import com.google.common.collect.Sets;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import org.eclipse.sisu.inject.BeanLocator;
import org.eclipse.sisu.wire.EntryListAdapter;
import static com.google.common.base.Preconditions.checkState;
/**
* Dynamic {@code ServletPipeline} that can update its sequence of servlet definitions on-demand.
* Includes patched methods from {@link ManagedServletPipeline} where delegating isn't possible.
*/
@Singleton
// don't use @Named, keep as implicit JIT-binding
final class DynamicServletPipeline
extends ManagedServletPipeline
{
static final Injector DUMMY_INJECTOR = Guice.createInjector();
// dynamic list of definitions
private final List<ServletDefinition> servletDefinitions;
// stable cache of definitions
private volatile ServletDefinition[] servletDefinitionCache = {};
@Inject
DynamicServletPipeline(final BeanLocator locator) {
super(DUMMY_INJECTOR);
servletDefinitions = new EntryListAdapter<>(locator.locate(Key.get(ServletDefinition.class)));
}
public synchronized void refreshCache() {
final Object[] snapshot = servletDefinitions.toArray();
servletDefinitionCache = Arrays.copyOf(snapshot, snapshot.length, ServletDefinition[].class);
PipelineLogger.dump(servletDefinitionCache);
}
@Override
public boolean service(ServletRequest request, ServletResponse response) throws IOException, ServletException {
for (ServletDefinition servletDefinition : servletDefinitions()) {
if (servletDefinition.service(request, response)) {
return true;
}
}
return false;
}
@Override
public void destroy() {
Set<HttpServlet> destroyedSoFar = Sets.newIdentityHashSet();
for (ServletDefinition servletDefinition : servletDefinitions()) {
servletDefinition.destroy(destroyedSoFar);
}
}
@Override
RequestDispatcher getRequestDispatcher(final String path) {
for (final ServletDefinition servletDefinition : servletDefinitions()) {
if (servletDefinition.shouldServe(path)) {
return new RequestDispatcher()
{
public void forward(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException
{
checkState(!servletResponse.isCommitted(),
"Response has been committed--you can only call forward before"
+ " committing the response (hint: don't flush buffers)");
servletResponse.resetBuffer();
ServletRequest requestToProcess = servletRequest instanceof HttpServletRequest ?
wrapRequest((HttpServletRequest) servletRequest, path) :
servletRequest;
doServiceImpl(requestToProcess, servletResponse);
}
public void include(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException
{
doServiceImpl(servletRequest, servletResponse);
}
private void doServiceImpl(ServletRequest servletRequest, ServletResponse servletResponse)
throws ServletException, IOException
{
servletRequest.setAttribute(REQUEST_DISPATCHER_REQUEST, Boolean.TRUE);
try {
servletDefinition.doService(servletRequest, servletResponse);
}
finally {
servletRequest.removeAttribute(REQUEST_DISPATCHER_REQUEST);
}
}
};
}
}
return null;
}
@Override
protected boolean hasServletsMapped() {
return servletDefinitionCache.length > 0;
}
private ServletDefinition[] servletDefinitions() {
return servletDefinitionCache;
}
}