Skip to content

Commit 8e7813a

Browse files
committed
[Fixes #2039] Prevent leaking HttpServiceEnabled instances
(cherry picked from commit 2288d8e)
1 parent 78ff0a3 commit 8e7813a

File tree

9 files changed

+70
-47
lines changed

9 files changed

+70
-47
lines changed

pax-web-extender-whiteboard/src/main/java/org/ops4j/pax/web/extender/whiteboard/internal/BundleWhiteboardApplication.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -157,19 +157,20 @@ public void webContainerRemoved(ServiceReference<WebContainer> ref) {
157157

158158
// no need to uninstall current contexts and elements - they'll get unregistered when bundle-scoped
159159
// HttpService/WebContainer is stopped. Here we only mark them as unregistered
160-
WhiteboardWebContainerView view = webContainerManager.whiteboardView(bundle, ref);
161-
if (view != null) {
162-
webElements.keySet().forEach(element -> {
163-
if (element.getContextModels().size() > 0 && webElements.get(element)) {
164-
webElements.put(element, false);
165-
}
166-
});
167-
webContexts.keySet().forEach(ctx -> {
168-
if (webContexts.get(ctx)) {
169-
webContexts.put(ctx, false);
170-
}
171-
});
172-
}
160+
webElements.keySet().forEach(element -> {
161+
if (element.getContextModels().size() > 0 && webElements.get(element)) {
162+
webElements.put(element, false);
163+
}
164+
if (element instanceof ServletModel && ((ServletModel) element).isResourceServlet()) {
165+
// we have to clear up the supplier which is related to old HttpService instance
166+
element.setElementSupplier(null);
167+
}
168+
});
169+
webContexts.keySet().forEach(ctx -> {
170+
if (webContexts.get(ctx)) {
171+
webContexts.put(ctx, false);
172+
}
173+
});
173174

174175
webContainerManager.releaseContainer(bundle, ref);
175176
webContainerServiceRef = null;

pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/JettyServerWrapper.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,8 +1286,7 @@ private void removeServletModel(String contextPath, ServletModel model) {
12861286

12871287
((PaxWebServletHandler) sch.getServletHandler()).removeServletWithMapping(model);
12881288

1289-
LOG.info("Removing servlet {}", model);
1290-
LOG.debug("Removing servlet {} from context {}", model.getName(), contextPath);
1289+
LOG.info("Removing servlet {} from context {}", model, contextPath);
12911290

12921291
// are there any error page declarations in the model?
12931292
ErrorPageModel epm = model.getErrorPageModel();

pax-web-jetty/src/main/java/org/ops4j/pax/web/service/jetty/internal/PaxWebServletHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ protected synchronized void doStop() throws Exception {
205205
}
206206

207207
// Jetty 10+ keeps only "durable" servlets/filters/listeners. We're handling it a bit differently,
208-
// so we have to preservet them (because there's no reflection-free access to
208+
// so we have to preserve them (because there's no reflection-free access to
209209
// org.eclipse.jetty.servlet.ServletHandler._durable field)
210210

211211
ServletHolder[] servlets = getServlets();

pax-web-runtime/src/main/java/org/ops4j/pax/web/service/internal/HttpServiceEnabled.java

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -267,11 +267,13 @@ public void stop() {
267267
b.getOperations().add(new TransactionStateChange(OpCode.DISASSOCIATE, ctx));
268268
}
269269

270-
serverModel.runSilently(() -> {
271-
serverController.sendBatch(b);
272-
b.accept(serviceModel);
273-
return null;
274-
}, true);
270+
if (!b.getOperations().isEmpty()) {
271+
serverModel.runSilently(() -> {
272+
serverController.sendBatch(b);
273+
b.accept(serviceModel);
274+
return null;
275+
}, true);
276+
}
275277

276278
stopped = true;
277279
}
@@ -501,7 +503,9 @@ private void doRegisterServlet(Collection<HttpContext> httpContexts, ServletMode
501503
jspSCIModel = serverModel.createJSPServletContainerInitializerModel(serviceBundle);
502504
jspSCIModel.setRegisteringBundle(model.getRegisteringBundle());
503505
jspSCIModel.getRelatedServletModels().add(model);
504-
model.getContextModels().forEach(jspSCIModel::addContextModel);
506+
if (!jspSCIModel.hasContextModels()) {
507+
model.getContextModels().forEach(jspSCIModel::addContextModel);
508+
}
505509
newBatch = new Batch("JSP Configuration and registration of " + model);
506510

507511
// whether or not the target context is started, we're adding an SCI that'll have to be run.
@@ -642,7 +646,7 @@ private void doUnregisterServlet(ServletModel model) {
642646
+ "was never registered by " + registeringBundle);
643647
}
644648
} else if (reference != null) {
645-
LOG.info("Unregistering servlet by refernce \"{}\"", reference);
649+
LOG.info("Unregistering servlet by reference \"{}\"", reference);
646650

647651
for (ServletModel existing : serviceModel.getServletModels()) {
648652
if (existing.getElementReference().equals(reference)) {
@@ -754,7 +758,7 @@ public void registerResources(String alias, String name, HttpContext context) th
754758

755759
/**
756760
* Helper method to create a <em>resource servlet</em> using a <em>base</em> which may be either a <em>chroot</em>
757-
* for bundle-resource access of {@code file:} URL.
761+
* for bundle-resource access or {@code file:} URL.
758762
* @param urlPatterns
759763
* @param rawBase
760764
* @return
@@ -983,7 +987,7 @@ private void doUnregisterFilter(final FilterModel model) {
983987
+ "was never registered by " + registeringBundle);
984988
}
985989
} else if (reference != null) {
986-
LOG.info("Unregistering filter by refernce \"{}\"", reference);
990+
LOG.info("Unregistering filter by reference \"{}\"", reference);
987991

988992
for (FilterModel existing : serviceModel.getFilterModels()) {
989993
if (existing.getElementReference().equals(reference)) {
@@ -1930,7 +1934,9 @@ private void doRegisterWebSocket(List<HttpContext> httpContexts, WebSocketModel
19301934
cimForGenericWSSupport.setServiceId(0);
19311935
cimForGenericWSSupport.setServiceRank(0);
19321936

1933-
model.getContextModels().forEach(cimForGenericWSSupport::addContextModel);
1937+
if (!cimForGenericWSSupport.hasContextModels()) {
1938+
model.getContextModels().forEach(cimForGenericWSSupport::addContextModel);
1939+
}
19341940
cimForGenericWSSupport.setRegisteringBundle(model.getRegisteringBundle());
19351941

19361942
// now the important part - according to JSR-356 (WebSockets), user is registering endpoints
@@ -1995,7 +2001,9 @@ private ContainerInitializerModel createContainerSpecificWsSCIModel(Bundle bundl
19952001
cim.setServiceId(0);
19962002
cim.setServiceRank(Integer.MAX_VALUE);
19972003

1998-
model.getContextModels().forEach(cim::addContextModel);
2004+
if (!cim.hasContextModels()) {
2005+
model.getContextModels().forEach(cim::addContextModel);
2006+
}
19992007
cim.setRegisteringBundle(model.getRegisteringBundle());
20002008

20012009
return cim;
@@ -2053,7 +2061,7 @@ private void doUnregisterWebSocket(WebSocketModel model) {
20532061
+ "was never registered by " + registeringBundle);
20542062
}
20552063
} else if (reference != null) {
2056-
LOG.info("Unregistering webs ocket by refernce \"{}\"", reference);
2064+
LOG.info("Unregistering webs ocket by reference \"{}\"", reference);
20572065

20582066
for (WebSocketModel existing : serviceModel.getWebSocketModels()) {
20592067
if (existing.getElementReference().equals(reference)) {
@@ -2499,7 +2507,7 @@ public void removeWhiteboardOsgiContextModel(OsgiContextModel model) {
24992507
try {
25002508
serverController.sendBatch(batch);
25012509
batch.accept(serviceModel);
2502-
handleReRegistrationEvents(WebElementEvent.State.DEPLOYED, batch, null);
2510+
handleReRegistrationEvents(WebElementEvent.State.UNDEPLOYED, batch, null);
25032511
} catch (Exception e) {
25042512
handleReRegistrationEvents(WebElementEvent.State.FAILED, batch, e);
25052513
}

pax-web-runtime/src/main/java/org/ops4j/pax/web/service/internal/HttpServiceProxy.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,20 @@ class HttpServiceProxy implements WebContainer, StoppableHttpService {
6969
public void stop() {
7070
if (delegate instanceof StoppableHttpService) {
7171
LOG.debug("Stopping http service: {}", delegate);
72-
final StoppableHttpService stopping = (StoppableHttpService) delegate;
72+
StoppableHttpService stopping;
73+
synchronized (this) {
74+
stopping = (StoppableHttpService) delegate;
7375

74-
// PAXWEB-1077: ServletContext becomes unavailable on restart when using Whiteboard and CustomContexts
76+
// PAXWEB-1077: ServletContext becomes unavailable on restart when using Whiteboard and CustomContexts
7577

76-
// first replace the delegate
77-
delegate = new HttpServiceDisabled(serviceBundle);
78+
// first replace the delegate
79+
delegate = new HttpServiceDisabled(serviceBundle);
80+
}
7881

7982
// then cleanup the delegate without a risk of problems happening at user side
80-
stopping.stop();
83+
if (stopping != null) {
84+
stopping.stop();
85+
}
8186
} else {
8287
LOG.warn("Http service has already been stopped");
8388
}

pax-web-spi/src/main/java/org/ops4j/pax/web/service/spi/servlet/OsgiServletContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,10 @@ public void register() {
215215
public void unregister() {
216216
if (registration != null) {
217217
try {
218-
LOG.info("Unegistering {} as OSGi service for \"{}\" context path", this, osgiContextModel.getContextPath());
218+
LOG.info("Unregistering {} as OSGi service for \"{}\" context path", this, osgiContextModel.getContextPath());
219219

220220
registration.unregister();
221+
} catch (IllegalStateException ignored) {
221222
} catch (Exception e) {
222223
if (osgiContextModel.getOwnerBundle().getState() == Bundle.ACTIVE) {
223224
LOG.error("Error unregistering {} from OSGi registry: {}", this, e.getMessage(), e);

pax-web-tomcat/src/main/java/org/ops4j/pax/web/service/tomcat/internal/TomcatServerWrapper.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,8 +1306,7 @@ public void visitServletModelChange(ServletModelChange change) {
13061306
}
13071307

13081308
private void removeServletModel(String contextPath, ServletModel model) {
1309-
LOG.info("Removing servlet {}", model);
1310-
LOG.debug("Removing servlet {} from context {}", model.getName(), contextPath);
1309+
LOG.info("Removing servlet {} from context {}", model, contextPath);
13111310

13121311
// there should already be a ServletContextHandler
13131312
Context realContext = contextHandlers.get(contextPath);

pax-web-undertow/src/main/java/org/ops4j/pax/web/service/undertow/internal/UndertowServerWrapper.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,8 +1683,7 @@ public void visitServletModelChange(ServletModelChange change) {
16831683
private void removeServletModel(String contextPath, ServletModel model, ServletModelChange change) {
16841684
// this time we just assume that the servlet context is started
16851685

1686-
LOG.info("Removing servlet {}", model);
1687-
LOG.debug("Removing servlet {} from context {}", model.getName(), contextPath);
1686+
LOG.info("Removing servlet {} from context {}", model, contextPath);
16881687

16891688
// take existing deployment manager and the deployment info from its deployment
16901689
DeploymentManager manager = getDeploymentManager(contextPath);

samples/samples-whiteboard/whiteboard-ds/src/main/java/org/ops4j/pax/web/samples/whiteboard/ds/extended/PaxWebWhiteboardServletMapping.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,34 @@
1919
import java.util.Map;
2020
import javax.servlet.MultipartConfigElement;
2121
import javax.servlet.Servlet;
22-
import javax.servlet.ServletException;
2322
import javax.servlet.http.HttpServlet;
2423
import javax.servlet.http.HttpServletRequest;
2524
import javax.servlet.http.HttpServletResponse;
2625

2726
import org.ops4j.pax.web.service.whiteboard.ServletMapping;
27+
import org.osgi.service.component.annotations.Activate;
2828
import org.osgi.service.component.annotations.Component;
29+
import org.osgi.service.component.annotations.Deactivate;
2930

3031
@Component
3132
public class PaxWebWhiteboardServletMapping implements ServletMapping {
3233

33-
private static final Servlet SERVLET = new HttpServlet() {
34-
@Override
35-
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
36-
resp.getWriter().println("Hello from " + PaxWebWhiteboardServletMapping.class.getName());
37-
}
38-
};
34+
private Servlet servlet = null;
35+
36+
@Activate
37+
protected void activate() {
38+
servlet = new HttpServlet() {
39+
@Override
40+
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
41+
resp.getWriter().println("Hello from " + PaxWebWhiteboardServletMapping.class.getName());
42+
}
43+
};
44+
}
45+
46+
@Deactivate
47+
protected void deactivate() {
48+
servlet = null;
49+
}
3950

4051
@Override
4152
public Class<? extends Servlet> getServletClass() {
@@ -64,7 +75,7 @@ public String getContextId() {
6475

6576
@Override
6677
public Servlet getServlet() {
67-
return SERVLET;
78+
return servlet;
6879
}
6980

7081
@Override

0 commit comments

Comments
 (0)