Changeset 829


Ignore:
Timestamp:
2008-11-26 09:35:34 (5 years ago)
Author:
bruno
Message:

Template engine: KauriHelper? class:

  • this class wasn't reentrant-safe: if a template would include/insert/... a service protocol URI which would in itself cause the execution of a template, then these values would be overwritten but not restored (though: in fact, not sure about this, since the way the things are currently connected, a new thread is created in such cases).
  • similarly, the thread-local variables were not cleared after template execution, disabling garbage collection of everything linked from it
  • the only remaining use of this class was to make the current Request object available for passing the hostref to the KauriModule?.publicUri method, however in that situation we actually need the hostref of the request which arrived on the external connector, and not e.g. the hostref of an internal request.

Therefore:

  • made the current external request available via a thread local variable on the RestserviceManager?, as well as via a method on KauriModule?, so that modules could get access to it.
  • since templates, when executed via TemplateRepresentation?, are only executed after the request processing finished, this thread local variable doesn't have its value any more at that time
  • therefore thought about making it available via the template's ExecutionContext?. To make this one available to KauriFunctions?, introduced another thread local variable.
  • but we can't add Kauri-specific properties on ExecutionContext?, therefore introduced a generic set of execution attributes

Also:

  • added a testcase that checks that publicUri prepends the host name when different from the current external request
  • set up log4j logging in AbstractRuntimeTest? to log warnings and errors to the console

Hmm, seemed like a lot of work for a small problem :-) In fact, we probably need some good thinking about thread local state, internal requests, and propagation of this data when spawning new threads.

Location:
trunk
Files:
1 added
1 deleted
18 edited
1 moved

Legend:

Unmodified
Added
Removed
  • trunk/core/kauri-runtime-rapi/src/main/java/org/kauriproject/runtime/rapi/KauriModule.java

    r808 r829  
    11package org.kauriproject.runtime.rapi; 
    22 
    3 import org.restlet.data.Reference; 
    43import org.restlet.data.Response; 
    54import org.restlet.data.Request; 
     
    1413     * <p>Other URIs can be passed as well, these will be returned unmodified. 
    1514     * 
    16      * <p>The public URI will be made relative to the specified "hostRef". Usually 
    17      * this hostRef should be the one from the original HTTP request. Note that the 
     15     * <p>The public URI will be made relative to the specified "hostIdentifier". Usually 
     16     * this hostIdentifier should be the one from the original HTTP request. Note that the 
    1817     * relativiziation only applies to dropping the scheme and authority parts, the 
    1918     * path part will always stay absolute. 
    2019     */ 
    21     String publicUri(String serviceProtocolUri, Reference hostRef); 
     20    String publicUri(String serviceProtocolUri, String hostIdentifier); 
    2221 
    2322    ModuleSource getSource(); 
     
    5453     */ 
    5554    KauriModule getOriginatingModule(String uri); 
     55 
     56    Request getCurrentExternalRequest(); 
    5657} 
  • trunk/core/kauri-runtime-testfw/src/main/java/org/kauriproject/runtime/testfw/AbstractRuntimeTest.java

    r671 r829  
    1818import org.kauriproject.runtime.repository.RepoArtifactRef; 
    1919import org.kauriproject.runtime.repository.ResolvedArtifact; 
     20import org.apache.log4j.ConsoleAppender; 
     21import org.apache.log4j.PatternLayout; 
     22import org.apache.log4j.Logger; 
     23import org.apache.log4j.Level; 
    2024 
    2125public abstract class AbstractRuntimeTest extends TestCase { 
     
    134138    } 
    135139 
     140    protected void setUpLogging() { 
     141        // TODO might be useful to introduce similar options like for the Runtime CLI 
     142        //      to easily enable more logging for certain categories (could be passed 
     143        //      as system properties, via -D) 
     144        String appenderName = "abstract-runtime-test-console-appender"; 
     145        Logger logger = Logger.getRootLogger(); 
     146        if (logger.getAppender(appenderName) == null) { 
     147            ConsoleAppender consoleAppender = new ConsoleAppender(); 
     148            consoleAppender.setName(appenderName); 
     149            consoleAppender.setLayout(new PatternLayout("[%t] %-5p %c - %m%n")); 
     150            consoleAppender.activateOptions(); 
     151 
     152            logger.setLevel(Level.WARN); 
     153            logger.addAppender(consoleAppender); 
     154        } 
     155    } 
     156 
    136157    protected void setUp() throws Exception { 
     158        setUpLogging(); 
     159 
    137160        KauriRuntimeConfig config = getRuntimeConfig(); 
    138161        if (config != null) { 
  • trunk/core/kauri-runtime-tests/src/test/java/org/kauriproject/runtime/test/ServiceProtocolToPublicTest.java

    r748 r829  
    5050            RestserviceFacet rsf = runtime.getModuleById("sptopublic1").getRestserviceFacet(); 
    5151 
    52             assertEquals("/public1", rsf.getPublicPath("service:/dummy", hostRef)); 
    53             assertEquals("/public1/boe/bie/ba.txt", rsf.getPublicPath("service:/dummy/boe/bie/ba.txt", hostRef)); 
    54             assertEquals("file:///tmp", rsf.getPublicPath("service:/dummy2", hostRef)); 
    55             assertEquals("file:///tmp/yes/no", rsf.getPublicPath("service:/dummy2/yes/no", hostRef)); 
    56             assertEquals("/public2/yes/no", rsf.getPublicPath("service:/dummy3/yes/no", hostRef)); 
     52            assertEquals("/public1", rsf.getPublicPath("service:/dummy", hostRef.getHostIdentifier())); 
     53            assertEquals("/public1/boe/bie/ba.txt", rsf.getPublicPath("service:/dummy/boe/bie/ba.txt", hostRef.getHostIdentifier())); 
     54            assertEquals("file:///tmp", rsf.getPublicPath("service:/dummy2", hostRef.getHostIdentifier())); 
     55            assertEquals("file:///tmp/yes/no", rsf.getPublicPath("service:/dummy2/yes/no", hostRef.getHostIdentifier())); 
     56            assertEquals("/public2/yes/no", rsf.getPublicPath("service:/dummy3/yes/no", hostRef.getHostIdentifier())); 
    5757        } 
    5858 
    5959        { 
    6060            RestserviceFacet rsf = runtime.getModuleById("sptopublic2").getRestserviceFacet(); 
    61             assertEquals("/public2", rsf.getPublicPath("service:/dummy", hostRef)); 
     61            assertEquals("/public2", rsf.getPublicPath("service:/dummy", hostRef.getHostIdentifier())); 
    6262        } 
    6363 
     
    9191 
    9292            // host is same as hostref, host should not be part of result 
    93             assertEquals("/public1", rsf.getPublicPath("service:/dummy", hostRef)); 
     93            assertEquals("/public1", rsf.getPublicPath("service:/dummy", hostRef.getHostIdentifier())); 
    9494 
    9595            // host is different, host should be part of result 
    96             assertEquals("http://otherhost:8888/public2/yes/no", rsf.getPublicPath("service:/dummy3/yes/no", hostRef)); 
     96            assertEquals("http://otherhost:8888/public2/yes/no", rsf.getPublicPath("service:/dummy3/yes/no", hostRef.getHostIdentifier())); 
    9797        } 
    9898 
    9999        { 
    100100            RestserviceFacet rsf = runtime.getModuleById("sptopublic2").getRestserviceFacet(); 
    101             assertEquals("http://otherhost:8888/public2", rsf.getPublicPath("service:/dummy", hostRef)); 
     101            assertEquals("http://otherhost:8888/public2", rsf.getPublicPath("service:/dummy", hostRef.getHostIdentifier())); 
    102102        } 
    103103 
  • trunk/core/kauri-runtime/src/main/java/org/kauriproject/runtime/module/restservice/RestserviceFacet.java

    r808 r829  
    137137    } 
    138138 
    139     public String getPublicPath(String uri, Reference hostRef) { 
     139    public String getPublicPath(String uri, String hostIdentifier) { 
    140140        Matcher matcher = Constants.SERVICE_PROTOCOL_URI_PATTERN.matcher(uri); 
    141141        if (!matcher.matches()) { 
     
    190190 
    191191         
    192         if (!pathIsAbsolute) { 
     192        if (!pathIsAbsolute && hostIdentifier != null) { 
    193193            VirtualHostDefinitions vhds = restserviceManager.getRuntime().getConfig().getVirtualHosts(); 
    194194            VirtualHostDefinition vhd = virtualHostName != null ? vhds.get(virtualHostName) : vhds.getDefault(); 
    195195            String canonicalUri = vhd.getCanonicalUri(); 
    196196 
    197             if (!canonicalUri.equals(hostRef.getHostIdentifier())) { 
     197            if (!canonicalUri.equals(hostIdentifier)) { 
    198198                mountPath = canonicalUri + mountPath; 
    199199            } 
  • trunk/core/kauri-runtime/src/main/java/org/kauriproject/runtime/module/restservice/RestserviceManager.java

    r803 r829  
    44import org.restlet.service.MetadataService; 
    55import org.restlet.data.Protocol; 
     6import org.restlet.data.Request; 
     7import org.restlet.data.Response; 
    68import org.kauriproject.runtime.KauriRTException; 
    79import org.kauriproject.runtime.KauriRuntime; 
     
    2224    public static final Protocol MODULE_PROTOCOL = new Protocol("module", "MODULE", "Kauri module protocol", -1); 
    2325 
     26    /** 
     27     * Holds the current request that arrived via a connector (if any). This will be different 
     28     * from the current request, when internal requests are performed. 
     29     */ 
     30    public static ThreadLocal<Request> CURRENT_EXTERNAL_REQUEST = new ThreadLocal<Request>(); 
     31 
    2432    public RestserviceManager(KauriRuntime runtime) { 
    25         component = new Component(); 
     33        component = new MyComponent(); 
    2634        this.runtime = runtime; 
    2735        this.metadataService = new KauriMetadataService(); 
     36    } 
     37 
     38    private static class MyComponent extends Component { 
     39        @Override 
     40        public void handle(Request request, Response response) { 
     41            CURRENT_EXTERNAL_REQUEST.set(request); 
     42            try { 
     43                super.handle(request, response); 
     44            } finally { 
     45                CURRENT_EXTERNAL_REQUEST.set(null); 
     46            } 
     47        } 
    2848    } 
    2949     
  • trunk/core/kauri-runtime/src/main/java/org/kauriproject/runtime/rapi_impl/KauriModuleImpl.java

    r808 r829  
    77import org.kauriproject.runtime.module.Module; 
    88import org.kauriproject.runtime.module.restservice.RestserviceRegistryEntry; 
     9import org.kauriproject.runtime.module.restservice.RestserviceManager; 
    910import org.kauriproject.runtime.KauriRuntime; 
    1011import org.kauriproject.runtime.Constants; 
     
    2829    } 
    2930 
    30     public String publicUri(String serviceProtocolUri, Reference hostRef) { 
    31         return module.getRestserviceFacet().getPublicPath(serviceProtocolUri, hostRef); 
     31    public String publicUri(String serviceProtocolUri, String hostIdentifier) { 
     32        return module.getRestserviceFacet().getPublicPath(serviceProtocolUri, hostIdentifier); 
    3233    } 
    3334 
     
    9495        return null; 
    9596    } 
     97 
     98    public Request getCurrentExternalRequest() { 
     99        return RestserviceManager.CURRENT_EXTERNAL_REQUEST.get(); 
     100    } 
    96101} 
  • trunk/modules/kauri-representation/kauri-representationbuilder-impl/src/test/java/org/kauriproject/representation/test/BuilderTest.java

    r808 r829  
    2121        // Mocked KauriModule implementation 
    2222        KauriModule kauriModule = new KauriModule() { 
    23             public String publicUri(String serviceProtocolUri, Reference hostRef) { 
     23            public String publicUri(String serviceProtocolUri, String hostRef) { 
    2424                return null; 
    2525            } 
     
    6464 
    6565            public KauriModule getOriginatingModule(String uri) { 
     66                return null; 
     67            } 
     68 
     69            public Request getCurrentExternalRequest() { 
    6670                return null; 
    6771            } 
  • trunk/modules/kauri-template/kauri-template-service-api/src/main/java/org/kauriproject/template/service/TemplateService.java

    r651 r829  
    3838            OutputStream outputStream, MediaType mediaType, Map<String, Object> parameters) throws Exception; 
    3939 
     40    public void generateTemplate(String templateLocation, Context restletContext, Request request, 
     41            OutputStream outputStream, MediaType mediaType, Map<String, Object> parameters, 
     42            Map<String, Object> executionAttributes) throws Exception; 
     43 
    4044    public Representation getTemplateRepresentation(final MediaType mediaType, final Context restletContext, 
    4145            Request request, final String templateLocation, final Map<String, Object> parameters); 
     46 
     47    public Representation getTemplateRepresentation(final MediaType mediaType, final Context restletContext, 
     48            Request request, final String templateLocation, final Map<String, Object> parameters, 
     49            Map<String, Object> executionAttributes); 
    4250} 
  • trunk/modules/kauri-template/kauri-template-service-impl/pom.xml

    r656 r829  
    4949      <groupId>org.kauriproject</groupId> 
    5050      <artifactId>kauri-runtime-testfw</artifactId> 
     51      <scope>test</scope> 
    5152    </dependency> 
    5253  </dependencies> 
  • trunk/modules/kauri-template/kauri-template-service-impl/src/main/java/org/kauriproject/template/service/impl/KauriFunctions.java

    r808 r829  
    1818import org.kauriproject.runtime.rapi.KauriModule; 
    1919import org.kauriproject.template.source.SourceResolver; 
     20import org.kauriproject.template.ExecutionContext; 
     21import org.apache.commons.logging.Log; 
     22import org.apache.commons.logging.LogFactory; 
    2023 
    2124/** 
     
    2427 */ 
    2528public class KauriFunctions { 
    26  
    2729    public static String publicUri(String serviceProtocolUri) { 
    2830        String uriString; 
     
    3436 
    3537            KauriModule kauriModule = sc.getKauriModule(); 
    36             uriString = kauriModule.publicUri(serviceProtocolUri, KauriHelper.getRequest().getHostRef()); 
     38            String hostIdentifier = (String)ExecutionContext.CURRENT.get().getAttributes().get("hostIdentifier"); 
     39            System.out.println("================================================= hostIdentifier = " + hostIdentifier); 
     40            uriString = kauriModule.publicUri(serviceProtocolUri, hostIdentifier); 
    3741        } catch (Exception ex) { 
    38             // TODO: log error 
     42            Log log = LogFactory.getLog(KauriFunctions.class); 
     43            log.error("Could not determine public URI for " + serviceProtocolUri, ex); 
    3944            uriString = "NOT_FOUND"; 
    4045        } 
  • trunk/modules/kauri-template/kauri-template-service-impl/src/main/java/org/kauriproject/template/service/impl/TemplateRepresentation.java

    r278 r829  
    3636    private String templateLocation; 
    3737    private Map<String, Object> parameters; 
    38  
    39     public TemplateRepresentation(final Context restletContext, final Request request, 
    40             final TemplateService templateService, final String templateLocation, 
    41             final Map<String, Object> parameters) { 
    42         this(MediaType.APPLICATION_XML, restletContext, request, templateService, templateLocation, 
    43                 parameters); 
    44     } 
     38    private Map<String, Object> executionAttributes; 
    4539 
    4640    public TemplateRepresentation(final MediaType mediaType, final Context restletContext, 
    4741            final Request request, final TemplateService templateService, final String templateLocation, 
    48             final Map<String, Object> parameters) { 
     42            final Map<String, Object> parameters, Map<String, Object> executionAttributes) { 
    4943        super(mediaType); 
    5044        this.restletContext = restletContext; 
     
    5347        this.templateLocation = templateLocation; 
    5448        this.parameters = parameters; 
     49        this.executionAttributes = executionAttributes; 
    5550    } 
    5651 
     
    5954        try { 
    6055            templateService.generateTemplate(templateLocation, restletContext, request, outputStream, 
    61                     getMediaType(), parameters); 
     56                    getMediaType(), parameters, executionAttributes); 
    6257        } catch (Exception ex) { 
    6358            throw new RuntimeException("Template processing error.", ex); 
  • trunk/modules/kauri-template/kauri-template-service-impl/src/main/java/org/kauriproject/template/service/impl/TemplateServiceImpl.java

    r808 r829  
    1919import java.io.OutputStream; 
    2020import java.util.Map; 
     21import java.util.HashMap; 
    2122 
    2223import org.kauriproject.template.TemplateResultImpl; 
     
    6667    public void generateTemplate(String templateLocation, Context restletContext, Request request, 
    6768            OutputStream outputStream, MediaType mediaType, Map<String, Object> parameters) throws Exception { 
    68         // update kaurihelper with request info 
    69         KauriHelper.init(restletContext, request); 
     69        generateTemplate(templateLocation, restletContext, request, outputStream, mediaType, parameters, null); 
     70    } 
     71 
     72    public void generateTemplate(String templateLocation, Context restletContext, Request request, 
     73            OutputStream outputStream, MediaType mediaType, Map<String, Object> parameters, 
     74            Map<String, Object> executionAttributes) throws Exception { 
    7075        // construct executioncontext 
    7176        SourceResolver sourceResolver = new KauriSourceResolver((KauriModule)restletContext.getAttributes().get("org.kauriproject.runtime.rapi.KauriModule")); 
     
    7782            templateContext.putAll(parameters); 
    7883        } 
     84 
    7985        ExecutionContext executionContext = delegate.createContext(sourceResolver, templateContext, 
    8086                functionRegistry); 
     87 
     88        if (executionAttributes == null) { 
     89            KauriModule kauriModule = (KauriModule)restletContext.getAttributes().get("org.kauriproject.runtime.rapi.KauriModule"); 
     90            executionAttributes = getDefaultExecutionAttributes(kauriModule); 
     91        } 
     92 
     93        executionContext.getAttributes().putAll(executionAttributes); 
     94 
    8195        // ask delegate to generate template 
    8296        delegate.generateTemplate(templateLocation, executionContext, templateResult); 
     
    97111    public Representation getTemplateRepresentation(final MediaType mediaType, final Context restletContext, 
    98112            final Request request, final String templateLocation, final Map<String, Object> parameters) { 
     113        return getTemplateRepresentation(mediaType, restletContext, request, templateLocation, 
     114                parameters, null); 
     115    } 
     116 
     117    public Representation getTemplateRepresentation(MediaType mediaType, Context restletContext, Request request, String templateLocation, Map<String, Object> parameters, Map<String, Object> executionAttributes) { 
     118        if (executionAttributes == null) { 
     119            KauriModule kauriModule = (KauriModule)restletContext.getAttributes().get("org.kauriproject.runtime.rapi.KauriModule"); 
     120            executionAttributes = getDefaultExecutionAttributes(kauriModule); 
     121        } 
     122 
    99123        return new TemplateRepresentation(mediaType, restletContext, request, this, templateLocation, 
    100                 parameters); 
     124                parameters, executionAttributes); 
     125    } 
     126 
     127    public Map<String, Object> getDefaultExecutionAttributes(KauriModule kauriModule) { 
     128        String hostIdentifier = null; 
     129        System.out.println("kauriModule.getCurrentExternalRequest() = " + kauriModule.getCurrentExternalRequest() + " thread " + Thread.currentThread()); 
     130        if (kauriModule.getCurrentExternalRequest() != null) { 
     131            hostIdentifier = kauriModule.getCurrentExternalRequest().getHostRef().toString(); 
     132        } 
     133 
     134        Map<String, Object> attrs = new HashMap<String, Object>(); 
     135        attrs.put("hostIdentifier", hostIdentifier); 
     136 
     137        return attrs; 
    101138    } 
    102139} 
  • trunk/modules/kauri-template/kauri-template-service-impl/src/test/java/org/kauriproject/template/service/test/TemplateServiceTest.java

    r808 r829  
    4747            File moduleDir = createModule("org.kauriproject.template.service.test.testmodules.template1"); 
    4848            ModuleDefinition module = new ModuleDefinition("template1", moduleDir, ModuleSourceType.EXPANDED_JAR); 
    49             module.addMount(new MountDefinition("templates", "/templates", null, true)); 
     49            module.addMount(new MountDefinition("executedtemplates", "/templates", null, true)); 
    5050            module.addMount(new MountDefinition("A", "/A1", null, true)); 
    5151            module.addInject(new RestserviceInjectDefinition("templates2", "template2", "templates")); 
  • trunk/modules/kauri-template/kauri-template-service-impl/src/test/java/org/kauriproject/template/service/test/testmodules/common/Templates.java

    r808 r829  
    1 package org.kauriproject.template.service.test.testmodules.template1; 
     1package org.kauriproject.template.service.test.testmodules.common; 
    22 
    33import org.restlet.Restlet; 
  • trunk/modules/kauri-template/kauri-template-service-impl/src/test/modulesrc/org/kauriproject/template/service/test/testmodules/template1/KAURI-INF/spring/services.xml

    r808 r829  
    2020 
    2121  <kauri:export-restservice ref="A"/> 
    22   <kauri:export-restservice ref="templates"/> 
     22  <kauri:export-restservice ref="executedtemplates"/> 
    2323 
    2424  <bean id="A" class="org.restlet.Directory"> 
     
    2727  </bean> 
    2828 
    29   <bean id="templates" class="org.kauriproject.template.service.test.testmodules.template1.Templates"> 
     29  <bean id="executedtemplates" class="org.kauriproject.template.service.test.testmodules.common.Templates"> 
    3030    <constructor-arg ref="templateService"/> 
    3131    <constructor-arg ref="restletContext"/> 
  • trunk/modules/kauri-template/kauri-template-service-impl/src/test/modulesrc/org/kauriproject/template/service/test/testmodules/template3/KAURI-INF/spring/services.xml

    r808 r829  
    1919  <kauri:export-restservice ref="A"/> 
    2020  <kauri:export-restservice ref="templates"/> 
     21  <kauri:export-restservice ref="executedtemplates"/> 
    2122 
    2223  <bean id="A" class="org.restlet.Directory"> 
     
    3031  </bean> 
    3132 
     33  <bean id="executedtemplates" class="org.kauriproject.template.service.test.testmodules.common.Templates"> 
     34    <constructor-arg ref="templateService"/> 
     35    <constructor-arg ref="restletContext"/> 
     36  </bean> 
    3237</beans> 
  • trunk/universe/kauri-template/src/main/java/org/kauriproject/template/DefaultTemplateExecutor.java

    r816 r829  
    2424 
    2525    public void execute(Step startStep, Step endStep, ExecutionContext context, TemplateResult result) throws SAXException { 
    26         if (context.getExecutor() == null) { 
    27             context.setExecutor(this); 
    28         } 
     26        ExecutionContext oldCurrentContext = ExecutionContext.CURRENT.get(); 
     27        try { 
     28            ExecutionContext.CURRENT.set(context); 
    2929 
    30         Step currentStep = startStep; 
    31         while (currentStep != endStep) { 
    32             currentStep = currentStep.executeAndProceed(context, result); 
     30            if (context.getExecutor() == null) { 
     31                context.setExecutor(this); 
     32            } 
     33 
     34            Step currentStep = startStep; 
     35            while (currentStep != endStep) { 
     36                currentStep = currentStep.executeAndProceed(context, result); 
     37            } 
     38        } finally { 
     39            ExecutionContext.CURRENT.set(oldCurrentContext); 
    3340        } 
    3441    } 
  • trunk/universe/kauri-template/src/main/java/org/kauriproject/template/ExecutionContext.java

    r822 r829  
    2929 */ 
    3030public class ExecutionContext { 
     31    public static ThreadLocal<ExecutionContext> CURRENT = new ThreadLocal<ExecutionContext>(); 
    3132 
    3233    private SourceResolver sourceResolver; 
     
    3637    private FunctionRegistry functionRegistry; 
    3738    private TemplateExecutor executor; 
     39    private Map<String, Object> attributes = new HashMap<String, Object>(); 
    3840 
    3941    private Map<String, MacroBlock> macroRegistry; 
     
    140142    } 
    141143 
     144    /** 
     145     * A free set of attributes. This is intended for template users to make 
     146     * non-predefined data available for during execution, e.g. for use 
     147     * by custom expression language functions. This map should not be 
     148     * modified during template execution.  
     149     */ 
     150    public Map<String, Object> getAttributes() { 
     151        return attributes; 
     152    } 
     153 
    142154    public List<ExtendBlock> getExtendList() { 
    143155        return extendList; 
Note: See TracChangeset for help on using the changeset viewer.