001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Stephane Lacoin 018 */ 019package org.nuxeo.ecm.webengine.gwt; 020 021import static java.nio.file.FileVisitResult.CONTINUE; 022import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; 023import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; 024 025import java.io.File; 026import java.io.IOException; 027import java.net.URI; 028import java.nio.file.FileSystem; 029import java.nio.file.FileSystems; 030import java.nio.file.FileVisitResult; 031import java.nio.file.FileVisitor; 032import java.nio.file.Files; 033import java.nio.file.Path; 034import java.nio.file.Paths; 035import java.nio.file.attribute.BasicFileAttributes; 036import java.util.ArrayList; 037import java.util.Collections; 038import java.util.HashMap; 039import java.util.List; 040import java.util.ListIterator; 041import java.util.Map; 042 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045import org.nuxeo.common.Environment; 046 047public class GwtResolver { 048 049 private final Log log = LogFactory.getLog(GwtResolver.class); 050 051 public interface Strategy { 052 053 URI source(); 054 055 File resolve(String path); 056 } 057 058 public final File GWT_ROOT = locateRoot(); 059 060 private static File locateRoot() { 061 File dir = new File(Environment.getDefault().getWeb(), "root.war/gwt"); 062 dir.mkdirs(); 063 return dir; 064 } 065 066 protected final Map<String, CompositeStrategy> strategies = new HashMap<String, CompositeStrategy>(); 067 068 protected final Strategy ROOT_RESOLVER_STRATEGY = new Strategy() { 069 070 @Override 071 public URI source() { 072 return GWT_ROOT.toURI(); 073 } 074 075 @Override 076 public File resolve(String path) { 077 return new File(GWT_ROOT, path); 078 } 079 }; 080 081 class CompositeStrategy { 082 final Map<URI, Strategy> strategiesByKey = new HashMap<URI, Strategy>(); 083 084 final List<Strategy> strategies = new ArrayList<Strategy>(); 085 086 void install(Strategy strategy) { 087 strategiesByKey.put(strategy.source(), strategy); 088 strategies.add(strategy); 089 } 090 091 void uninstall(URI source) { 092 Strategy strategy = strategiesByKey.remove(source); 093 if (strategy == null) { 094 return; 095 } 096 strategies.remove(strategy); 097 } 098 099 public File resolve(String path) { 100 ListIterator<Strategy> it = strategies.listIterator(strategies.size()); 101 while (it.hasPrevious()) { 102 File file = it.previous().resolve(path); 103 if (file.exists()) { 104 return file; 105 } 106 } 107 return null; 108 } 109 } 110 111 public Strategy newStrategy(final URI location) throws IOException { 112 final File root = install(location); 113 return new Strategy() { 114 115 116 @Override 117 public URI source() { 118 return location; 119 } 120 121 @Override 122 public File resolve(String path) { 123 return new File(root, path); 124 } 125 }; 126 } 127 128 protected File install(URI location) throws IOException { 129 if ("jar".equals(location.getScheme())) { 130 Map<String, Object> env = Collections.emptyMap(); 131 try (FileSystem fileSystem = FileSystems.newFileSystem(location, env, this.getClass().getClassLoader());) { 132 Path path = Paths.get(location); 133 try { 134 // it's a directory 135 return path.toFile(); 136 } catch (UnsupportedOperationException cause) { 137 // it's a jar, we should install content 138 } 139 Files.walkFileTree(path, new TreeImporter(path, GWT_ROOT.toPath())); 140 return GWT_ROOT; 141 } 142 } 143 return Paths.get(location).toFile(); 144 } 145 146 public void install(String name, Strategy strategy) { 147 if (!strategies.containsKey(name)) { 148 strategies.put(name, new CompositeStrategy()); 149 } 150 strategies.get(name).install(strategy); 151 } 152 153 public void install(String name, URI location) throws IOException { 154 install(name, newStrategy(location)); 155 } 156 157 public void uninstall(String name) { 158 strategies.remove(name); 159 } 160 161 public File resolve(String path) { 162 int indexOf = path.indexOf('/'); 163 if (indexOf == -1) { 164 if (strategies.containsKey(path)) { 165 return strategies.get(path).resolve("/"); 166 } 167 return ROOT_RESOLVER_STRATEGY.resolve(path); 168 } 169 String name = path.substring(0, indexOf); 170 if (strategies.containsKey(name)) { 171 return strategies.get(name).resolve(path); 172 } 173 return ROOT_RESOLVER_STRATEGY.resolve(path); 174 } 175 176 public class TreeImporter implements FileVisitor<Path> { 177 final Path source; 178 179 final Path sink; 180 181 public TreeImporter(Path source, Path sink) { 182 this.source = source; 183 this.sink = sink; 184 } 185 186 @Override 187 public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { 188 if (dir == source) { 189 return CONTINUE; 190 } 191 Path sinkPath = toSinkPath(dir); 192 if (!Files.exists(sinkPath)) { 193 Files.createDirectory(sinkPath); 194 } 195 return CONTINUE; 196 } 197 198 Path toSinkPath(Path path) { 199 if (path == source) { 200 return sink; 201 } 202 path = source.relativize(path); 203 path = sink.resolve(path.toString()); 204 return path; 205 } 206 207 @Override 208 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 209 Files.copy(file, toSinkPath(file), COPY_ATTRIBUTES, REPLACE_EXISTING); 210 return CONTINUE; 211 } 212 213 @Override 214 public FileVisitResult postVisitDirectory(Path dir, IOException error) { 215 if (error != null) { 216 return FileVisitResult.TERMINATE; 217 } 218 return CONTINUE; 219 } 220 221 @Override 222 public FileVisitResult visitFileFailed(Path file, IOException error) { 223 if (error != null) { 224 return FileVisitResult.TERMINATE; 225 } 226 227 return CONTINUE; 228 } 229 230 } 231}