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