    001// Licensed under the Apache License, Version 2.0 (the "License");
    002// you may not use this file except in compliance with the License.
    003// You may obtain a copy of the License at
    004//
    005// http://www.apache.org/licenses/LICENSE-2.0
    006//
    007// Unless required by applicable law or agreed to in writing, software
    008// distributed under the License is distributed on an "AS IS" BASIS,
    009// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    010// See the License for the specific language governing permissions and
    011// limitations under the License.
    012
    013package org.apache.tapestry5.internal.services.assets;
    014
    015import org.apache.tapestry5.http.services.Request;
    016import org.apache.tapestry5.http.services.Response;
    017import org.apache.tapestry5.internal.services.ResourceStreamer;
    018import org.apache.tapestry5.ioc.IOOperation;
    019import org.apache.tapestry5.ioc.OperationTracker;
    020import org.apache.tapestry5.services.LocalizationSetter;
    021import org.apache.tapestry5.services.assets.AssetRequestHandler;
    022import org.apache.tapestry5.services.assets.StreamableResource;
    023import org.apache.tapestry5.services.javascript.JavaScriptStack;
    024import org.apache.tapestry5.services.javascript.JavaScriptStackSource;
    025import org.slf4j.Logger;
    026
    027import java.io.IOException;
    028import java.util.regex.Matcher;
    029import java.util.regex.Pattern;
    030
    031public class StackAssetRequestHandler implements AssetRequestHandler
    032{
    033    private final Logger logger;
    034
    035    private final LocalizationSetter localizationSetter;
    036
    037    private final ResourceStreamer resourceStreamer;
    038
    039    // Group 1: checksum
    040    // Group 2: locale
    041    // Group 3: path
    042    private final Pattern pathPattern = Pattern.compile("^(.+)/(.+)/(.+)\\.js$");
    043
    044    private final OperationTracker tracker;
    045
    046    private final JavaScriptStackAssembler javaScriptStackAssembler;
    047
    048    private final JavaScriptStackSource stackSource;
    049
    050    public StackAssetRequestHandler(Logger logger, LocalizationSetter localizationSetter,
    051                                    ResourceStreamer resourceStreamer,
    052                                    OperationTracker tracker,
    053                                    JavaScriptStackAssembler javaScriptStackAssembler,
    054                                    JavaScriptStackSource stackSource)
    055    {
    056        this.logger = logger;
    057        this.localizationSetter = localizationSetter;
    058        this.resourceStreamer = resourceStreamer;
    059        this.tracker = tracker;
    060        this.javaScriptStackAssembler = javaScriptStackAssembler;
    061        this.stackSource = stackSource;
    062    }
    063
    064    public boolean handleAssetRequest(Request request, Response response, final String extraPath) throws IOException
    065    {
    066        return tracker.perform(String.format("Streaming JavaScript asset stack %s", extraPath),
    067                new IOOperation<Boolean>()
    068                {
    069                    public Boolean perform() throws IOException
    070                    {
    071                        return streamStackResource(extraPath);
    072                    }
    073                });
    074    }
    075
    076    private boolean streamStackResource(String extraPath) throws IOException
    077    {
    078        Matcher matcher = pathPattern.matcher(extraPath);
    079
    080        if (!matcher.matches())
    081        {
    082            logger.warn("Unable to parse '{}' as an asset stack path", extraPath);
    083
    084            return false;
    085        }
    086
    087        String checksum = matcher.group(1);
    088        String localeName = matcher.group(2);
    089        final String stackName = matcher.group(3);
    090
    091        final boolean compressed = checksum.startsWith("z");
    092
    093        if (compressed)
    094        {
    095            checksum = checksum.substring(1);
    096        }
    097
    098        final JavaScriptStack stack = stackSource.findStack(stackName);
    099
    100        if (stack == null)
    101        {
    102            logger.warn("JavaScript stack '{}' not found.", stackName);
    103            return false;
    104        }
    105
    106
    107        // Yes, I have a big regret that the JavaScript stack stuff relies on this global, rather than
    108        // having it passed around properly.
    109
    110        localizationSetter.setNonPersistentLocaleFromLocaleName(localeName);
    111
    112        StreamableResource resource =
    113                tracker.perform(String.format("Assembling JavaScript asset stack '%s' (%s)",
    114                                stackName, localeName),
    115                        new IOOperation<StreamableResource>()
    116                        {
    117                            public StreamableResource perform() throws IOException
    118                            {
    119
    120                                return javaScriptStackAssembler.assembleJavaScriptResourceForStack(stackName, compressed,
    121                                        stack.getJavaScriptAggregationStrategy());
    122
    123                            }
    124                        });
    125
    126
    127        if (resource == null)
    128        {
    129            return false;
    130        }
    131
    132        return resourceStreamer.streamResource(resource, checksum, ResourceStreamer.DEFAULT_OPTIONS);
    133    }
    134}