diff --git a/core/src/main/java/org/kohsuke/stapler/RequestImpl.java b/core/src/main/java/org/kohsuke/stapler/RequestImpl.java index 51c288b61..0ae81f138 100644 --- a/core/src/main/java/org/kohsuke/stapler/RequestImpl.java +++ b/core/src/main/java/org/kohsuke/stapler/RequestImpl.java @@ -216,6 +216,7 @@ public RenderOnDemandParameters createJavaScriptProxyParameters(Object toBeExpor return new RenderOnDemandParameters( "makeStaplerProxy", bound.getURL(), + bound.getReleaseURL(), getWebApp().getCrumbIssuer().issueCrumb(), bound.getBoundJavaScriptUrlNames()); } diff --git a/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java b/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java index 0c0ea358b..86c4b383b 100644 --- a/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java +++ b/core/src/main/java/org/kohsuke/stapler/StaplerRequest.java @@ -571,12 +571,15 @@ public interface StaplerRequest extends HttpServletRequest { final class RenderOnDemandParameters { public final String proxyMethod; public final String url; + public final String releaseUrl; public final String crumb; public final Set urlNames; - public RenderOnDemandParameters(String proxyMethod, String url, String crumb, Set urlNames) { + public RenderOnDemandParameters( + String proxyMethod, String url, String releaseUrl, String crumb, Set urlNames) { this.proxyMethod = proxyMethod; this.url = url; + this.releaseUrl = releaseUrl; this.crumb = crumb; this.urlNames = urlNames; } @@ -998,7 +1001,8 @@ public String createJavaScriptProxy(Object toBeExported) { @Override public RenderOnDemandParameters createJavaScriptProxyParameters(Object toBeExported) { StaplerRequest.RenderOnDemandParameters result = from.createJavaScriptProxyParameters(toBeExported); - return new RenderOnDemandParameters(result.proxyMethod, result.url, result.crumb, result.urlNames); + return new RenderOnDemandParameters( + result.proxyMethod, result.url, result.releaseUrl, result.crumb, result.urlNames); } @Override @@ -1654,7 +1658,8 @@ public String createJavaScriptProxy(Object toBeExported) { @Override public RenderOnDemandParameters createJavaScriptProxyParameters(Object toBeExported) { StaplerRequest2.RenderOnDemandParameters result = from.createJavaScriptProxyParameters(toBeExported); - return new RenderOnDemandParameters(result.proxyMethod, result.crumb, result.url, result.urlNames); + return new RenderOnDemandParameters( + result.proxyMethod, result.crumb, result.url, result.releaseUrl, result.urlNames); } @Override diff --git a/core/src/main/java/org/kohsuke/stapler/StaplerRequest2.java b/core/src/main/java/org/kohsuke/stapler/StaplerRequest2.java index 91b4eb996..f816f2ac5 100644 --- a/core/src/main/java/org/kohsuke/stapler/StaplerRequest2.java +++ b/core/src/main/java/org/kohsuke/stapler/StaplerRequest2.java @@ -533,12 +533,15 @@ public interface StaplerRequest2 extends HttpServletRequest { final class RenderOnDemandParameters { public final String proxyMethod; public final String url; + public final String releaseUrl; public final String crumb; public final Set urlNames; - public RenderOnDemandParameters(String proxyMethod, String url, String crumb, Set urlNames) { + public RenderOnDemandParameters( + String proxyMethod, String url, String releaseUrl, String crumb, Set urlNames) { this.proxyMethod = proxyMethod; this.url = url; + this.releaseUrl = releaseUrl; this.crumb = crumb; this.urlNames = urlNames; } diff --git a/core/src/main/java/org/kohsuke/stapler/bind/Bound.java b/core/src/main/java/org/kohsuke/stapler/bind/Bound.java index fabbef659..3d8baaf26 100644 --- a/core/src/main/java/org/kohsuke/stapler/bind/Bound.java +++ b/core/src/main/java/org/kohsuke/stapler/bind/Bound.java @@ -54,6 +54,13 @@ public abstract class Bound implements HttpResponse { */ public abstract String getURL(); + /** + * The URL where the object can be released, or {@code null} if not applicable. + */ + public String getReleaseURL() { + return null; + } + /** * Gets the bound object. */ diff --git a/core/src/main/java/org/kohsuke/stapler/bind/BoundObjectTable.java b/core/src/main/java/org/kohsuke/stapler/bind/BoundObjectTable.java index 79ccfbd1b..2887801d4 100644 --- a/core/src/main/java/org/kohsuke/stapler/bind/BoundObjectTable.java +++ b/core/src/main/java/org/kohsuke/stapler/bind/BoundObjectTable.java @@ -36,6 +36,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.kohsuke.stapler.Ancestor; +import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.Stapler; import org.kohsuke.stapler.StaplerFallback; @@ -71,6 +72,30 @@ public static boolean isValidJavaIdentifier(String name) { .allMatch(it -> Character.isJavaIdentifierPart(it) && !Character.isIdentifierIgnorable(it) && it < 256); } + /** + * Explicitly unbind this object via HTTP. + * + * @throws HttpResponses.HttpResponseException expected outcome returning 200 OK + */ + public void doRelease(StaplerRequest2 req, StaplerResponse2 rsp) + throws HttpResponses.HttpResponseException, IOException { + final Table table = resolve(false); + if (table == null) { + rsp.sendError(404); + return; + } + + String id = req.getRestOfPath().replace("/", ""); + + Object object = table.resolve(id); + if (object == null) { + rsp.sendError(200); + return; + } + table.release(id); + rsp.sendError(200); + } + /** * This serves the script content for a bound object. Support CSP-compatible st:bind and similar methods of making * objects accessible to JS. @@ -231,6 +256,11 @@ public void release() { Table.this.release(id); } + @Override + public String getReleaseURL() { + return Stapler.getCurrentRequest2().getContextPath() + RELEASE_PREFIX + id; + } + @Override public String getURL() { return Stapler.getCurrentRequest2().getContextPath() + PREFIX + id; @@ -351,6 +381,7 @@ private Object writeReplace() { Duration.ofDays(1).toMillis()); public static final String PREFIX = "/$stapler/bound/"; + public static final String RELEASE_PREFIX = "/$stapler/bound/release/"; static final String SCRIPT_PREFIX = "/$stapler/bound/script"; private static final Logger LOGGER = Logger.getLogger(BoundObjectTable.class.getName());