Requirement background: we wrote a small program to read log files and monitor in real-time, packaged into Jar package executable files, uploaded to each server through our web main system, and then executed by calling ssh command. Before each upload, you should modify or replace the configuration file through the decompression software. This is a little troublesome. You can find a way to dynamically generate the configuration file through the program, then modify or replace the configuration file in the jar package, and finally upload it to each server for execution.
Implementation process: at the beginning, I read a lot of articles and sorted out a tool class for operating JAR package. When I used the methods in the tool class to modify a JAR package file of about 30M, I found that it took 7 seconds, and the method of modifying JAR file is indeed a little complex (this may require professionals developing JDK to provide simple and efficient methods). I can't stand it decisively, Then I wonder if it can be realized by other methods. I came up with a scheme, that is, instead of modifying the file in the JAR through java, I used the JAR command of linux (of course, I also called the linux command through java), first decompressed the JAR package, then replaced the configuration file in the decompressed package with the dynamically generated configuration file, and finally compressed the directory into a JAR file. After testing, it takes about 4 seconds, which is really much faster. After thinking for a long time, I didn't think of any better way. When I was about to implement it, I saw this command: jar uf test.jar manifest.mf - u update the existing JAR package (add files to the JAR package) - f specify the JAR file name; Instant inspiration, directly call this command is not OK! Through this order, it was easy to finish in a few seconds. It was almost at the end of the mountain and water. There was no way to doubt. There was another village with a bright future.
The following is the tool class I sorted out to operate the Jar package (which has passed the test and can be used directly), including reading the file content of the Jar package, traversing the Jar package, modifying the file content of the Jar package and other operations for your reference.
1 package com.agent.util; 2 3 import java.io.BufferedReader; 4 import java.io.ByteArrayOutputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.io.InputStream; 9 import java.io.InputStreamReader; 10 import java.util.Enumeration; 11 import java.util.Iterator; 12 import java.util.Map; 13 import java.util.Set; 14 import java.util.TreeMap; 15 import java.util.jar.JarEntry; 16 import java.util.jar.JarFile; 17 import java.util.jar.JarOutputStream; 18 19 20 public class JarUtil { 21 22 /** 23 * Read all the file contents of the jar package and display the list of jar file contents 24 * @param jarFileName 25 * @throws IOException 26 */ 27 public static void readJARList(String jarFilePath) throws IOException { 28 // Create JAR file object 29 JarFile jarFile = new JarFile(jarFilePath); 30 // Enumeration to obtain the entity in the JAR file, that is, the relative path 31 Enumeration en = jarFile.entries(); 32 System.out.println("file name\t file size\t Compressed size"); 33 // Traversal displays the content information in the JAR file 34 while (en.hasMoreElements()) { 35 // Call method display content 36 process(en.nextElement()); 37 } 38 } 39 40 // Display object information 41 private static void process(Object obj) { 42 // Object to Jar object 43 JarEntry entry = (JarEntry) obj; 44 // File name 45 String name = entry.getName(); 46 // file size 47 long size = entry.getSize(); 48 // Compressed size 49 long compressedSize = entry.getCompressedSize(); 50 System.out.println(name + "\t" + size + "\t" + compressedSize); 51 } 52 53 /** 54 * Read the contents of the specified file in the jar package 55 * @param jarFileName jar Package file path 56 * @param fileName file name 57 * @throws IOException 58 */ 59 public static void readJarFile(String jarFilePath,String fileName) throws IOException{ 60 JarFile jarFile = new JarFile(jarFilePath); 61 JarEntry entry = jarFile.getJarEntry(fileName); 62 InputStream input = jarFile.getInputStream(entry); 63 readFile(input); 64 jarFile.close(); 65 } 66 67 68 public static void readFile(InputStream input) throws IOException{ 69 InputStreamReader in = new InputStreamReader(input); 70 BufferedReader reader = new BufferedReader(in); 71 String line ; 72 while((line = reader.readLine())!=null){ 73 System.out.println(line); 74 } 75 reader.close(); 76 } 77 78 /** 79 * Read stream 80 * 81 * @param inStream 82 * @return Byte array 83 * @throws Exception 84 */ 85 public static byte[] readStream(InputStream inStream) throws Exception { 86 ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); 87 byte[] buffer = new byte[1024]; 88 int len = -1; 89 while ((len = inStream.read(buffer)) != -1) { 90 outSteam.write(buffer, 0, len); 91 } 92 outSteam.close(); 93 inStream.close(); 94 return outSteam.toByteArray(); 95 } 96 97 /** 98 * Modify or add files in Jar package 99 * @param jarFile jar Package path 100 * @param entryName File name to write 101 * @param data File content 102 * @throws Exception 103 */ 104 public static void writeJarFile(String jarFilePath,String entryName,byte[] data) throws Exception{ 105 106 //1. First, read all the contents of the original Jar package into memory and save it with TreeMap 107 JarFile jarFile = new JarFile(jarFilePath); 108 //You can keep the order of arrangement, so use TreeMap instead of HashMap 109 TreeMap tm = new TreeMap(); 110 Enumeration es = jarFile.entries(); 111 while(es.hasMoreElements()){ 112 JarEntry je = (JarEntry)es.nextElement(); 113 byte[] b = readStream(jarFile.getInputStream(je)); 114 tm.put(je.getName(),b); 115 } 116 117 JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFilePath)); 118 Iterator it = tm.entrySet().iterator(); 119 boolean has = false; 120 121 //2. Re write the TreeMap to the original jar. If there is already an entryName file in the TreeMap, overwrite it, otherwise add it at the end 122 while(it.hasNext()){ 123 Map.Entry item = (Map.Entry) it.next(); 124 String name = (String)item.getKey(); 125 JarEntry entry = new JarEntry(name); 126 jos.putNextEntry(entry); 127 byte[] temp ; 128 if(name.equals(entryName)){ 129 //cover 130 temp = data; 131 has = true ; 132 }else{ 133 temp = (byte[])item.getValue(); 134 } 135 jos.write(temp, 0, temp.length); 136 } 137 138 if(!has){ 139 //Finally add 140 JarEntry newEntry = new JarEntry(entryName); 141 jos.putNextEntry(newEntry); 142 jos.write(data, 0, data.length); 143 } 144 jos.finish(); 145 jos.close(); 146 147 } 148 149 150 /** 151 * Test case 152 * @param args 153 * @throws Exception 154 */ 155 public static void main(String args[]) throws Exception{ 156 157 // 158 readJarFile("D:\\esjavaclient-0.0.1-SNAPSHOT.jar","es-info.properties"); 159 160 String data = "helloBabydsafsadfasdfsdafsdgasdgweqtqwegtqwfwefasdfasfadfasf"; 161 162 long start = System.currentTimeMillis(); 163 164 writeJarFile("D:\\esjavaclient-0.0.1-SNAPSHOT.jar","es-info.properties",data.getBytes()); 165 166 long end = System.currentTimeMillis(); 167 168 System.out.println(end-start); 169 170 readJarFile("D:\\esjavaclient-0.0.1-SNAPSHOT.jar","es-info.properties"); 171 172 } 173 174 }
There is a test case above. The test reads one of the configuration files, then modifies the file, and finally reads it out. A 30M Jar package file takes more than 7 seconds, which is unbearable. I found many articles on the Internet, read the Zip related source code, and found a way to directly modify the contents of Jar package files. Finally, we can only find another way to find a very efficient method.
As for the tool class, there is no method to delete files. You can modify it slightly according to the modified method.
Blog for tool class reference:
http://blog.csdn.net/young_kim1/article/details/50482398
http://javasam.iteye.com/blog/1486803
How to quickly modify the file contents in Jar package:
Post the code I cleverly implemented: (if you don't know how Java calls linux commands, please fill in this knowledge first)
1 String cmd = "jar uf esjavaclient-0.0.1-SNAPSHOT.jar config.properties"; 2 String[] cmds = {"/bin/sh","-c",cmd}; 3 Process pro; 4 try { 5 pro = Runtime.getRuntime().exec(cmds); 6 pro.waitFor(); 7 InputStream in = pro.getInputStream(); 8 BufferedReader read = new BufferedReader(new InputStreamReader(in)); String line = null; while((line = read.readLine())!=null){ System.out.println(line); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
By executing these commands, we can quickly overwrite the configuration file config.properties in Jar to achieve the purpose of modification.
Conclusion: if the file in the Jar package is not large, you can use the methods provided by the tool class to operate the Jar package. If it is relatively large, I recommend using my ingenious method to modify the Jar package.
Finally: give some suggestions to professionals developing JDK. The API for operating compressed packages is not strong enough. I hope it can be improved in the future
Reproduced at: https://www.cnblogs.com/pfblog/p/7227184.html
Related resources: Used to modify the jar package path_ Java modify jar package file, jar package file path - Java