001/* 002 * Copyright 2019 Anyware Services 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 */ 016package org.ametys.core.util; 017 018import java.io.File; 019import java.io.IOException; 020import java.net.JarURLConnection; 021import java.net.URI; 022import java.net.URL; 023import java.net.URLConnection; 024import java.nio.file.FileSystem; 025import java.nio.file.FileSystems; 026import java.nio.file.NoSuchFileException; 027import java.util.Collections; 028import java.util.HashMap; 029import java.util.Map; 030 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import org.ametys.core.util.LambdaUtils.LambdaException; 035 036/** 037 * This component centralize the access to zip files 038 * Beware: All opened zip files are kept opened in memory 039 */ 040public final class JarFSManager 041{ 042 private static JarFSManager _instance; 043 044 private Map<String, FileSystem> _openedFS = new HashMap<>(); 045 046 private Logger _logger = LoggerFactory.getLogger(getClass()); 047 048 private JarFSManager() 049 { 050 // private 051 } 052 053 /** 054 * Get the unique instance 055 * @return the unique instance 056 */ 057 public static JarFSManager getInstance() 058 { 059 if (_instance == null) 060 { 061 _instance = new JarFSManager(); 062 } 063 064 return _instance; 065 } 066 067 /** 068 * Return the jar filesystem associated with the given resource 069 * @param resourcePath The resource path to seek. Such as 'org/apache/ivy/logo.png'. 070 * @return The JAR filesystem or null if the resource cannot be found 071 * @throws IOException If an error occurred while getting the JAR file of an existing resource 072 */ 073 public FileSystem getFileSystemByResource(String resourcePath) throws IOException 074 { 075 String uri = getJARFileURI(resourcePath); 076 return getFileSystemByURI(uri); 077 } 078 079 /** 080 * Return the jar filesystem associated with the given jar file 081 * @param jarFile the jar file to open 082 * @return The JAR filesystem or null if the file has a problem 083 * @throws IOException If an error occurred while opening the JAR file 084 */ 085 public FileSystem getFileSystemByFile(File jarFile) throws IOException 086 { 087 String uri = "jar:" + jarFile.getCanonicalFile().toURI().toString(); 088 return getFileSystemByURI(uri); 089 } 090 091 /** 092 * Determine the JAR file holding the given resource 093 * @param resourcePath The resource path to seek. Such as 'org/apache/ivy/logo.png'. 094 * @return The JAR file location. Such as '/path/to/jar/ivy.jar' or null if the resource cannot be found 095 * @throws IOException If an error occurred while getting the JAR file of an existing resource 096 */ 097 public String getJARFileURI(String resourcePath) throws IOException 098 { 099 URL resource = getClass().getClassLoader().getResource(resourcePath); 100 if (resource == null) 101 { 102 return null; 103 } 104 105 URLConnection connection = resource.openConnection(); 106 URL jarFileURL = ((JarURLConnection) connection).getJarFileURL(); 107 return "jar:" + jarFileURL.toString(); 108 } 109 110 /** 111 * Return the jar filesystem associated with the given jar file denoted by its URI 112 * @param jarURI Such as "jar:file:/path/to/file.jar" 113 * @return The filesystem or null if the jarURI is null 114 * @throws IOException If the JAR file cannot be opened correctly 115 */ 116 public synchronized FileSystem getFileSystemByURI(String jarURI) throws IOException 117 { 118 if (jarURI == null) 119 { 120 return null; 121 } 122 123 try 124 { 125 _logger.debug("Opening the jar at uri: {}", jarURI); 126 return _openedFS.computeIfAbsent(jarURI, LambdaUtils.wrap(u -> { 127 return FileSystems.newFileSystem(new URI(u), Collections.emptyMap()); 128 })); 129 } 130 catch (LambdaException e) 131 { 132 if (e.getCause() instanceof NoSuchFileException) 133 { 134 _logger.error("Could not find the jar at uri: {}", jarURI); 135 } 136 throw (IOException) e.getCause(); 137 } 138 } 139}