You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by th...@apache.org on 2014/06/30 20:08:43 UTC

git commit: TAP5-1515 : Support for external assets

Repository: tapestry-5
Updated Branches:
  refs/heads/master ef7f9c43e -> 37a801d97


TAP5-1515 : Support for external assets

Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/37a801d9
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/37a801d9
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/37a801d9

Branch: refs/heads/master
Commit: 37a801d975de8d76c3123c3a9b14efda56db7d10
Parents: ef7f9c4
Author: Thiago H. de Paula Figueiredo <th...@apache.org>
Authored: Mon Jun 30 15:08:27 2014 -0300
Committer: Thiago H. de Paula Figueiredo <th...@apache.org>
Committed: Mon Jun 30 15:08:27 2014 -0300

----------------------------------------------------------------------
 .../tapestry5/internal/AssetConstants.java      |  16 +++
 .../internal/services/AssetSourceImpl.java      |  58 +++++++++-
 .../services/ExternalUrlAssetFactory.java       |  70 ++++++++++++
 .../tapestry5/internal/services/UrlAsset.java   |  44 ++++++++
 .../internal/services/UrlResource.java          | 112 +++++++++++++++++++
 .../apache/tapestry5/modules/AssetsModule.java  |   4 +
 .../tapestry5/integration/app1/AssetTests.java  |  12 ++
 .../app1/pages/nested/AssetDemo.java            |  22 +++-
 .../integration/app1/services/AppModule.java    |   8 +-
 .../integration/app1/pages/nested/AssetDemo.tml |  14 +++
 10 files changed, 353 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/main/java/org/apache/tapestry5/internal/AssetConstants.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/AssetConstants.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/AssetConstants.java
index 7ea1431..647b3b6 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/AssetConstants.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/AssetConstants.java
@@ -14,6 +14,9 @@
 
 package org.apache.tapestry5.internal;
 
+import java.net.MalformedURLException;
+import java.net.URL;
+
 /**
  * Defines constants for the two basic asset prefixes.
  * 
@@ -26,4 +29,17 @@ public class AssetConstants
 
     /** For assets that are stored in the classpath (i.e., inside 3rd party component library JARs). */
     public static final String CLASSPATH = "classpath";
+
+    /** For assets that are external (stored in another service) and have a non-secure URL (http://...) */
+    public static final String HTTP = "http";
+
+    /** For assets that are external (stored in another service) and have a secure URL (https://...) */
+    public static final String HTTPS = "https";
+
+    /** For assets that are external (stored in another service) and have a protocol-relative URL (//...) */
+    public static final String PROTOCOL_RELATIVE = "//";
+
+    /** For assets that are external (stored in another service) and stored in a publicly available FTP server (ftp://...) */
+    public static final String FTP = "ftp";
+
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
index cd9b4af..bac9e0c 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java
@@ -31,9 +31,14 @@ import org.apache.tapestry5.ioc.services.ThreadLocale;
 import org.apache.tapestry5.ioc.util.StrategyRegistry;
 import org.apache.tapestry5.services.AssetFactory;
 import org.apache.tapestry5.services.AssetSource;
+import org.apache.tapestry5.services.Request;
 import org.slf4j.Logger;
 
 import java.lang.ref.SoftReference;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -42,6 +47,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
 @SuppressWarnings("all")
 public class AssetSourceImpl extends LockSupport implements AssetSource
 {
+    
+    private final List<String> EXTERNAL_URL_PREFIXES = Arrays.asList(
+            AssetConstants.HTTP, AssetConstants.HTTPS, AssetConstants.PROTOCOL_RELATIVE, AssetConstants.FTP); 
+    
     private final StrategyRegistry<AssetFactory> registry;
 
     private final ThreadLocale threadLocale;
@@ -57,15 +66,29 @@ public class AssetSourceImpl extends LockSupport implements AssetSource
     private final AtomicBoolean firstWarning = new AtomicBoolean(true);
 
     private final OperationTracker tracker;
+    
+    private final Request request;
+    
+    private final Map<String, AssetFactory> configuration;
 
-    public AssetSourceImpl(ThreadLocale threadLocale,
+    public AssetSourceImpl(ThreadLocale threadLocale, 
 
-                           Map<String, AssetFactory> configuration, SymbolSource symbolSource, Logger logger, OperationTracker tracker)
+            Map<String, AssetFactory> configuration, SymbolSource symbolSource, Logger logger, OperationTracker tracker)
     {
+        this(threadLocale, configuration, symbolSource, logger, tracker, null);
+    }
+
+    
+    public AssetSourceImpl(ThreadLocale threadLocale, 
+
+                           Map<String, AssetFactory> configuration, SymbolSource symbolSource, Logger logger, OperationTracker tracker, Request request)
+    {
+        this.configuration = configuration;
         this.threadLocale = threadLocale;
         this.symbolSource = symbolSource;
         this.logger = logger;
         this.tracker = tracker;
+        this.request = request;
 
         Map<Class, AssetFactory> byResourceClass = CollectionFactory.newMap();
 
@@ -141,9 +164,36 @@ public class AssetSourceImpl extends LockSupport implements AssetSource
                         // We special case the hell out of 'classpath:' so that we can provide warnings today (5.4) and
                         // blow up in a useful fashion tomorrow (5.5).
 
-                        if (dotx > 0 && !expanded.substring(0, dotx).equalsIgnoreCase(AssetConstants.CLASSPATH))
+                        if (expanded.startsWith("//") || (dotx > 0 && !expanded.substring(0, dotx).equalsIgnoreCase(AssetConstants.CLASSPATH)))
                         {
-                            return getAssetInLocale(resources.getBaseResource(), expanded, resources.getLocale());
+                            final String prefix = dotx >= 0 ? expanded.substring(0, dotx) : AssetConstants.PROTOCOL_RELATIVE;
+                            if (EXTERNAL_URL_PREFIXES.contains(prefix))
+                            {
+                                
+                                String url;
+                                if (prefix.equals(AssetConstants.PROTOCOL_RELATIVE)) 
+                                {
+                                    url = (request != null && request.isSecure() ? "https:" : "http:") + expanded;  
+                                }
+                                else
+                                {
+                                    url = expanded;
+                                }
+                                
+                                try
+                                {
+                                    UrlResource resource = new UrlResource(new URL(url));
+                                    return new UrlAsset(url, resource);
+                                }
+                                catch (MalformedURLException e)
+                                {
+                                    throw new RuntimeException(e);
+                                }
+                            }
+                            else
+                            {
+                                return getAssetInLocale(resources.getBaseResource(), expanded, resources.getLocale());
+                            }
                         }
 
                         // No prefix, so implicitly classpath:, or explicitly classpath:

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ExternalUrlAssetFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ExternalUrlAssetFactory.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ExternalUrlAssetFactory.java
new file mode 100644
index 0000000..738984e
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ExternalUrlAssetFactory.java
@@ -0,0 +1,70 @@
+// Copyright 2014 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.services;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+import org.apache.tapestry5.Asset;
+import org.apache.tapestry5.internal.AssetConstants;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.services.AssetFactory;
+
+public class ExternalUrlAssetFactory implements AssetFactory {
+    
+    final private String protocol;
+    
+    final private UrlResource rootResource;
+    
+    final private Map<URL, Asset> cache = CollectionFactory.newMap();
+
+    public ExternalUrlAssetFactory(String protocol)
+    {
+        super();
+        this.protocol = protocol;
+//        try
+//        {
+            if (protocol.equals(AssetConstants.PROTOCOL_RELATIVE)) {
+                protocol = "http";
+            }
+            this.rootResource = new UrlResource();
+//        }
+//        catch (MalformedURLException e)
+//        {
+//            throw new RuntimeException(e);
+//        }
+    }
+
+    @Override
+    public Resource getRootResource()
+    {
+        return rootResource;
+    }
+
+    @Override
+    public Asset createAsset(Resource resource)
+    {
+        final URL url = resource.toURL();
+        Asset asset = cache.get(url);
+        if (asset == null)
+        {
+            asset = new UrlAsset(url.toExternalForm(), resource);
+            cache.put(url, asset);
+        }
+        return asset;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlAsset.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlAsset.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlAsset.java
new file mode 100644
index 0000000..1441af8
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlAsset.java
@@ -0,0 +1,44 @@
+// Copyright 2014 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.services;
+
+import org.apache.tapestry5.ioc.Resource;
+
+public class UrlAsset extends AbstractAsset
+{
+    
+    final private String url;
+    
+    final private Resource resource;
+    
+    public UrlAsset(String url, Resource resource)
+    {
+        super(true);
+        this.url = url;
+        this.resource = resource;
+    }
+
+    @Override
+    public String toClientURL()
+    {
+        return url;
+    }
+
+    @Override
+    public Resource getResource()
+    {
+        return resource;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlResource.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlResource.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlResource.java
new file mode 100644
index 0000000..d897f4a
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/UrlResource.java
@@ -0,0 +1,112 @@
+// Copyright 2014 The Apache Software Foundation
+//
+// Licensed 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.tapestry5.internal.services;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Locale;
+
+import org.apache.tapestry5.internal.util.VirtualResource;
+import org.apache.tapestry5.ioc.Resource;
+
+public class UrlResource extends VirtualResource {
+    
+    final public static URL PLACEHOLDER_URL;
+    
+    static {
+        try
+        {
+            PLACEHOLDER_URL = new URL("http://tapestry.apache.org");
+        }
+        catch (MalformedURLException e)
+        {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+    
+    final private URL url;
+    
+    private Boolean exists;
+    
+    private InputStream inputStream;
+
+    public UrlResource()
+    {
+        this(PLACEHOLDER_URL);
+        exists = true;
+    }
+    
+    public UrlResource(URL url)
+    {
+        super();
+        this.url = url;
+    }
+
+    @Override
+    public InputStream openStream() throws IOException
+    {
+        if (exists == null) 
+        {
+            try
+            {
+                inputStream = url.openStream();
+                exists = true;
+            }
+            catch (IOException e)
+            {
+                exists = false;
+                throw e;
+            }
+        }
+        return inputStream;
+    }
+
+    @Override
+    public URL toURL()
+    {
+        return url;
+    }
+
+    @Override
+    public boolean exists()
+    {
+        if (exists == null)
+        {
+            try
+            {
+                openStream();
+            }
+            catch (IOException e)
+            {
+                // openStream() will always set the exists field.
+            }
+        }
+        return exists;
+    }
+
+    @Override
+    public Resource forLocale(Locale locale)
+    {
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        return "UrlResource [url=" + url + "]";
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/main/java/org/apache/tapestry5/modules/AssetsModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/AssetsModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/AssetsModule.java
index 4ec8207..aa98e5b 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/AssetsModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/AssetsModule.java
@@ -60,6 +60,10 @@ public class AssetsModule
     {
         configuration.add(AssetConstants.CONTEXT, contextAssetFactory);
         configuration.add(AssetConstants.CLASSPATH, classpathAssetFactory);
+        configuration.add(AssetConstants.HTTP, new ExternalUrlAssetFactory(AssetConstants.HTTP));
+        configuration.add(AssetConstants.HTTPS, new ExternalUrlAssetFactory(AssetConstants.HTTPS));
+        configuration.add(AssetConstants.FTP, new ExternalUrlAssetFactory(AssetConstants.FTP));
+        configuration.add(AssetConstants.PROTOCOL_RELATIVE, new ExternalUrlAssetFactory(AssetConstants.PROTOCOL_RELATIVE));
     }
 
 

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java
index 3c03253..6848223 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/AssetTests.java
@@ -53,6 +53,18 @@ public class AssetTests extends App1TestCase
 
         compareDownloadedAsset(assetURL, localPath);
     }
+    
+    // TAP5-1515
+    @Test
+    public void external_url_asset_bindings()
+    {
+        openLinks("AssetDemo");
+        
+        assertEquals("http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.js", getText("httpAsset"));
+        assertEquals("https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.js", getText("httpsAsset"));
+        assertEquals("http://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.js", getText("protocolRelativeAsset"));
+        assertEquals("ftp://cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.js", getText("ftpAsset"));
+    }
 
     private void compareDownloadedAsset(String assetURL, String localPath) throws Exception
     {

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.java
index c8a781b..e2b3705 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2010 The Apache Software Foundation
+// Copyright 2006, 2007, 2010, 2014 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -48,6 +48,26 @@ public class AssetDemo
     @Inject
     @Path("context:css/ie-only.css")
     private Asset ieOnly;
+    
+    @Property
+    @Inject
+    @Path("http://${d3.url}")
+    private Asset httpAsset;
+
+    @Property
+    @Inject
+    @Path("https://${d3.url}")
+    private Asset httpsAsset;
+
+    @Property
+    @Inject
+    @Path("//${d3.url}")
+    private Asset protocolRelativeAsset;
+
+    @Property
+    @Inject
+    @Path("ftp://${d3.url}")
+    private Asset ftpAsset;
 
     @Environmental
     private JavaScriptSupport javascriptSupport;

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
index a6cfe30..85e0fcc 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
@@ -1,4 +1,4 @@
-// Copyright 2006-2013 The Apache Software Foundation
+// Copyright 2006-2014 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -11,7 +11,6 @@
 // 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.tapestry5.integration.app1.services;
 
 import org.apache.tapestry5.SymbolConstants;
@@ -49,6 +48,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
  */
 public class AppModule
 {
+    
+    final public static String D3_URL_SYMBOL = "d3.url";
+
     /**
      * Used to disambiguate services in this module from services in other modules that share the
      * same service
@@ -151,6 +153,8 @@ public class AppModule
 
         // This is the emphasis of testing at this point.
         configuration.add(SymbolConstants.JAVASCRIPT_INFRASTRUCTURE_PROVIDER, "jquery");
+
+        configuration.add(D3_URL_SYMBOL, "cdnjs.cloudflare.com/ajax/libs/d3/3.4.8/d3.js");
     }
 
     public static void contributeIgnoredPathsFilter(Configuration<String> configuration)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/37a801d9/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.tml b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.tml
index 17df073..969031e 100644
--- a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.tml
+++ b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/nested/AssetDemo.tml
@@ -45,6 +45,14 @@
     Context asset via context: binding
 </p>
 
+<p>External asset using "asset:http: " binding: <span id="httpAsset">${httpAsset.toClientURL()}</span></p>
+
+<p>External asset using "asset:https:" binding: <span id="httpsAsset">${httpsAsset.toClientURL()}</span></p>
+
+<p>External asset using "asset://:" binding: <span id="protocolRelativeAsset">${protocolRelativeAsset.toClientURL()}</span></p>
+
+<p>External asset using "asset:ftp:" binding: <span id="ftpAsset">${ftpAsset.toClientURL()}</span></p>
+
 <img id="viaContext" src="${context:images/asf_logo_wide.gif}"/>
 
 <p>
@@ -71,4 +79,10 @@
     </a>
 </p>
 
+<p>
+	Using d3, loaded directly from a CDN using external asset support: 
+</p>
+
+<div id="viz"/>
+
 </html>
\ No newline at end of file