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