|
1 | 1 | package com.codename1.ant; |
2 | 2 |
|
| 3 | +import java.io.ByteArrayOutputStream; |
3 | 4 | import java.io.File; |
| 5 | +import java.io.IOException; |
| 6 | +import java.io.OutputStream; |
| 7 | +import java.io.PrintStream; |
| 8 | +import java.io.UnsupportedEncodingException; |
4 | 9 | import java.util.Properties; |
5 | 10 |
|
6 | 11 | import org.apache.tools.ant.BuildException; |
@@ -31,61 +36,154 @@ public static boolean executeAntTask(String buildXmlFileFullPath) { |
31 | 36 | public static boolean executeAntTask(String buildXmlFileFullPath, String target, Properties properties) { |
32 | 37 |
|
33 | 38 | boolean success = false; |
34 | | - DefaultLogger consoleLogger = getConsoleLogger(); |
35 | 39 |
|
36 | | - // Prepare Ant project |
37 | | - Project project = new Project(); |
38 | | - File buildFile = new File(buildXmlFileFullPath); |
39 | | - |
40 | | - project.setBasedir(buildFile.getParentFile().getAbsolutePath()); |
41 | | - project.setBaseDir(buildFile.getParentFile()); |
42 | | - |
43 | | - project.setUserProperty("ant.file", buildFile.getAbsolutePath()); |
44 | | - if (properties != null) { |
45 | | - for (String k : properties.stringPropertyNames()) { |
46 | | - project.setProperty(k, properties.getProperty(k)); |
47 | | - } |
| 40 | + // Tee stdout/stderr so that, on failure, we can recover server-reported |
| 41 | + // error details (such as the JSON body returned by the build server) that |
| 42 | + // the build client prints but does not propagate via the exception message. |
| 43 | + ByteArrayOutputStream captured = new ByteArrayOutputStream(); |
| 44 | + PrintStream originalOut = System.out; |
| 45 | + PrintStream originalErr = System.err; |
| 46 | + PrintStream teeOut; |
| 47 | + PrintStream teeErr; |
| 48 | + try { |
| 49 | + teeOut = new PrintStream(new TeeOutputStream(originalOut, captured), true, "UTF-8"); |
| 50 | + teeErr = new PrintStream(new TeeOutputStream(originalErr, captured), true, "UTF-8"); |
| 51 | + } catch (UnsupportedEncodingException e) { |
| 52 | + throw new IllegalStateException("UTF-8 not supported", e); |
48 | 53 | } |
49 | 54 |
|
50 | | - project.addBuildListener(consoleLogger); |
| 55 | + DefaultLogger consoleLogger = new DefaultLogger(); |
| 56 | + consoleLogger.setErrorPrintStream(teeErr); |
| 57 | + consoleLogger.setOutputPrintStream(teeOut); |
| 58 | + consoleLogger.setMessageOutputLevel(Project.MSG_INFO); |
| 59 | + |
| 60 | + System.setOut(teeOut); |
| 61 | + System.setErr(teeErr); |
51 | 62 |
|
52 | | - // Capture event for Ant script build start / stop / failure |
53 | 63 | try { |
54 | | - project.fireBuildStarted(); |
55 | | - project.init(); |
56 | | - ProjectHelper projectHelper = ProjectHelper.getProjectHelper(); |
57 | | - |
58 | | - project.addReference("ant.projectHelper", projectHelper); |
59 | | - |
60 | | - projectHelper.parse(project, buildFile); |
61 | | - |
62 | | - // If no target specified then default target will be executed. |
63 | | - String targetToExecute = (target != null && target.trim().length() > 0) ? target.trim() : project.getDefaultTarget(); |
64 | | - project.executeTarget(targetToExecute); |
65 | | - project.fireBuildFinished(null); |
66 | | - success = true; |
67 | | - } catch (BuildException buildException) { |
68 | | - project.fireBuildFinished(buildException); |
69 | | - throw new RuntimeException("!!! Unable to restart the IEHS App !!!", buildException); |
| 64 | + // Prepare Ant project |
| 65 | + Project project = new Project(); |
| 66 | + File buildFile = new File(buildXmlFileFullPath); |
| 67 | + |
| 68 | + project.setBasedir(buildFile.getParentFile().getAbsolutePath()); |
| 69 | + project.setBaseDir(buildFile.getParentFile()); |
| 70 | + |
| 71 | + project.setUserProperty("ant.file", buildFile.getAbsolutePath()); |
| 72 | + if (properties != null) { |
| 73 | + for (String k : properties.stringPropertyNames()) { |
| 74 | + project.setProperty(k, properties.getProperty(k)); |
| 75 | + } |
| 76 | + } |
| 77 | + |
| 78 | + project.addBuildListener(consoleLogger); |
| 79 | + |
| 80 | + // Capture event for Ant script build start / stop / failure |
| 81 | + try { |
| 82 | + project.fireBuildStarted(); |
| 83 | + project.init(); |
| 84 | + ProjectHelper projectHelper = ProjectHelper.getProjectHelper(); |
| 85 | + |
| 86 | + project.addReference("ant.projectHelper", projectHelper); |
| 87 | + |
| 88 | + projectHelper.parse(project, buildFile); |
| 89 | + |
| 90 | + // If no target specified then default target will be executed. |
| 91 | + String targetToExecute = (target != null && target.trim().length() > 0) ? target.trim() : project.getDefaultTarget(); |
| 92 | + project.executeTarget(targetToExecute); |
| 93 | + project.fireBuildFinished(null); |
| 94 | + success = true; |
| 95 | + } catch (BuildException buildException) { |
| 96 | + project.fireBuildFinished(buildException); |
| 97 | + teeOut.flush(); |
| 98 | + teeErr.flush(); |
| 99 | + String capturedText; |
| 100 | + try { |
| 101 | + capturedText = captured.toString("UTF-8"); |
| 102 | + } catch (UnsupportedEncodingException e) { |
| 103 | + throw new IllegalStateException("UTF-8 not supported", e); |
| 104 | + } |
| 105 | + String detail = extractServerErrorDetail(capturedText); |
| 106 | + StringBuilder message = new StringBuilder("Ant task failed: ").append(buildException.getMessage()); |
| 107 | + if (detail != null) { |
| 108 | + message.append(System.lineSeparator()).append(detail); |
| 109 | + } |
| 110 | + throw new RuntimeException(message.toString(), buildException); |
| 111 | + } |
| 112 | + } finally { |
| 113 | + System.setOut(originalOut); |
| 114 | + System.setErr(originalErr); |
70 | 115 | } |
71 | 116 |
|
72 | 117 | return success; |
| 118 | + } |
73 | 119 |
|
74 | | - |
| 120 | + /** |
| 121 | + * Scans build output for server-reported error markers (HTTP status, response |
| 122 | + * message, JSON error body) and returns them joined by newlines, or {@code null} |
| 123 | + * if none were found. |
| 124 | + */ |
| 125 | + static String extractServerErrorDetail(String log) { |
| 126 | + if (log == null || log.isEmpty()) { |
| 127 | + return null; |
| 128 | + } |
| 129 | + StringBuilder sb = new StringBuilder(); |
| 130 | + String[] lines = log.split("\\r?\\n"); |
| 131 | + for (String raw : lines) { |
| 132 | + String line = raw.trim(); |
| 133 | + if (line.isEmpty()) { |
| 134 | + continue; |
| 135 | + } |
| 136 | + if (line.startsWith("Response message from server is:") |
| 137 | + || line.startsWith("Server Detailed Error Message:") |
| 138 | + || line.startsWith("Server returned HTTP response code:") |
| 139 | + || line.contains("Server returned HTTP response code:")) { |
| 140 | + if (sb.length() > 0) { |
| 141 | + sb.append(System.lineSeparator()); |
| 142 | + } |
| 143 | + sb.append(line); |
| 144 | + } |
| 145 | + } |
| 146 | + return sb.length() == 0 ? null : sb.toString(); |
75 | 147 | } |
76 | 148 |
|
77 | 149 | /** |
78 | | - * Logger to log output generated while executing ant script in console |
79 | | - * |
80 | | - * @return |
| 150 | + * OutputStream that writes to two underlying streams. Used to forward Ant |
| 151 | + * output to the original console while also retaining a copy for diagnostics. |
81 | 152 | */ |
82 | | - private static DefaultLogger getConsoleLogger() { |
83 | | - DefaultLogger consoleLogger = new DefaultLogger(); |
84 | | - consoleLogger.setErrorPrintStream(System.err); |
85 | | - consoleLogger.setOutputPrintStream(System.out); |
86 | | - consoleLogger.setMessageOutputLevel(Project.MSG_INFO); |
| 153 | + private static final class TeeOutputStream extends OutputStream { |
| 154 | + private final OutputStream a; |
| 155 | + private final OutputStream b; |
87 | 156 |
|
88 | | - return consoleLogger; |
89 | | - } |
| 157 | + TeeOutputStream(OutputStream a, OutputStream b) { |
| 158 | + this.a = a; |
| 159 | + this.b = b; |
| 160 | + } |
| 161 | + |
| 162 | + @Override |
| 163 | + public void write(int byteValue) throws IOException { |
| 164 | + a.write(byteValue); |
| 165 | + b.write(byteValue); |
| 166 | + } |
| 167 | + |
| 168 | + @Override |
| 169 | + public void write(byte[] buf, int off, int len) throws IOException { |
| 170 | + a.write(buf, off, len); |
| 171 | + b.write(buf, off, len); |
| 172 | + } |
| 173 | + |
| 174 | + @Override |
| 175 | + public void flush() throws IOException { |
| 176 | + a.flush(); |
| 177 | + b.flush(); |
| 178 | + } |
90 | 179 |
|
| 180 | + @Override |
| 181 | + public void close() throws IOException { |
| 182 | + try { |
| 183 | + a.close(); |
| 184 | + } finally { |
| 185 | + b.close(); |
| 186 | + } |
| 187 | + } |
| 188 | + } |
91 | 189 | } |
0 commit comments