Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast index view search #102

Merged
merged 5 commits into from
Jan 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/src/main/java/org/kohsuke/stapler/Facet.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,17 @@ public abstract class Facet {
*/
public abstract void buildViewDispatchers(MetaClass owner, List<Dispatcher> dispatchers);

/**
* Adds {@link Dispatcher}s that serves the likes of {@code index.EXT}
*
* The default implementation invokes {@link #handleIndexRequest(RequestImpl, ResponseImpl, Object, MetaClass)}
* but facet implementations can improve runtime dispatch performance by testing the presence
* of index view page upfront.
*/
public void buildIndexDispatchers(MetaClass owner, List<Dispatcher> dispatchers) {
dispatchers.add(new IndexViewDispatcher(owner,this));
}

/**
* Adds {@link Dispatcher}s that do catch-all behaviours like "doDispatch" does.
*/
Expand Down
51 changes: 51 additions & 0 deletions core/src/main/java/org/kohsuke/stapler/IndexHtmlDispatcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.kohsuke.stapler;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

/**
* Serve {@code index.html} from {@code WEB-INF/side-files} if that exists.
*
* @author Kohsuke Kawaguchi
*/
class IndexHtmlDispatcher extends Dispatcher {
private final URL html;

IndexHtmlDispatcher(URL html) {
this.html = html;
}

@Override
public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object node) throws IOException, ServletException {
if (!req.tokens.hasMore()) {
rsp.serveFile(req, html, 0);
return true;
}
return false;
}

@Override
public String toString() {
return "index.html for url=/";
}

/**
* Returns a {@link IndexHtmlDispatcher} if and only if the said class has {@code index.html} as a side-file
*/
static Dispatcher make(ServletContext context, Class c) {
for (; c != Object.class; c = c.getSuperclass()) {
String name = "/WEB-INF/side-files/" + c.getName().replace('.', '/') + "/index.html";
try {
URL url = context.getResource(name);
if (url != null)
return new IndexHtmlDispatcher(url);
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
}
return null;
}
}
30 changes: 5 additions & 25 deletions core/src/main/java/org/kohsuke/stapler/IndexViewDispatcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import javax.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;

/**
* {@link Dispatcher} that deals with the "index" view pages that are used when the request path doesn't contain
Expand All @@ -17,41 +15,23 @@
*/
class IndexViewDispatcher extends Dispatcher {
private final MetaClass metaClass;
private final Facet facet;

IndexViewDispatcher(MetaClass metaClass) {
IndexViewDispatcher(MetaClass metaClass, Facet facet) {
this.metaClass = metaClass;
this.facet = facet;
}

@Override
public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object node) throws IOException, ServletException, IllegalAccessException, InvocationTargetException {
if (req.tokens.hasMore())
return false;

for (Facet f : metaClass.webApp.facets) {
if (f.handleIndexRequest(req, rsp, node, metaClass))
return true;
}

URL indexHtml = getSideFileURL(req.stapler, node, "index.html");
if (indexHtml != null) {
rsp.serveFile(req, indexHtml, 0);
return true; // done
}

return false;
}

private URL getSideFileURL(Stapler stapler, Object node, String fileName) throws MalformedURLException {
for (Class c = node.getClass(); c != Object.class; c = c.getSuperclass()) {
String name = "/WEB-INF/side-files/" + c.getName().replace('.', '/') + '/' + fileName;
URL url = stapler.getResource(name);
if (url != null) return url;
}
return null;
return facet.handleIndexRequest(req, rsp, node, metaClass);
}

@Override
public String toString() {
return "index views for url=/";
return "index view of "+facet+" for url=/";
}
}
14 changes: 12 additions & 2 deletions core/src/main/java/org/kohsuke/stapler/MetaClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class MetaClass extends TearOffSupport {
*/
public final MetaClassLoader classLoader;

public final List<Dispatcher> dispatchers = new ArrayList<Dispatcher>();
public final List<Dispatcher> dispatchers = new ArrayList<>();

/**
* Base metaclass.
Expand Down Expand Up @@ -166,7 +166,12 @@ public String toString() {
for (Facet f : webApp.facets)
f.buildViewDispatchers(this, dispatchers);

dispatchers.add(new IndexViewDispatcher(this));
for (Facet f : webApp.facets)
f.buildIndexDispatchers(this, dispatchers);

Dispatcher d = IndexHtmlDispatcher.make(webApp.context, clazz);
if (d!=null)
dispatchers.add(d);

// check public properties of the form NODE.TOKEN
for (final FieldRef f : node.fields) {
Expand Down Expand Up @@ -428,6 +433,11 @@ private String getProtectedRole(FieldRef f) {
}
}

@Override
public String toString() {
return "MetaClass["+klass+"]";
}

private static String camelize(String name) {
return Character.toLowerCase(name.charAt(0))+name.substring(1);
}
Expand Down
11 changes: 9 additions & 2 deletions core/src/main/java/org/kohsuke/stapler/StaticViewFacet.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,17 @@ public void include(ServletRequest request, ServletResponse response) throws Ser
};
}

@Override
public void buildIndexDispatchers(MetaClass owner, List<Dispatcher> dispatchers) {
URL res = findResource(owner.klass, "index.html");
if (res!=null) {
dispatchers.add(new IndexHtmlDispatcher(res));
}
}

public boolean handleIndexRequest(RequestImpl req, ResponseImpl rsp, Object node, MetaClass nodeMetaClass) throws IOException, ServletException {
URL res = findResource(nodeMetaClass.klass,"index.html");
if (res==null) return false;
rsp.serveFile(req,res);
return true;
return new IndexHtmlDispatcher(res).dispatch(req,rsp,node);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

package org.kohsuke.stapler.jelly.groovy;

import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.Script;
import org.kohsuke.MetaInfServices;
import org.kohsuke.stapler.Dispatcher;
Expand All @@ -31,6 +32,7 @@
import org.kohsuke.stapler.RequestImpl;
import org.kohsuke.stapler.ResponseImpl;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.jelly.JellyClassTearOff;
import org.kohsuke.stapler.jelly.JellyCompatibleFacet;
import org.kohsuke.stapler.jelly.JellyFacet;
import org.kohsuke.stapler.lang.Klass;
Expand All @@ -42,6 +44,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;

/**
* {@link Facet} that brings in Groovy support on top of Jelly.
Expand Down Expand Up @@ -114,6 +117,17 @@ private RequestDispatcher createDispatcher(Object it, String viewName, MetaClass
return d;
}

@Override
public void buildIndexDispatchers(MetaClass owner, List<Dispatcher> dispatchers) {
try {
if (owner.loadTearOff(JellyClassTearOff.class).findScript("index")!=null) {
super.buildIndexDispatchers(owner, dispatchers);
}
} catch (JellyException e) {
LOGGER.log(Level.WARNING, "Failed to parse index.groovy for "+owner, e);
}
}

public boolean handleIndexRequest(RequestImpl req, ResponseImpl rsp, Object node, MetaClass nodeMetaClass) throws IOException, ServletException {
RequestDispatcher d = createDispatcher(node,"index", nodeMetaClass);

Expand Down
13 changes: 13 additions & 0 deletions jelly/src/main/java/org/kohsuke/stapler/jelly/JellyFacet.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

package org.kohsuke.stapler.jelly;

import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.expression.ExpressionFactory;
import org.kohsuke.MetaInfServices;
Expand All @@ -43,6 +44,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;

/**
* {@link Facet} that adds Jelly as the view.
Expand Down Expand Up @@ -110,6 +112,17 @@ public String toString() {
});
}

@Override
public void buildIndexDispatchers(MetaClass owner, List<Dispatcher> dispatchers) {
try {
if (owner.loadTearOff(JellyClassTearOff.class).findScript("index.jelly")!=null) {
super.buildIndexDispatchers(owner, dispatchers);
}
} catch (JellyException e) {
LOGGER.log(Level.WARNING, "Failed to parse index.jelly for "+owner, e);
}
}

public Collection<Class<JellyClassTearOff>> getClassTearOffTypes() {
return TEAROFF_TYPES;
}
Expand Down
5 changes: 5 additions & 0 deletions jrebel/src/main/java/org/kohsuke/stapler/JRebelFacet.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ public RequestDispatcher createRequestDispatcher(RequestImpl request, Klass<?> t
return null;
}

@Override
public void buildIndexDispatchers(MetaClass owner, List<Dispatcher> dispatchers) {
// no-op
}

@Override
public boolean handleIndexRequest(RequestImpl req, ResponseImpl rsp, Object node, MetaClass nodeMetaClass) {
return false;
Expand Down
67 changes: 53 additions & 14 deletions jruby/src/main/java/org/kohsuke/stapler/jelly/jruby/JRubyFacet.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;

import static java.util.logging.Level.FINE;

/**
* {@link Facet} that adds Ruby-based view technologies.
*
Expand Down Expand Up @@ -149,24 +151,61 @@ public RequestDispatcher createRequestDispatcher(RequestImpl request, Klass<?> t
return mc.loadTearOff(ERbClassTearOff.class).createDispatcher(it,viewName);
}

public boolean handleIndexRequest(RequestImpl req, ResponseImpl rsp, Object node, MetaClass mc) throws IOException, ServletException {
private ScriptDispatcher makeIndexDispatcher(MetaClass mc) throws IOException {
for (Class<? extends AbstractRubyTearOff> t : getClassTearOffTypes()) {
AbstractRubyTearOff rt = mc.loadTearOff(t);
Script script = rt.findScript("index");
if(script!=null) {
try {
if(LOGGER.isLoggable(Level.FINE))
LOGGER.fine("Invoking index"+rt.getDefaultScriptExtension()+" on " + node);
WebApp.getCurrent().getFacet(JellyFacet.class).scriptInvoker.invokeScript(req, rsp, script, node);
return true;
} catch (JellyTagException e) {
throw new ServletException(e);
}
}
final AbstractRubyTearOff rt = mc.loadTearOff(t);
final Script script = rt.findScript("index");
if(script!=null)
return new ScriptDispatcher(rt, script);
}
return null;
}

return false;
@Override
public void buildIndexDispatchers(MetaClass mc, List<Dispatcher> dispatchers) {
try {
ScriptDispatcher d = makeIndexDispatcher(mc);
if (d!=null)
dispatchers.add(d);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Failed to parse Ruby index view for "+mc, e);
}
}

public boolean handleIndexRequest(RequestImpl req, ResponseImpl rsp, Object node, MetaClass mc) throws IOException, ServletException {
ScriptDispatcher d = makeIndexDispatcher(mc);
return d!=null && d.dispatch(req,rsp,node);
}

private static class ScriptDispatcher extends Dispatcher {
private final AbstractRubyTearOff rt;
private final Script script;

public ScriptDispatcher(AbstractRubyTearOff rt, Script script) {
this.rt = rt;
this.script = script;
}

@Override
public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object node) throws IOException, ServletException {
try {
if (req.tokens.hasMore())
return false;

if(LOGGER.isLoggable(FINE))
LOGGER.fine("Invoking index"+ rt.getDefaultScriptExtension()+" on " + node);

WebApp.getCurrent().getFacet(JellyFacet.class).scriptInvoker.invokeScript(req, rsp, script, node);
return true;
} catch (JellyTagException e) {
throw new ServletException(e);
}
}

@Override
public String toString() {
return "index"+ rt.getDefaultScriptExtension()+" for url=/";
}
}
}