/* * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * Stephane Lacoin */ package org.nuxeo.ecm.webengine.gwt; import static java.nio.file.FileVisitResult.CONTINUE; import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import java.io.File; import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.common.Environment; public class GwtResolver { private final Log log = LogFactory.getLog(GwtResolver.class); public interface Strategy { URI source(); File resolve(String path); } public final File GWT_ROOT = locateRoot(); private static File locateRoot() { File dir = new File(Environment.getDefault().getWeb(), "root.war/gwt"); dir.mkdirs(); return dir; } protected final Map<String, CompositeStrategy> strategies = new HashMap<String, CompositeStrategy>(); protected final Strategy ROOT_RESOLVER_STRATEGY = new Strategy() { @Override public URI source() { return GWT_ROOT.toURI(); } @Override public File resolve(String path) { return new File(GWT_ROOT, path); } }; class CompositeStrategy { final Map<URI, Strategy> strategiesByKey = new HashMap<URI, Strategy>(); final List<Strategy> strategies = new ArrayList<Strategy>(); void install(Strategy strategy) { strategiesByKey.put(strategy.source(), strategy); strategies.add(strategy); } void uninstall(URI source) { Strategy strategy = strategiesByKey.remove(source); if (strategy == null) { return; } strategies.remove(strategy); } public File resolve(String path) { ListIterator<Strategy> it = strategies.listIterator(strategies.size()); while (it.hasPrevious()) { File file = it.previous().resolve(path); if (file.exists()) { return file; } } return null; } } public Strategy newStrategy(final URI location) throws IOException { final File root = install(location); return new Strategy() { @Override public URI source() { return location; } @Override public File resolve(String path) { return new File(root, path); } }; } protected File install(URI location) throws IOException { if ("jar".equals(location.getScheme())) { Map<String, Object> env = Collections.emptyMap(); try (FileSystem fileSystem = FileSystems.newFileSystem(location, env, this.getClass().getClassLoader());) { Path path = Paths.get(location); try { // it's a directory return path.toFile(); } catch (UnsupportedOperationException cause) { // it's a jar, we should install content } Files.walkFileTree(path, new TreeImporter(path, GWT_ROOT.toPath())); return GWT_ROOT; } } return Paths.get(location).toFile(); } public void install(String name, Strategy strategy) { if (!strategies.containsKey(name)) { strategies.put(name, new CompositeStrategy()); } strategies.get(name).install(strategy); } public void install(String name, URI location) throws IOException { install(name, newStrategy(location)); } public void uninstall(String name) { strategies.remove(name); } public File resolve(String path) { int indexOf = path.indexOf('/'); if (indexOf == -1) { if (strategies.containsKey(path)) { return strategies.get(path).resolve("/"); } return ROOT_RESOLVER_STRATEGY.resolve(path); } String name = path.substring(0, indexOf); if (strategies.containsKey(name)) { return strategies.get(name).resolve(path); } return ROOT_RESOLVER_STRATEGY.resolve(path); } public class TreeImporter implements FileVisitor<Path> { final Path source; final Path sink; public TreeImporter(Path source, Path sink) { this.source = source; this.sink = sink; } @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { if (dir == source) { return CONTINUE; } Path sinkPath = toSinkPath(dir); if (!Files.exists(sinkPath)) { Files.createDirectory(sinkPath); } return CONTINUE; } Path toSinkPath(Path path) { if (path == source) { return sink; } path = source.relativize(path); path = sink.resolve(path.toString()); return path; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Files.copy(file, toSinkPath(file), COPY_ATTRIBUTES, REPLACE_EXISTING); return CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException error) { if (error != null) { return FileVisitResult.TERMINATE; } return CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException error) { if (error != null) { return FileVisitResult.TERMINATE; } return CONTINUE; } } }