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.javascript; 016 017import org.apache.tapestry5.Asset; 018import org.apache.tapestry5.SymbolConstants; 019import org.apache.tapestry5.func.F; 020import org.apache.tapestry5.func.Mapper; 021import org.apache.tapestry5.internal.services.RequestConstants; 022import org.apache.tapestry5.internal.services.assets.JavaScriptStackAssembler; 023import org.apache.tapestry5.ioc.annotations.Symbol; 024import org.apache.tapestry5.ioc.internal.util.CollectionFactory; 025import org.apache.tapestry5.ioc.services.ThreadLocale; 026import org.apache.tapestry5.ioc.util.ExceptionUtils; 027import org.apache.tapestry5.services.ResponseCompressionAnalyzer; 028import org.apache.tapestry5.services.assets.AssetPathConstructor; 029import org.apache.tapestry5.services.assets.StreamableResource; 030import org.apache.tapestry5.services.javascript.JavaScriptStack; 031import org.apache.tapestry5.services.javascript.JavaScriptStackSource; 032 033import java.io.IOException; 034import java.util.List; 035 036public class JavaScriptStackPathConstructorImpl implements JavaScriptStackPathConstructor 037{ 038 private final ThreadLocale threadLocale; 039 040 private final AssetPathConstructor assetPathConstructor; 041 042 private final JavaScriptStackSource javascriptStackSource; 043 044 private final JavaScriptStackAssembler assembler; 045 046 private final ResponseCompressionAnalyzer compressionAnalyzer; 047 048 private final boolean combineScripts; 049 050 private final Mapper<Asset, String> toPath = new Mapper<Asset, String>() 051 { 052 public String map(Asset input) 053 { 054 return input.toClientURL(); 055 } 056 }; 057 058 public JavaScriptStackPathConstructorImpl(ThreadLocale threadLocale, AssetPathConstructor assetPathConstructor, 059 JavaScriptStackSource javascriptStackSource, 060 JavaScriptStackAssembler assembler, 061 ResponseCompressionAnalyzer compressionAnalyzer, 062 @Symbol(SymbolConstants.COMBINE_SCRIPTS) 063 boolean combineScripts) 064 { 065 this.threadLocale = threadLocale; 066 this.assetPathConstructor = assetPathConstructor; 067 this.javascriptStackSource = javascriptStackSource; 068 this.assembler = assembler; 069 this.compressionAnalyzer = compressionAnalyzer; 070 this.combineScripts = combineScripts; 071 } 072 073 public List<String> constructPathsForJavaScriptStack(String stackName) 074 { 075 JavaScriptStack stack = javascriptStackSource.getStack(stackName); 076 077 List<Asset> assets = stack.getJavaScriptLibraries(); 078 079 // When combine scripts is true, we want to build the virtual aggregated JavaScript ... but only 080 // if there is more than one library asset, or any modules. 081 if (combineScripts) 082 { 083 boolean needsVirtual = (assets.size() > 1) || (!stack.getModules().isEmpty()); 084 085 if (needsVirtual) 086 { 087 return combinedStackURL(stackName); 088 } 089 } 090 091 return toPaths(assets); 092 } 093 094 private List<String> toPaths(List<Asset> assets) 095 { 096 assert assets != null; 097 098 return F.flow(assets).map(toPath).toList(); 099 } 100 101 private List<String> combinedStackURL(String stackName) 102 { 103 try 104 { 105 StreamableResource assembled = assembler.assembleJavaScriptResourceForStack(stackName, compressionAnalyzer.isGZipSupported()); 106 107 String path = String.format("%s/%s.js", 108 threadLocale.getLocale(), 109 stackName); 110 111 String stackURL = assetPathConstructor.constructAssetPath(RequestConstants.STACK_FOLDER, path, assembled); 112 113 return CollectionFactory.newList(stackURL); 114 } catch (IOException ex) 115 { 116 throw new RuntimeException(String.format("Unable to construct path for '%s' JavaScript stack: %s", 117 stackName, 118 ExceptionUtils.toMessage(ex)), ex); 119 } 120 } 121 122}