33import org .slf4j .Logger ;
44
55import java .io .*;
6- import java .net .URI ;
7- import java .net .URISyntaxException ;
86import java .net .URL ;
97import java .nio .charset .StandardCharsets ;
108import java .nio .file .Files ;
119import java .nio .file .NoSuchFileException ;
1210import java .nio .file .Paths ;
11+ import java .security .MessageDigest ;
12+ import java .security .NoSuchAlgorithmException ;
1313import java .util .Arrays ;
1414import java .util .List ;
1515import java .util .Locale ;
16+ import java .util .Objects ;
1617import java .util .function .Function ;
1718
1819public final class Execution {
@@ -29,8 +30,9 @@ private Execution() {
2930 private static final String FILE_NAME_MAC = "cx-mac" ;
3031 private static final String FILE_NAME_WINDOWS = "cx.exe" ;
3132 private static final String LINE_SEPARATOR = System .getProperty ("line.separator" );
33+ private static final String TEMP_DIR = System .getProperty ("java.io.tmpdir" );
3234
33- private static URL executable = null ;
35+ private static String executable = null ;
3436
3537 static <T > T executeCommand (List <String > arguments ,
3638 Logger logger ,
@@ -83,6 +85,29 @@ static String executeCommand(List<String> arguments,
8385 StandardCharsets .UTF_8 );
8486 }
8587
88+ static String getTempBinary () throws IOException {
89+ if (executable == null ) {
90+ String fileName = detectBinaryName ();
91+ if (fileName == null ) {
92+ throw new IOException ("Unsupported architecture" );
93+ }
94+ URL resource = Execution .class .getClassLoader ().getResource (fileName );
95+ if (resource == null ) {
96+ throw new NoSuchFileException ("Could not find CLI executable" );
97+ }
98+ File tempExecutable = new File (TEMP_DIR , fileName );
99+ if (!tempExecutable .exists () || !compareChecksum (resource .openStream (),
100+ new FileInputStream (tempExecutable ))) {
101+ copyURLToFile (resource , tempExecutable );
102+ }
103+ if (!tempExecutable .canExecute () && !tempExecutable .setExecutable (true )) {
104+ throw new IOException ("Could not set CLI as executable" );
105+ }
106+ executable = tempExecutable .getAbsolutePath ();
107+ }
108+ return executable ;
109+ }
110+
86111 private static BufferedReader getReader (Process process ) {
87112 InputStream is = process .getInputStream ();
88113 InputStreamReader isr = new InputStreamReader (is );
@@ -95,38 +120,56 @@ private static Process buildProcess(List<String> commands) throws IOException {
95120 return lmBuilder .start ();
96121 }
97122
98- /**
99- * Detect binary name by the current architecture.
100- *
101- * @return binary name
102- * @throws IOException when architecture is unsupported
103- * @throws URISyntaxException when the file has an invalid URI
104- */
105- public static URI detectBinary () throws IOException , URISyntaxException {
106- if (executable == null ) {
107- final String arch = OS_NAME ;
108- String fileName = null ;
109- if (arch .contains (OS_LINUX )) {
110- fileName = FILE_NAME_LINUX ;
111- } else if (arch .contains (OS_WINDOWS )) {
112- fileName = FILE_NAME_WINDOWS ;
113- } else {
114- for (String macStr : OS_MAC ) {
115- if (arch .contains (macStr )) {
116- fileName = FILE_NAME_MAC ;
117- break ;
118- }
123+ private static String detectBinaryName () {
124+ String arch = OS_NAME ;
125+ String fileName = null ;
126+ if (arch .contains (OS_LINUX )) {
127+ fileName = FILE_NAME_LINUX ;
128+ } else if (arch .contains (OS_WINDOWS )) {
129+ fileName = FILE_NAME_WINDOWS ;
130+ } else {
131+ for (String macStr : OS_MAC ) {
132+ if (arch .contains (macStr )) {
133+ fileName = FILE_NAME_MAC ;
134+ break ;
119135 }
120136 }
121- if (fileName == null ) {
122- throw new IOException ("Unsupported architecture" );
137+ }
138+ return fileName ;
139+ }
140+
141+ private static void copyURLToFile (URL source , File destination ) throws IOException {
142+ final byte [] buf = new byte [8192 ];
143+ try (InputStream reader = source .openStream ();
144+ OutputStream writer = new FileOutputStream (destination )) {
145+ int i ;
146+ while ((i = reader .read (buf )) != -1 ) {
147+ writer .write (buf , 0 , i );
123148 }
124- executable = Execution .class .getClassLoader ().getResource (fileName );
149+ } catch (IOException e ) {
150+ throw new IOException ("Could not copy CLI to the temporary directory" , e );
125151 }
126- URL resource = executable ;
127- if (resource == null ) {
128- throw new NoSuchFileException ("Could not find CLI executable" );
152+ }
153+
154+ private static boolean compareChecksum (InputStream a , InputStream b ) {
155+ String aMD5 = md5 (a );
156+ String bMD5 = md5 (b );
157+ return aMD5 != null && bMD5 != null && Objects .equals (aMD5 , bMD5 );
158+ }
159+
160+ private static String md5 (InputStream a ) {
161+ String md5 = null ;
162+ final byte [] buf = new byte [8192 ];
163+ try {
164+ MessageDigest md = MessageDigest .getInstance ("MD5" );
165+ int i ;
166+ while ((i = a .read (buf )) != -1 ) {
167+ md .update (buf , 0 , i );
168+ }
169+ md5 = new String (md .digest ());
170+ } catch (NoSuchAlgorithmException | IOException e ) {
171+ // ignore
129172 }
130- return resource . toURI () ;
173+ return md5 ;
131174 }
132175}
0 commit comments