public Asset getComponentAsset()

in tapestry-core/src/main/java/org/apache/tapestry5/internal/services/AssetSourceImpl.java [164:281]


    public Asset getComponentAsset(final ComponentResources resources, final String path, final String libraryName)
    {
        assert resources != null;

        assert InternalUtils.isNonBlank(path);

        return tracker.invoke(String.format("Resolving '%s' for component %s", path, resources.getCompleteId()),
                new Invokable<Asset>()
                {
                    public Asset invoke()
                    {
                        // First, expand symbols:

                        String expanded = symbolSource.expandSymbols(path);

                        int dotx = expanded.indexOf(':');

                        // 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 (expanded.startsWith("//") || (dotx > 0 && !expanded.substring(0, dotx).equalsIgnoreCase(AssetConstants.CLASSPATH)))
                        {
                            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;
                                    url = url.replace("//:", "//");
                                } 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:

                        String restOfPath = expanded.substring(dotx + 1);

                        // This is tricky, because a relative path (including "../") is ok in 5.3, since its just somewhere
                        // else on the classpath (though you can "stray" out of the "safe" zone).  In 5.4, under /META-INF/assets/
                        // it's possible to "stray" out beyond the safe zone more easily, into parts of the classpath that can't be
                        // represented in the URL.

                        // Ends with trailing slash:
                        String metaRoot = "META-INF/assets/" + toPathPrefix(libraryName);

                        String trimmedRestOfPath = restOfPath.startsWith("/") ? restOfPath.substring(1) : restOfPath;


                        // TAP5-2044: Some components specify a full path, starting with META-INF/assets/, and we should just trust them.
                        // The warning logic below is for compnents that specify a relative path. Our bad decisions come back to haunt us;
                        // Resource paths should always had a leading slash to differentiate relative from complete.
                        String metaPath = trimmedRestOfPath.startsWith("META-INF/assets/") ? trimmedRestOfPath : metaRoot + trimmedRestOfPath;

                        // Based on the path, metaResource is where it should exist in a 5.4 and beyond world ... unless the expanded
                        // path was a bit too full of ../ sequences, in which case the expanded path is not valid and we adjust the
                        // error we write.

                        Resource metaResource = findLocalizedResource(null, metaPath, resources.getLocale());

                        Asset result = getComponentAsset(resources, expanded, metaResource);

                        if (result == null)
                        {
                            throw new RuntimeException(String.format("Unable to locate asset '%s' for component %s. It should be located at %s.",
                                    path, resources.getCompleteId(),
                                    metaPath));
                        }

                        // This is the best way to tell if the result is an asset for a Classpath resource.

                        Resource resultResource = result.getResource();

                        if (!resultResource.equals(metaResource))
                        {
                            if (firstWarning.getAndSet(false))
                            {
                                logger.error("Packaging of classpath assets has changed in release 5.4; " +
                                        "Assets should no longer be on the main classpath, " +
                                        "but should be moved to 'META-INF/assets/' or a sub-folder. Future releases of Tapestry may " +
                                        "no longer support assets on the main classpath.");
                            }

                            if (metaResource.getFolder().startsWith(metaRoot))
                            {
                                logger.warn(String.format("Classpath asset '/%s' should be moved to folder '/%s/'.",
                                        resultResource.getPath(),
                                        metaResource.getFolder()));
                            } else
                            {
                                logger.warn(String.format("Classpath asset '/%s' should be moved under folder '/%s', and the relative path adjusted.",
                                        resultResource.getPath(),
                                        metaRoot));
                            }
                        }

                        return result;
                    }
                }

        );
    }