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