You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rave.apache.org by ca...@apache.org on 2012/03/07 18:09:35 UTC

svn commit: r1298022 - in /incubator/rave/trunk: rave-components/rave-commons/ rave-components/rave-commons/src/main/java/org/apache/rave/model/ rave-components/rave-commons/src/main/java/org/apache/rave/service/ rave-components/rave-commons/src/main/j...

Author: carlucci
Date: Wed Mar  7 17:09:34 2012
New Revision: 1298022

URL: http://svn.apache.org/viewvc?rev=1298022&view=rev
Log:
RAVE-506: Static Content Fetcher

Added:
    incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/model/
    incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/model/StaticContent.java
    incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/StaticContentFetcherService.java
    incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/impl/DefaultStaticContentFetcherService.java
    incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/service/impl/DefaultStaticContentFetcherServiceTest.java
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/StaticContentTag.java
      - copied, changed from r1297542, incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/RegionWidgetTag.java
    incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/tag/StaticContentTagTest.java
Modified:
    incubator/rave/trunk/rave-components/rave-commons/pom.xml
    incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptor.java
    incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java
    incubator/rave/trunk/rave-components/rave-web/src/main/resources/META-INF/rave.tld
    incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptorTest.java
    incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/common/header.jsp

Modified: incubator/rave/trunk/rave-components/rave-commons/pom.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/pom.xml?rev=1298022&r1=1298021&r2=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/pom.xml (original)
+++ incubator/rave/trunk/rave-components/rave-commons/pom.xml Wed Mar  7 17:09:34 2012
@@ -61,6 +61,10 @@
         </dependency>
         <dependency>
             <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
             <artifactId>spring-orm</artifactId>
         </dependency>
         <dependency>
@@ -106,6 +110,11 @@
             <artifactId>aspectjweaver</artifactId>
             <version>1.6.9</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-test</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file

Added: incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/model/StaticContent.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/model/StaticContent.java?rev=1298022&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/model/StaticContent.java (added)
+++ incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/model/StaticContent.java Wed Mar  7 17:09:34 2012
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rave.model;
+
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * POJO representing a static content artifact
+ */
+public class StaticContent {   
+    private String id;
+    private URI location;
+    private String content;
+    // replacementTokens can be used when the static content artifact is a template and needs some
+    // environment specific tokens to be replaced
+    private Map<String, String> replacementTokens;
+
+    public StaticContent(String id, URI location, Map<String, String> replacementTokens) {
+        this.id = id;
+        this.location = location;
+        this.replacementTokens = replacementTokens == null ? new HashMap<String, String>() : replacementTokens;
+        this.content = "";
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+
+    public URI getLocation() {
+        return location;
+    }
+
+    public Map<String, String> getReplacementTokens() {
+        return replacementTokens;
+    }
+
+    @Override
+    public String toString() {
+        return "StaticContent{" + "id=" + id + ", location=" + location + "}";
+    }
+}

Added: incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/StaticContentFetcherService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/StaticContentFetcherService.java?rev=1298022&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/StaticContentFetcherService.java (added)
+++ incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/StaticContentFetcherService.java Wed Mar  7 17:09:34 2012
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rave.service;
+
+/**
+ * Interface for dealing with remote Static Content (such as HTML or JavaScript fragments) so that
+ * Rave can render them directly on pages and improve performance by internally caching the content
+ */
+public interface StaticContentFetcherService {
+    /**
+     * Returns the content from the cache for the supplied key
+     *
+     * @param key the cache key of the content artifact to lookup
+     * @return String representing the static content
+     */
+    String getContent(String key);
+
+    /**
+     * Refreshes all of the cached StaticContent artifacts currently in the cache
+     */
+    void refreshAll();
+
+    /**
+     * Refresh a single StaticContent artifact in the cache based on the supplied key
+     * @param key the cache key of the content artifact to refresh
+     */
+    void refresh(String key);
+}

Added: incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/impl/DefaultStaticContentFetcherService.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/impl/DefaultStaticContentFetcherService.java?rev=1298022&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/impl/DefaultStaticContentFetcherService.java (added)
+++ incubator/rave/trunk/rave-components/rave-commons/src/main/java/org/apache/rave/service/impl/DefaultStaticContentFetcherService.java Wed Mar  7 17:09:34 2012
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.rave.service.impl;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.rave.model.StaticContent;
+import org.apache.rave.service.StaticContentFetcherService;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DefaultStaticContentFetcherService implements StaticContentFetcherService {
+    private static final Log log = LogFactory.getLog(DefaultStaticContentFetcherService.class);
+    private Map<String, StaticContent> contentMap;
+    private RestTemplate restTemplate;
+
+    public DefaultStaticContentFetcherService(RestTemplate restTemplate, List<StaticContent> contentObjects) {
+        this.restTemplate = restTemplate;
+        contentMap = new HashMap<String, StaticContent>();
+        for (StaticContent contentObject : contentObjects) {
+            contentMap.put(contentObject.getId(), contentObject);
+        }
+    }
+
+    @Override
+    public String getContent(String key) {
+        log.debug("getContent(" + key + ")");
+        StaticContent content = contentMap.get(key);
+        if (content == null) {
+            throw new IllegalArgumentException("Invalid content key: " + key);
+        }
+        return content.getContent();
+    }
+
+    @Override
+    public void refreshAll() {
+        log.debug("refreshAll()");
+        for (StaticContent curEntry : contentMap.values()) {
+            refreshFromLocation(curEntry);
+        }
+    }
+
+    @Override
+    public void refresh(String key) {
+        log.debug("refresh(" + key + ")");
+        //if the key exists in the content map, refresh it
+        StaticContent item = contentMap.get(key);
+        if (item != null) {
+            refreshFromLocation(item);
+        }
+    }
+
+    private void refreshFromLocation(StaticContent staticContent) {
+        log.debug("refreshFromLocation() - for " + staticContent);
+        try {
+            //We need an intermediate temp content string here so we don't even accidentally hand out a reference to
+            //a not-fully-token-replaced piece of content.  Very unlikely, but could happen.
+            String tempContent = restTemplate.getForObject(staticContent.getLocation(), String.class);
+            for (Map.Entry<String, String> replacementTokenEntry : staticContent.getReplacementTokens().entrySet()) {
+                tempContent = tempContent.replaceAll(replacementTokenEntry.getKey(), replacementTokenEntry.getValue());
+            }
+            staticContent.setContent(tempContent);
+        } catch (RestClientException e) {
+            //RestClientException handles server errors 5xx, client errors 4xx, and IO errors
+            log.error("Unable to process {" + staticContent.getLocation() + "}", e);
+        }        
+    }
+}

Added: incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/service/impl/DefaultStaticContentFetcherServiceTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/service/impl/DefaultStaticContentFetcherServiceTest.java?rev=1298022&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/service/impl/DefaultStaticContentFetcherServiceTest.java (added)
+++ incubator/rave/trunk/rave-components/rave-commons/src/test/java/org/apache/rave/service/impl/DefaultStaticContentFetcherServiceTest.java Wed Mar  7 17:09:34 2012
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rave.service.impl;
+
+import org.apache.rave.model.StaticContent;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+public class DefaultStaticContentFetcherServiceTest {
+    private RestTemplate restTemplate;
+
+    private final String VALID_URL1 = "http://www.google.com/";
+    private final String VALID_URL1_ID = "google";
+    private final String VALID_URL1_CONTENT = "Bogus www.google.page";
+    private StaticContent VALID_CONTENT_1;
+
+    private final String VALID_URL2 = "http://www.yahoo.com/";
+    private final String VALID_URL2_ID = "yahoo";
+    private final String VALID_URL2_CONTENT = "Bogus www.yahoo.page";
+    private StaticContent VALID_CONTENT_2;
+
+    private final String VALID_URL3 = "http://www.bing.com/";
+    private final String VALID_URL3_ID = "bing";
+    private final String VALID_URL3_CONTENT = "Bogus www.bing.page with replacement tokens: {token1} && {token2}";
+    private final String VALID_URL3_TOKEN_1 = "\\{token1\\}";
+    private final String VALID_URL3_TOKEN_2 = "\\{token2\\}";
+    private final String VALID_URL3_TOKEN_1_REPLACEMENT = "token-1-value";
+    private final String VALID_URL3_TOKEN_2_REPLACEMENT = "token-2-value";
+    private final String VALID_URL3_CONTENT_WITH_REPLACEMENTS = VALID_URL3_CONTENT
+            .replaceAll(VALID_URL3_TOKEN_1, VALID_URL3_TOKEN_1_REPLACEMENT)
+            .replaceAll(VALID_URL3_TOKEN_2, VALID_URL3_TOKEN_2_REPLACEMENT);
+    private StaticContent VALID_CONTENT_3;
+
+    @Before
+    public void setUp() throws URISyntaxException {
+        VALID_CONTENT_1 = new StaticContent(VALID_URL1_ID, new URI(VALID_URL1), null);
+        VALID_CONTENT_2 = new StaticContent(VALID_URL2_ID, new URI(VALID_URL2), null);
+        VALID_CONTENT_3 = new StaticContent(VALID_URL3_ID, new URI(VALID_URL3), null);
+        
+        restTemplate = createNiceMock(RestTemplate.class);
+    }
+
+    @Test
+    public void getContent_validId() throws URISyntaxException {
+        DefaultStaticContentFetcherService service = new DefaultStaticContentFetcherService(restTemplate, Arrays.asList(VALID_CONTENT_1, VALID_CONTENT_2, VALID_CONTENT_3));
+        expectAllRestTemplateGetForObject();        
+        replay(restTemplate);
+        
+        //Initially all content will be blank        
+        assertBlankInitialContent(service);
+        
+        service.refreshAll();
+
+        //Now that the content has been refreshed we should get a valid value
+        assertThat(service.getContent(VALID_URL1_ID), is(VALID_URL1_CONTENT));
+        
+        verify(restTemplate);
+    }
+
+    @Test
+    public void getContent_validIdsWithReplacementTokens() throws URISyntaxException {
+        //Setup our third content object to use the replacement feature and test for it
+        Map<String, String> tokenReplacements = new HashMap<String, String>();
+        tokenReplacements.put(VALID_URL3_TOKEN_1, VALID_URL3_TOKEN_1_REPLACEMENT);
+        tokenReplacements.put(VALID_URL3_TOKEN_2, VALID_URL3_TOKEN_2_REPLACEMENT);
+        VALID_CONTENT_3 = new StaticContent(VALID_URL3_ID, new URI(VALID_URL3), tokenReplacements);
+
+        expectAllRestTemplateGetForObject();
+        replay(restTemplate);
+
+        DefaultStaticContentFetcherService service = new DefaultStaticContentFetcherService(restTemplate, Arrays.asList(VALID_CONTENT_1, VALID_CONTENT_2, VALID_CONTENT_3));
+
+        service.refreshAll();
+
+        assertThat(service.getContent(VALID_URL1_ID), is(VALID_URL1_CONTENT));
+        assertThat(service.getContent(VALID_URL2_ID), is(VALID_URL2_CONTENT));
+        assertThat(service.getContent(VALID_URL3_ID), is(VALID_URL3_CONTENT_WITH_REPLACEMENTS));
+        
+        verify(restTemplate);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void getContent_invalidId() {
+        DefaultStaticContentFetcherService service = new DefaultStaticContentFetcherService(restTemplate, Arrays.asList(VALID_CONTENT_1, VALID_CONTENT_2, VALID_CONTENT_3));
+        service.getContent("INVALID");
+    }
+
+    @Test
+    public void refreshAll() throws URISyntaxException {
+        DefaultStaticContentFetcherService service = new DefaultStaticContentFetcherService(restTemplate, Arrays.asList(VALID_CONTENT_1, VALID_CONTENT_2, VALID_CONTENT_3));
+
+        assertBlankInitialContent(service);
+
+        expectAllRestTemplateGetForObject();
+        replay(restTemplate);
+
+        service.refreshAll();
+
+        //Now that the content has been refreshed we should get a valid value
+        assertThat(service.getContent(VALID_URL1_ID), is(VALID_URL1_CONTENT));
+        assertThat(service.getContent(VALID_URL2_ID), is(VALID_URL2_CONTENT));
+        assertThat(service.getContent(VALID_URL3_ID), is(VALID_URL3_CONTENT));
+    }
+
+    @Test
+    public void refresh_invalidKey() throws URISyntaxException {
+        DefaultStaticContentFetcherService service = new DefaultStaticContentFetcherService(restTemplate, Arrays.asList(VALID_CONTENT_1, VALID_CONTENT_2, VALID_CONTENT_3));
+
+        assertBlankInitialContent(service);
+
+        expectAllRestTemplateGetForObject();
+        replay(restTemplate);
+
+        service.refresh("INVALID_CACHE_KEY");
+
+        //Now that the content has been refreshed we should get a valid value
+        assertThat(service.getContent(VALID_URL1_ID), is(""));
+        assertThat(service.getContent(VALID_URL2_ID), is(""));
+        assertThat(service.getContent(VALID_URL3_ID), is(""));
+    }
+
+    @Test
+    public void refresh_validKey() throws URISyntaxException {
+        DefaultStaticContentFetcherService service = new DefaultStaticContentFetcherService(restTemplate, Arrays.asList(VALID_CONTENT_1, VALID_CONTENT_2, VALID_CONTENT_3));
+
+        assertBlankInitialContent(service);
+
+        expectAllRestTemplateGetForObject();
+        replay(restTemplate);
+
+        service.refresh(VALID_URL2_ID);
+
+        //Now that the content has been refreshed we should get a valid value
+        assertThat(service.getContent(VALID_URL1_ID), is(""));
+        assertThat(service.getContent(VALID_URL2_ID), is(VALID_URL2_CONTENT));
+        assertThat(service.getContent(VALID_URL3_ID), is(""));
+    }
+
+    @Test
+    public void refresh_fetchErrorExistingValidContentPreserved() throws URISyntaxException {
+        //Create the fetcher, refresh the content and then validate that we have the expected content
+        DefaultStaticContentFetcherService service = new DefaultStaticContentFetcherService(restTemplate, Arrays.asList(VALID_CONTENT_1, VALID_CONTENT_2, VALID_CONTENT_3));
+
+        expectAllRestTemplateGetForObject();
+        replay(restTemplate);
+        
+        service.refreshAll();
+
+        assertThat(service.getContent(VALID_URL1_ID), is(VALID_URL1_CONTENT));
+        assertThat(service.getContent(VALID_URL2_ID), is(VALID_URL2_CONTENT));
+        assertThat(service.getContent(VALID_URL3_ID), is(VALID_URL3_CONTENT));
+
+        //Now create a new RestTemplate mock and have it return new content for two of the items and throw an exception
+        //for the third -- in this case the two items should have the new content but the third with the exception should
+        //retain the old good cached content.
+        String newContent = "new content from refresh";
+        restTemplate = createNiceMock(RestTemplate.class);
+        expect(restTemplate.getForObject(new URI(VALID_URL1), String.class)).andReturn(newContent);
+        expect(restTemplate.getForObject(new URI(VALID_URL2), String.class)).andThrow(new RestClientException("Boom"));
+        expect(restTemplate.getForObject(new URI(VALID_URL3), String.class)).andReturn(newContent);
+        replay(restTemplate);
+        //Use reflection to stuff the new RestTemplate instance into our existing content fetcher instance
+        ReflectionTestUtils.setField(service, "restTemplate", restTemplate);
+
+        service.refreshAll();
+
+        assertThat(service.getContent(VALID_URL1_ID), is(newContent));
+        assertThat(service.getContent(VALID_URL2_ID), is(VALID_URL2_CONTENT));
+        assertThat(service.getContent(VALID_URL3_ID), is(newContent));
+    }    
+
+    private void expectAllRestTemplateGetForObject() throws URISyntaxException {
+        expect(restTemplate.getForObject(new URI(VALID_URL1), String.class)).andReturn(VALID_URL1_CONTENT);
+        expect(restTemplate.getForObject(new URI(VALID_URL2), String.class)).andReturn(VALID_URL2_CONTENT);
+        expect(restTemplate.getForObject(new URI(VALID_URL3), String.class)).andReturn(VALID_URL3_CONTENT);
+    }
+
+    private void assertBlankInitialContent(DefaultStaticContentFetcherService service) {
+        assertThat(service.getContent(VALID_URL1_ID), is(""));
+        assertThat(service.getContent(VALID_URL2_ID), is(""));
+        assertThat(service.getContent(VALID_URL3_ID), is(""));
+    }
+}

Modified: incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml?rev=1298022&r1=1298021&r2=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml (original)
+++ incubator/rave/trunk/rave-components/rave-core/src/main/resources/org/apache/rave/core-applicationContext.xml Wed Mar  7 17:09:34 2012
@@ -124,13 +124,13 @@
         </property>-->
     </bean>
     <!--
-     NOTE: to use mail session you'll need to configure following within catalina_home/conf/context.xml
-        <Resource name="mail/Session" auth="Container" type="javax.mail.Session" mail.smtp.host="my.mail.host"/>
+    NOTE: to use mail session you'll need to configure following within catalina_home/conf/context.xml
+    <Resource name="mail/Session" auth="Container" type="javax.mail.Session" mail.smtp.host="my.mail.host"/>
 
-        Further, activation & mail jars needs to be placed within catalina_home/lib folder
-     -->
-  <!--
-  <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+    Further, activation & mail jars needs to be placed within catalina_home/lib folder
+    -->
+    <!--
+    <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
         <property name="session" ref="mailSession"/>
     </bean>
     <bean id="mailSession" class="org.springframework.jndi.JndiObjectFactoryBean">
@@ -138,4 +138,53 @@
     </bean>
     -->
 
+    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate" />
+
+    <bean id="staticContentCache" class="org.apache.rave.service.impl.DefaultStaticContentFetcherService">
+        <constructor-arg ref="restTemplate"/>
+        <constructor-arg>
+            <list>
+                <!-- example of a Static Content source that doesn't have any string token placeholders in its content body
+                <bean class="org.apache.rave.model.StaticContent">
+                    <constructor-arg index="0" value="standardCompanyHeader"/>
+                    <constructor-arg index="1" value="${company.header.host}/content/standard_header.html"/>
+                    <constructor-arg index="2">
+                        <null/>
+                    </constructor-arg>
+                </bean>
+                -->
+                <!-- example of a Static Content source that has string token placeholders
+                <bean class="org.apache.rave.model.StaticContent">
+                    <constructor-arg index="0" value="environmentSpecificContent"/>
+                    <constructor-arg index="1" value="${company.header.host}/content/footer.html"/>
+                    <constructor-arg index="2">
+                        <map>
+                            <entry key="\{supportEmail\}" value="${raveproperty.supportemail}"/>
+                            <entry key="\{productVersion\}" value="${raveproperty.version}"/>
+                        </map>
+                    </constructor-arg>
+                </bean>
+                -->
+            </list>
+        </constructor-arg>
+    </bean>
+
+    <!-- example on how to setup a Spring Timer to refresh the Static Content cache at a fixed interval
+    <bean id="refreshStaticContentCacheScheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
+        <property name="delay" value="5000"/>
+        <property name="period" value="300000"/>
+        <property name="timerTask">
+            <bean class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean"
+                  p:targetObject-ref="staticContentCache" p:targetMethod="refreshAll"/>
+        </property>
+    </bean>
+    <bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
+        <property name="daemon" value="true"/>
+        <property name="scheduledTimerTasks">
+            <list>
+                <ref local="refreshStaticContentCacheScheduledTask"/>
+            </list>
+        </property>
+    </bean>
+    -->
 </beans>
\ No newline at end of file

Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptor.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptor.java?rev=1298022&r1=1298021&r2=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptor.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptor.java Wed Mar  7 17:09:34 2012
@@ -21,6 +21,7 @@ package org.apache.rave.portal.web.inter
 
 import org.apache.rave.portal.service.PortalPreferenceService;
 import org.apache.rave.portal.web.util.ModelKeys;
+import org.apache.rave.service.StaticContentFetcherService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.ui.ModelMap;
@@ -36,10 +37,12 @@ import javax.servlet.http.HttpServletRes
 public class CommonModelHandlerInterceptor extends HandlerInterceptorAdapter {
 
     private final PortalPreferenceService preferenceService;
+    private final StaticContentFetcherService staticContentFetcherService;
 
     @Autowired
-    public CommonModelHandlerInterceptor(PortalPreferenceService preferenceService) {
+    public CommonModelHandlerInterceptor(PortalPreferenceService preferenceService, StaticContentFetcherService staticContentFetcherService) {
         this.preferenceService = preferenceService;
+        this.staticContentFetcherService = staticContentFetcherService;
     }
 
     @Override
@@ -47,6 +50,7 @@ public class CommonModelHandlerIntercept
         if (modelAndView != null) {
             ModelMap map = modelAndView.getModelMap();
             map.addAttribute(ModelKeys.PORTAL_SETTINGS, preferenceService.getPreferencesAsMap());
+            map.addAttribute(ModelKeys.STATIC_CONTENT_CACHE, staticContentFetcherService);
         }
         super.postHandle(request, response, handler, modelAndView);
     }

Copied: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/StaticContentTag.java (from r1297542, incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/RegionWidgetTag.java)
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/StaticContentTag.java?p2=incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/StaticContentTag.java&p1=incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/RegionWidgetTag.java&r1=1297542&r2=1298022&rev=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/RegionWidgetTag.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/tag/StaticContentTag.java Wed Mar  7 17:09:34 2012
@@ -16,73 +16,40 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
 package org.apache.rave.portal.web.tag;
 
-import org.apache.commons.lang.StringEscapeUtils;
-import org.apache.rave.portal.model.RegionWidget;
-import org.apache.rave.portal.web.renderer.RenderScope;
-import org.apache.rave.portal.web.renderer.RenderService;
-import org.apache.rave.portal.web.renderer.ScriptLocation;
-import org.apache.rave.portal.web.renderer.ScriptManager;
-
+import org.apache.rave.service.StaticContentFetcherService;
 import javax.servlet.jsp.JspException;
 
 /**
- * JSP tag that renders a RegionWidget
+ * JSP tag that renders a block of static content from the StaticContentFetcherService cache
  */
-public class RegionWidgetTag extends AbstractContextAwareSingletonBeanDependentTag<RenderService> {
-
-    private RegionWidget regionWidget;
+public class StaticContentTag extends AbstractContextAwareSingletonBeanDependentTag<StaticContentFetcherService> {
+    private String contentKey;
 
-    // Script block for disabled gadget
-    private static final String DISABLED_SCRIPT_BLOCK =
-            "<script>rave.registerWidget(widgetsByRegionIdMap, %1$s, {type: 'DISABLED'," +
-            " regionWidgetId: %2$s," +
-            " disabledMessage: '%3$s'," +
-            " collapsed: %4$s," +
-            " widgetId: %5$s});</script>";
-
-    public RegionWidgetTag() {
-        super(RenderService.class);
+    public StaticContentTag() {
+        super(StaticContentFetcherService.class);
     }
 
-    public RegionWidget getRegionWidget() {
-        return regionWidget;
+    public String getContentKey() {
+        return contentKey;
     }
 
-    public void setRegionWidget(RegionWidget regionWidget) {
-        this.regionWidget = regionWidget;
+    public void setContentKey(String contentKey) {
+        this.contentKey = contentKey;
     }
 
-    /**
-     * Delegates rendering of the RegionWidget to the RenderService
-     *
-     * @return EVAL_BODY_INCLUDE if no exception is thrown
-     * @throws JspException if the regionWidget is not set or is not supported by the renderService.
-     */
     @Override
     public int doStartTag() throws JspException {
-
-        if (regionWidget != null && getBean().getSupportedWidgetTypes().contains(regionWidget.getWidget().getType()) ) {
-            if ( regionWidget.getWidget().isDisableRendering() ) {
-                ScriptManager scriptManager = getBeanFromContext(ScriptManager.class);
-                String widgetScript = String.format(DISABLED_SCRIPT_BLOCK, regionWidget.getRegion().getEntityId(),
-                        regionWidget.getEntityId(),
-                        StringEscapeUtils.escapeJavaScript(regionWidget.getWidget().getDisableRenderingMessage()),
-                        regionWidget.isCollapsed(),
-                        regionWidget.getWidget().getEntityId());
-                scriptManager.registerScriptBlock(widgetScript, ScriptLocation.AFTER_RAVE, RenderScope.CURRENT_REQUEST, getContext());
-            } else {
-                writeString(getBean().render(regionWidget, getContext()));
-            }
+        if (contentKey != null) {
+            writeString(getBean().getContent(contentKey));
         }
         else {
-                throw new JspException("Unsupported regionWidget type or regionWidget not set: " + regionWidget);
+            throw new JspException("contentKey can't be null");
         }
-        //Certain JSP implementations use tag pools.  Setting the regionWidget to null ensures that there is no chance a given tag
-        //will accidentally re-use a region widget if the attribute in the JSP is empty
-        regionWidget = null;
+        //Certain JSP implementations use tag pools.  Setting the contentKey to null ensures that there is no chance a given tag
+        //will accidentally re-use a contentKey if the attribute in the JSP is empty
+        contentKey = null;
         return EVAL_BODY_INCLUDE;
     }
-}
+}
\ No newline at end of file

Modified: incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java?rev=1298022&r1=1298021&r2=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/java/org/apache/rave/portal/web/util/ModelKeys.java Wed Mar  7 17:09:34 2012
@@ -51,4 +51,5 @@ public class ModelKeys {
     public static final String DEFAULT_TAG_PAGE = "defaultTagPage";
     public static final String CAPTCHA_HTML = "captchaHtml";
     public static final String REDIRECT_MESSAGE = "message";
+    public static final String STATIC_CONTENT_CACHE = "staticContentCache";
 }
\ No newline at end of file

Modified: incubator/rave/trunk/rave-components/rave-web/src/main/resources/META-INF/rave.tld
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/main/resources/META-INF/rave.tld?rev=1298022&r1=1298021&r2=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/main/resources/META-INF/rave.tld (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/main/resources/META-INF/rave.tld Wed Mar  7 17:09:34 2012
@@ -50,4 +50,18 @@
             <type>org.apache.rave.portal.web.renderer.ScriptLocation</type>
         </attribute>
     </tag>
+    <tag>
+        <description>Renders a static content fragment based on the cache key</description>
+        <name>render-static-content</name>
+        <tag-class>org.apache.rave.portal.web.tag.StaticContentTag</tag-class>
+        <body-content>JSP</body-content>
+        <attribute>
+            <description>The cache key of the static content to render</description>
+            <name>contentKey</name>
+            <required>true</required>
+            <rtexprvalue>true</rtexprvalue>
+            <type>java.lang.String</type>
+        </attribute>
+    </tag>
+
 </taglib>

Modified: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptorTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptorTest.java?rev=1298022&r1=1298021&r2=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptorTest.java (original)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/interceptors/CommonModelHandlerInterceptorTest.java Wed Mar  7 17:09:34 2012
@@ -22,6 +22,7 @@ package org.apache.rave.portal.web.inter
 import org.apache.rave.portal.model.PortalPreference;
 import org.apache.rave.portal.service.PortalPreferenceService;
 import org.apache.rave.portal.web.util.ModelKeys;
+import org.apache.rave.service.StaticContentFetcherService;
 import org.junit.Before;
 import org.junit.Test;
 import org.springframework.mock.web.MockHttpServletRequest;
@@ -31,12 +32,9 @@ import org.springframework.web.servlet.M
 import java.util.HashMap;
 import java.util.Map;
 
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.*;
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.CoreMatchers.*;
 
 /**
  * Test for {@link CommonModelHandlerInterceptor}
@@ -45,11 +43,14 @@ public class CommonModelHandlerIntercept
 
     CommonModelHandlerInterceptor interceptor;
     PortalPreferenceService portalPreferenceService;
+    StaticContentFetcherService staticContentFetcherService;
 
     @Before
     public void setUp() throws Exception {
         portalPreferenceService = createMock(PortalPreferenceService.class);
-        interceptor = new CommonModelHandlerInterceptor(portalPreferenceService);
+        staticContentFetcherService = createMock(StaticContentFetcherService.class);
+        
+        interceptor = new CommonModelHandlerInterceptor(portalPreferenceService, staticContentFetcherService);
     }
 
     @Test
@@ -66,7 +67,9 @@ public class CommonModelHandlerIntercept
         interceptor.postHandle(request, response, handler, modelAndView);
         verify(portalPreferenceService);
 
-        assertTrue(modelAndView.getModelMap().containsKey(ModelKeys.PORTAL_SETTINGS));
+        Map<String, Object> modelMap = modelAndView.getModelMap();
+        assertThat((Map<String, PortalPreference>) modelMap.get(ModelKeys.PORTAL_SETTINGS), sameInstance(preferenceMap));
+        assertThat((StaticContentFetcherService) modelMap.get(ModelKeys.STATIC_CONTENT_CACHE), sameInstance(staticContentFetcherService));
     }
     @Test
     public void testPostHandle_noModelAndView() throws Exception {
@@ -76,6 +79,6 @@ public class CommonModelHandlerIntercept
         ModelAndView modelAndView = null;
 
         interceptor.postHandle(request, response, handler, modelAndView);
-        assertNull(modelAndView);
+        assertThat(modelAndView, is(nullValue(ModelAndView.class)));
     }
 }

Added: incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/tag/StaticContentTagTest.java
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/tag/StaticContentTagTest.java?rev=1298022&view=auto
==============================================================================
--- incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/tag/StaticContentTagTest.java (added)
+++ incubator/rave/trunk/rave-components/rave-web/src/test/java/org/apache/rave/portal/web/tag/StaticContentTagTest.java Wed Mar  7 17:09:34 2012
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.rave.portal.web.tag;
+
+
+import org.apache.rave.service.StaticContentFetcherService;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.web.context.WebApplicationContext;
+
+import javax.servlet.ServletContext;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import java.io.IOException;
+
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+public class StaticContentTagTest {
+    private StaticContentFetcherService service;
+    private StaticContentTag tag;
+    private WebApplicationContext wContext;
+    private PageContext pageContext;
+    private ServletContext servletContext;
+    private JspWriter writer;
+
+    private final String VALID_CACHE_KEY = "myCacheKey";
+    private final String VALID_STATIC_CONTENT = "the content of the static content artifact";
+    
+    @Before
+    public void setup() throws JspException {        
+        service = createMock(StaticContentFetcherService.class);
+        wContext = createNiceMock(WebApplicationContext.class);
+        pageContext = createNiceMock(PageContext.class);
+        servletContext = createNiceMock(ServletContext.class);
+        writer = createNiceMock(JspWriter.class);
+
+        tag = new StaticContentTag();
+        tag.setPageContext(pageContext);
+    }
+
+    @Test
+    public void doStartTag_validKey() throws IOException, JspException {
+        tag.setContentKey(VALID_CACHE_KEY);
+        
+        expect(service.getContent(VALID_CACHE_KEY)).andReturn(VALID_STATIC_CONTENT);
+        expect(pageContext.getServletContext()).andReturn(servletContext).anyTimes();
+        expect(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)).andReturn(wContext).anyTimes();
+        expect(wContext.getBean(StaticContentFetcherService.class)).andReturn(service).anyTimes();
+        expect(pageContext.getOut()).andReturn(writer);
+        replay(service, pageContext, servletContext, wContext, writer);
+
+        int result = tag.doStartTag();
+
+        assertThat(result, is(javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE));
+        verify(service, pageContext, servletContext, wContext, writer);
+    }
+
+    @Test(expected = JspException.class)
+    public void doStartTag_nullKey() throws IOException, JspException {
+        tag.setContentKey(null);
+        tag.doStartTag();
+    }
+    
+    @Test
+    public void getCacheKey() {
+        assertThat(tag.getContentKey(), is(nullValue(String.class)));
+        tag.setContentKey(VALID_CACHE_KEY);
+        assertThat(tag.getContentKey(), is(VALID_CACHE_KEY));
+    }
+}

Modified: incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/common/header.jsp
URL: http://svn.apache.org/viewvc/incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/common/header.jsp?rev=1298022&r1=1298021&r2=1298022&view=diff
==============================================================================
--- incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/common/header.jsp (original)
+++ incubator/rave/trunk/rave-portal-resources/src/main/webapp/WEB-INF/jsp/common/header.jsp Wed Mar  7 17:09:34 2012
@@ -16,4 +16,9 @@ KIND, either express or implied.  See th
 specific language governing permissions and limitations
 under the License.
 --%>
-<%-- Common header will go here... --%>
\ No newline at end of file
+<%-- Common header will go here... --%>
+
+<%-- example on how to render static content from the cache using the tag library
+<%@ include file="/WEB-INF/jsp/includes/taglibs.jsp" %>
+<portal:render-static-content contentKey="standardCompanyHeader" />
+--%>
\ No newline at end of file