|  | @ -4,16 +4,22 @@ import com.databasir.core.domain.DomainErrors; | 
			
		
	
		
		
			
				
					|  |  | import lombok.RequiredArgsConstructor; |  |  | import lombok.RequiredArgsConstructor; | 
			
		
	
		
		
			
				
					|  |  | import lombok.extern.slf4j.Slf4j; |  |  | import lombok.extern.slf4j.Slf4j; | 
			
		
	
		
		
			
				
					|  |  | import org.apache.commons.io.IOUtils; |  |  | import org.apache.commons.io.IOUtils; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import org.apache.commons.lang3.ClassUtils; | 
			
		
	
		
		
			
				
					|  |  | import org.springframework.beans.factory.annotation.Value; |  |  | import org.springframework.beans.factory.annotation.Value; | 
			
		
	
		
		
			
				
					|  |  | import org.springframework.http.HttpMethod; |  |  | import org.springframework.http.HttpMethod; | 
			
		
	
		
		
			
				
					|  |  | import org.springframework.stereotype.Component; |  |  | import org.springframework.stereotype.Component; | 
			
		
	
		
		
			
				
					|  |  | import org.springframework.util.StreamUtils; |  |  | import org.springframework.util.StreamUtils; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import org.springframework.web.client.RestClientException; | 
			
		
	
		
		
			
				
					|  |  | import org.springframework.web.client.RestTemplate; |  |  | import org.springframework.web.client.RestTemplate; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | import java.io.*; |  |  | import java.io.*; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import java.net.MalformedURLException; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import java.net.URL; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import java.net.URLClassLoader; | 
			
		
	
		
		
			
				
					|  |  | import java.nio.file.Files; |  |  | import java.nio.file.Files; | 
			
		
	
		
		
			
				
					|  |  | import java.nio.file.Path; |  |  | import java.nio.file.Path; | 
			
		
	
		
		
			
				
					|  |  | import java.nio.file.Paths; |  |  | import java.nio.file.Paths; | 
			
		
	
		
		
			
				
					|  |  |  |  |  | import java.util.Optional; | 
			
		
	
		
		
			
				
					|  |  | import java.util.UUID; |  |  | import java.util.UUID; | 
			
		
	
		
		
			
				
					|  |  | import java.util.jar.JarFile; |  |  | import java.util.jar.JarFile; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  | @ -27,7 +33,7 @@ public class DriverResources { | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     private final RestTemplate restTemplate; |  |  |     private final RestTemplate restTemplate; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     public void delete(String databaseType) { |  |  |     public void deleteByDatabaseType(String databaseType) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |         Path path = Paths.get(driverFilePath(driverBaseDirectory, databaseType)); |  |  |         Path path = Paths.get(driverFilePath(driverBaseDirectory, databaseType)); | 
			
		
	
		
		
			
				
					|  |  |         try { |  |  |         try { | 
			
		
	
		
		
			
				
					|  |  |             Files.deleteIfExists(path); |  |  |             Files.deleteIfExists(path); | 
			
		
	
	
		
		
			
				
					|  | @ -36,10 +42,24 @@ public class DriverResources { | 
			
		
	
		
		
			
				
					|  |  |         } |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     public String resolveSqlDriverNameFromJar(String driverFileUrl) { |  |  |     public Optional<File> loadByDatabaseType(String databaseType) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |         Path path = Paths.get(driverFilePath(driverBaseDirectory, databaseType)); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         if (Files.exists(path)) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             return Optional.of(path.toFile()); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } else { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             return Optional.empty(); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     public File loadOrDownloadByDatabaseType(String databaseType, String driverFileUrl) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         return loadByDatabaseType(databaseType) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 .orElseGet(() -> download(driverFileUrl, driverFilePath(driverBaseDirectory, databaseType))); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     public String resolveDriverClassName(String driverFileUrl) { | 
			
		
	
		
		
			
				
					|  |  |         String tempFilePath = "temp/" + UUID.randomUUID() + ".jar"; |  |  |         String tempFilePath = "temp/" + UUID.randomUUID() + ".jar"; | 
			
		
	
		
		
			
				
					
					|  |  |         File driverFile = doDownload(driverFileUrl, tempFilePath); |  |  |         File driverFile = download(driverFileUrl, tempFilePath); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         String className = doResolveSqlDriverNameFromJar(driverFile); |  |  |         String className = resolveDriverClassName(driverFile); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |         try { |  |  |         try { | 
			
		
	
		
		
			
				
					|  |  |             Files.deleteIfExists(driverFile.toPath()); |  |  |             Files.deleteIfExists(driverFile.toPath()); | 
			
		
	
		
		
			
				
					|  |  |         } catch (IOException e) { |  |  |         } catch (IOException e) { | 
			
		
	
	
		
		
			
				
					|  | @ -48,19 +68,40 @@ public class DriverResources { | 
			
		
	
		
		
			
				
					|  |  |         return className; |  |  |         return className; | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     public File loadOrDownload(String databaseType, String driverFileUrl) { |  |  |     public String resolveDriverClassName(File driverFile) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         String filePath = driverFilePath(driverBaseDirectory, databaseType); |  |  |         JarFile jarFile = null; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         Path path = Path.of(filePath); |  |  |         try { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         if (Files.exists(path)) { |  |  |             jarFile = new JarFile(driverFile); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             // ignore
 |  |  |         } catch (IOException e) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             log.debug("{} already exists, ignore download from {}", filePath, driverFileUrl); |  |  |             log.error("resolve driver class name error", e); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             return path.toFile(); |  |  |             throw DomainErrors.DRIVER_CLASS_NOT_FOUND.exception(e.getMessage()); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |         } |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |         return this.doDownload(driverFileUrl, filePath); |  |  | 
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |         final JarFile driverJar = jarFile; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         String driverClassName = jarFile.stream() | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 .filter(entry -> entry.getName().contains("META-INF/services/java.sql.Driver")) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 .findFirst() | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 .map(entry -> { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                     InputStream stream = null; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                     BufferedReader reader = null; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                     try { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                         stream = driverJar.getInputStream(entry); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                         reader = new BufferedReader(new InputStreamReader(stream)); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                         return reader.readLine(); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                     } catch (IOException e) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                         log.error("resolve driver class name error", e); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                         throw DomainErrors.DRIVER_CLASS_NOT_FOUND.exception(e.getMessage()); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                     } finally { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                         IOUtils.closeQuietly(reader, ex -> log.error("close reader error", ex)); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 }) | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 .orElseThrow(DomainErrors.DRIVER_CLASS_NOT_FOUND::exception); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         IOUtils.closeQuietly(jarFile, ex -> log.error("close jar file error", ex)); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         return driverClassName; | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     private File doDownload(String driverFileUrl, String filePath) { |  |  |     private File download(String driverFileUrl, String targetFile) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         Path path = Path.of(filePath); |  |  |         Path path = Path.of(targetFile); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |         // create parent directory
 |  |  |         // create parent directory
 | 
			
		
	
		
		
			
				
					|  |  |         if (Files.notExists(path)) { |  |  |         if (Files.notExists(path)) { | 
			
		
	
	
		
		
			
				
					|  | @ -68,7 +109,7 @@ public class DriverResources { | 
			
		
	
		
		
			
				
					|  |  |             try { |  |  |             try { | 
			
		
	
		
		
			
				
					|  |  |                 Files.createFile(path); |  |  |                 Files.createFile(path); | 
			
		
	
		
		
			
				
					|  |  |             } catch (IOException e) { |  |  |             } catch (IOException e) { | 
			
		
	
		
		
			
				
					
					|  |  |                 log.error("create file error " + filePath, e); |  |  |                 log.error("create file error " + targetFile, e); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |                 throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage()); |  |  |                 throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage()); | 
			
		
	
		
		
			
				
					|  |  |             } |  |  |             } | 
			
		
	
		
		
			
				
					|  |  |         } |  |  |         } | 
			
		
	
	
		
		
			
				
					|  | @ -81,52 +122,55 @@ public class DriverResources { | 
			
		
	
		
		
			
				
					|  |  |                     FileOutputStream out = new FileOutputStream(file); |  |  |                     FileOutputStream out = new FileOutputStream(file); | 
			
		
	
		
		
			
				
					|  |  |                     StreamUtils.copy(response.getBody(), out); |  |  |                     StreamUtils.copy(response.getBody(), out); | 
			
		
	
		
		
			
				
					|  |  |                     IOUtils.closeQuietly(out, ex -> log.error("close file error", ex)); |  |  |                     IOUtils.closeQuietly(out, ex -> log.error("close file error", ex)); | 
			
		
	
		
		
			
				
					
					|  |  |                     log.info("{} download success ", filePath); |  |  |                     log.info("{} download success ", targetFile); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |                     return file; |  |  |                     return file; | 
			
		
	
		
		
			
				
					|  |  |                 } else { |  |  |                 } else { | 
			
		
	
		
		
			
				
					
					|  |  |                     log.error("{} download error from {}: {} ", filePath, driverFileUrl, response); |  |  |                     log.error("{} download error from {}: {} ", targetFile, driverFileUrl, response); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |                     throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception("驱动下载失败:" |  |  |                     throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception("驱动下载失败:" | 
			
		
	
		
		
			
				
					|  |  |                             + response.getStatusCode() |  |  |                             + response.getStatusCode() | 
			
		
	
		
		
			
				
					|  |  |                             + ", " |  |  |                             + ", " | 
			
		
	
		
		
			
				
					|  |  |                             + response.getStatusText()); |  |  |                             + response.getStatusText()); | 
			
		
	
		
		
			
				
					|  |  |                 } |  |  |                 } | 
			
		
	
		
		
			
				
					|  |  |             }); |  |  |             }); | 
			
		
	
		
		
			
				
					
					|  |  |         } catch (IllegalArgumentException e) { |  |  |         } catch (RestClientException e) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             log.error(filePath + " download driver error", e); |  |  |             log.error(targetFile + " download driver error", e); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |             throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage()); |  |  |             throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage()); | 
			
		
	
		
		
			
				
					|  |  |         } |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     private String doResolveSqlDriverNameFromJar(File driverFile) { |  |  |     public void validateJar(String driverFileUrl, String className) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         JarFile jarFile = null; |  |  |         String tempFilePath = "temp/" + UUID.randomUUID() + ".jar"; | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |         File driverFile = download(driverFileUrl, tempFilePath); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         URLClassLoader loader = null; | 
			
		
	
		
		
			
				
					|  |  |         try { |  |  |         try { | 
			
		
	
		
		
			
				
					
					|  |  |             jarFile = new JarFile(driverFile); |  |  |             loader = new URLClassLoader( | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         } catch (IOException e) { |  |  |                     new URL[]{driverFile.toURI().toURL()}, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             log.error("resolve driver class name error", e); |  |  |                     this.getClass().getClassLoader() | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |             throw DomainErrors.DRIVER_CLASS_NAME_OBTAIN_ERROR.exception(e.getMessage()); |  |  |             ); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  |         } catch (MalformedURLException e) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             log.error("load driver jar error ", e); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage()); | 
			
		
	
		
		
			
				
					|  |  |         } |  |  |         } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |         final JarFile driverJar = jarFile; |  |  |         try { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         String driverClassName = jarFile.stream() |  |  |             Class clazz = Class.forName(className, false, loader); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                 .filter(entry -> entry.getName().contains("META-INF/services/java.sql.Driver")) |  |  |             boolean isValid = ClassUtils.getAllInterfaces(clazz) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                 .findFirst() |  |  |                     .stream() | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                 .map(entry -> { |  |  |                     .anyMatch(cls -> cls.getName().equals("java.sql.Driver")); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                     InputStream stream = null; |  |  |             if (!isValid) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                     BufferedReader reader = null; |  |  |                 throw DomainErrors.DRIVER_CLASS_NOT_FOUND.exception("不合法的驱动类,请重新指定"); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                     try { |  |  |             } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                         stream = driverJar.getInputStream(entry); |  |  |         } catch (ClassNotFoundException e) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                         reader = new BufferedReader(new InputStreamReader(stream)); |  |  |             log.error("init driver error", e); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                         return reader.readLine(); |  |  |             throw DomainErrors.DRIVER_CLASS_NOT_FOUND.exception("驱动初始化异常, 请检查驱动类名:" + e.getMessage()); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                     } catch (IOException e) { |  |  |         } finally { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                         log.error("resolve driver class name error", e); |  |  |             IOUtils.closeQuietly(loader); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                         throw DomainErrors.DRIVER_CLASS_NAME_OBTAIN_ERROR.exception(e.getMessage()); |  |  |             try { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                     } finally { |  |  |                 Files.deleteIfExists(driverFile.toPath()); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                         IOUtils.closeQuietly(reader, ex -> log.error("close reader error", ex)); |  |  |             } catch (IOException e) { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                     } |  |  |                 log.error("delete driver error " + tempFilePath, e); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                 }) |  |  |             } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |                 .orElseThrow(DomainErrors.DRIVER_CLASS_NAME_OBTAIN_ERROR::exception); |  |  |         } | 
			
				
				
			
		
	
		
		
			
				
					|  |  |         IOUtils.closeQuietly(jarFile, ex -> log.error("close jar file error", ex)); |  |  |  | 
			
		
	
		
		
			
				
					|  |  |         return driverClassName; |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     private String driverFilePath(String baseDir, String databaseType) { |  |  |     private String driverFilePath(String baseDir, String databaseType) { | 
			
		
	
	
		
		
			
				
					|  | 
 |