001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.q2.install; 020 021import java.io.File; 022import java.io.IOException; 023import java.net.JarURLConnection; 024import java.net.URI; 025import java.net.URISyntaxException; 026import java.net.URL; 027import java.security.MessageDigest; 028import java.security.NoSuchAlgorithmException; 029import java.util.*; 030import java.util.jar.JarEntry; 031import java.util.jar.JarFile; 032import java.util.stream.Collectors; 033 034/** 035 * @author vsalaman 036 */ 037public class ModuleUtils 038{ 039 private static final String MODULES_UUID_DIR = "META-INF/modules/uuids/"; 040 private static final String MODULES_RKEYS_DIR = "META-INF/modules/rkeys/"; 041 042 public static List<String> getModuleEntries(String prefix) throws IOException { 043 List<String> result = new ArrayList<>(); 044 045 Enumeration<URL> urls = ModuleUtils.class.getClassLoader().getResources(prefix); 046 while (urls.hasMoreElements()) { 047 URL url = urls.nextElement(); 048 if (url == null) continue; 049 050 try { 051 List<String> entries; 052 String protocol = url.getProtocol(); 053 if ("jar".equals(protocol)) { 054 entries = resolveModuleEntriesFromJar(url, prefix); 055 } else if ("file".equals(protocol)) { 056 entries = resolveModuleEntriesFromFiles(url, prefix); 057 } else { 058 // Unsupported protocol, skip with optional logging 059 continue; 060 } 061 result.addAll(entries); 062 } catch (URISyntaxException e) { 063 throw new IOException("Bad URL: " + url, e); 064 } 065 } 066 return result; 067 } 068 069 public static List<String> getModulesUUIDs() throws IOException { 070 return getModuleEntries(MODULES_UUID_DIR).stream() 071 .sorted() 072 .map(p -> p.substring(MODULES_UUID_DIR.length())) 073 .collect(Collectors.toList()); 074 } 075 076 public static List<String> getRKeys () throws IOException { 077 return ModuleUtils.getModuleEntries(MODULES_RKEYS_DIR) 078 .stream() 079 .sorted() 080 .map(p -> p.substring(MODULES_RKEYS_DIR.length())) 081 .collect(Collectors.toList()); 082 } 083 084 public static String getSystemHash() throws IOException, NoSuchAlgorithmException { 085 MessageDigest digest = MessageDigest.getInstance("SHA-256"); 086 List<String> uuids = getModulesUUIDs(); 087 if (uuids.isEmpty()) return ""; 088 089 uuids.forEach(uuid -> digest.update(uuid.getBytes())); 090 return Base64.getEncoder().encodeToString(digest.digest()); 091 } 092 093 private static List<String> resolveModuleEntriesFromFiles(URL url, String prefix) 094 throws URISyntaxException { 095 String normalizedPrefix = prefix.endsWith("/") ? prefix : prefix + "/"; 096 List<String> resourceList = new ArrayList<>(); 097 File dir = new File(url.toURI()); 098 addFiles(dir, normalizedPrefix, resourceList); 099 return resourceList; 100 } 101 102 private static void addFiles(File dir, String prefix, List<String> resourceList) { 103 File[] files = dir.listFiles(); 104 if (files == null) return; 105 106 for (File file : files) { 107 if (file.isDirectory()) { 108 addFiles(file, prefix + file.getName() + "/", resourceList); 109 } else { 110 resourceList.add(prefix + file.getName()); 111 } 112 } 113 } 114 115 private static List<String> resolveModuleEntriesFromJar(URL url, String prefix) 116 throws IOException { 117 String normalizedPrefix = prefix.endsWith("/") ? prefix : prefix + "/"; 118 List<String> resourceList = new ArrayList<>(); 119 120 JarURLConnection conn = (JarURLConnection) url.openConnection(); 121 try (JarFile jarFile = conn.getJarFile()) { 122 Enumeration<JarEntry> entries = jarFile.entries(); 123 while (entries.hasMoreElements()) { 124 JarEntry entry = entries.nextElement(); 125 String name = entry.getName(); 126 if (name.startsWith(normalizedPrefix) && !entry.isDirectory()) { 127 resourceList.add(name); 128 } 129 } 130 } 131 return resourceList; 132 } 133}