• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

alibaba / java-dns-cache-manipulator / 1267

pending completion
1267

push

Appveyor

oldratlee
chore(ci): upgrade CI JDK 🤖

527 of 645 relevant lines covered (81.71%)

0.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.48
/library/src/main/java/com/alibaba/dcm/agent/DcmAgent.java
1
package com.alibaba.dcm.agent;
2

3
import com.alibaba.dcm.DnsCache;
4
import com.alibaba.dcm.DnsCacheEntry;
5
import com.alibaba.dcm.DnsCacheManipulator;
6
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
7

8
import javax.annotation.Nonnull;
9
import javax.annotation.Nullable;
10
import java.io.*;
11
import java.lang.instrument.Instrumentation;
12
import java.lang.reflect.InvocationTargetException;
13
import java.lang.reflect.Method;
14
import java.text.SimpleDateFormat;
15
import java.util.*;
16
import java.util.logging.Logger;
17

18
import static java.lang.String.format;
19
import static java.nio.charset.StandardCharsets.UTF_8;
20

21
/**
22
 * DCM agent.
23
 *
24
 * @author Jerry Lee (oldratlee at gmail dot com)
25
 * @see Instrumentation
26
 * @see <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.instrument/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
27
 * @see <a href="https://docs.oracle.com/javase/10/docs/specs/jar/jar.html#jar-manifest">JAR File Specification - JAR Manifest</a>
28
 * @see <a href="https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html">Working with Manifest Files - The Java™ Tutorials</a>
29
 * @since 1.4.0
30
 */
31
public class DcmAgent {
×
32
    private static final Logger logger = Logger.getLogger(DcmAgent.class.getName());
1✔
33

34
    private static final String FILE_KEY = "file";
35

36
    private static final String DCM_AGENT_SUPPRESS_EXCEPTION_STACK = "DCM_AGENT_SUPPRESS_EXCEPTION_STACK";
37

38
    static final String DCM_AGENT_SUCCESS_MARK_LINE = "!!DCM SUCCESS!!";
39

40
    /**
41
     * Entrance method of DCM Java Agent.
42
     */
43
    @SuppressFBWarnings("THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION")
44
    public static void agentmain(@Nonnull String agentArgument) throws Exception {
45
        logger.info(format("%s: attached with agent argument: %s.%n", DcmAgent.class.getName(), agentArgument));
1✔
46

47
        agentArgument = agentArgument.trim();
1✔
48
        if (agentArgument.isEmpty()) {
1✔
49
            logger.info(DcmAgent.class.getName() + ": agent argument is blank, do nothing!");
1✔
50
            return;
1✔
51
        }
52

53
        initAction2Method();
1✔
54

55
        PrintWriter filePrinter = null;
1✔
56
        try {
57
            final Map<String, List<String>> action2Arguments = parseAgentArgument(agentArgument);
1✔
58

59
            // Extract file argument, set file printer if needed
60
            filePrinter = getFilePrintWriter(action2Arguments.remove(FILE_KEY));
1✔
61

62
            if (action2Arguments.isEmpty()) {
1✔
63
                logger.info(DcmAgent.class.getName() + ": No action in agent argument, do nothing!");
1✔
64
                if (filePrinter != null) {
1✔
65
                    filePrinter.printf("No action in agent argument, do nothing! agent argument: %s.%n", agentArgument);
1✔
66
                }
67
                return;
1✔
68
            }
69

70
            boolean allSuccess = true;
1✔
71
            for (Map.Entry<String, List<String>> entry : action2Arguments.entrySet()) {
1✔
72
                final String action = entry.getKey();
1✔
73
                final List<String> arguments = entry.getValue();
1✔
74

75
                boolean success = doAction(action, arguments, filePrinter);
1✔
76
                if (!success) allSuccess = false;
1✔
77
            }
1✔
78

79
            if (allSuccess && filePrinter != null) {
1✔
80
                filePrinter.println(DCM_AGENT_SUCCESS_MARK_LINE);
1✔
81
            }
82
        } finally {
83
            if (filePrinter != null) {
1✔
84
                try {
85
                    filePrinter.close();
1✔
86
                } catch (Exception e) {
×
87
                    // do nothing!
88
                }
1✔
89
            }
90
        }
91
    }
1✔
92

93
    @Nonnull
94
    private static Map<String, List<String>> parseAgentArgument(@Nonnull String argument) {
95
        final String[] split = argument.split("\\s+");
1✔
96

97
        int idx = 0;
1✔
98
        Map<String, List<String>> action2Arguments = new HashMap<>();
1✔
99
        while (idx < split.length) {
1✔
100
            final String action = split[idx++];
1✔
101
            if (!action2Method.containsKey(action)) {
1✔
102
                continue; // TODO error message
1✔
103
            }
104

105
            List<String> arguments = new ArrayList<>();
1✔
106
            while (idx < split.length) {
1✔
107
                if (action2Method.containsKey(split[idx])) {
1✔
108
                    break;
1✔
109
                }
110
                arguments.add(split[idx++]);
1✔
111
            }
112
            action2Arguments.put(action, arguments);
1✔
113
        }
1✔
114

115
        return action2Arguments;
1✔
116
    }
117

118
    @Nullable
119
    private static PrintWriter getFilePrintWriter(@Nullable List<String> files) throws FileNotFoundException {
120
        if (null == files) return null;
1✔
121

122
        FileOutputStream fileOutputStream = new FileOutputStream(files.get(0), false);
1✔
123
        final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, UTF_8);
1✔
124

125
        return new PrintWriter(outputStreamWriter, true);
1✔
126
    }
127

128
    private static boolean doAction(final String action, final List<String> arguments, final PrintWriter filePrinter) {
129
        final String argumentString = join(arguments);
1✔
130

131
        if (!action2Method.containsKey(action)) {
1✔
132
            logger.info(format(("%s: Unknown action %s, ignore! action: %<s %s!%n"), DcmAgent.class.getName(), action, argumentString));
×
133
            if (filePrinter != null) {
×
134
                filePrinter.printf("Unknown action %s, ignore! action: %<s %s !%n", action, argumentString);
×
135
            }
136
            return false;
×
137
        }
138

139
        try {
140
            final Object result = invokeAction(action, arguments.toArray(new String[0]));
1✔
141
            printActionResult(action, result, filePrinter);
1✔
142
            return true;
1✔
143
        } catch (Exception e) {
1✔
144
            final String exString = throwable2StackString(e);
1✔
145
            final String sdtoutExString;
146
            if (isDcmAgentSuppressExceptionStack()) {
1✔
147
                sdtoutExString = e.toString();
1✔
148
            } else {
149
                sdtoutExString = exString;
×
150
            }
151

152
            logger.info(format(("%s: Error to do action %s %s, cause: %s%n"), DcmAgent.class.getName(), action, argumentString, sdtoutExString));
1✔
153
            if (filePrinter != null) {
1✔
154
                filePrinter.printf("Error to do action %s %s, cause: %s%n", action, argumentString, exString);
1✔
155
            }
156

157
            return false;
1✔
158
        }
159
    }
160

161
    private static boolean isDcmAgentSuppressExceptionStack() {
162
        String suppressException = getConfig(DCM_AGENT_SUPPRESS_EXCEPTION_STACK);
1✔
163
        if (suppressException == null) return false;
1✔
164

165
        suppressException = suppressException.trim();
1✔
166
        if (suppressException.length() == 0) return false;
1✔
167

168
        return "true".equalsIgnoreCase(suppressException);
1✔
169
    }
170

171
    private static Object invokeAction(String action, String[] arguments) throws InvocationTargetException, IllegalAccessException {
172
        Method method = action2Method.get(action);
1✔
173

174
        final Class<?>[] parameterTypes = method.getParameterTypes();
1✔
175
        final Object[] methodArgs = convertStringArray2Arguments(action, arguments, parameterTypes);
1✔
176
        return method.invoke(null, methodArgs);
1✔
177
    }
178

179
    private static Object[] convertStringArray2Arguments(String action, String[] arguments, Class<?>[] parameterTypes) {
180
        if (arguments.length < parameterTypes.length) {
1✔
181
            final String message = format("Action %s need more argument! arguments: %s", action, Arrays.toString(arguments));
1✔
182
            throw new IllegalStateException(message);
1✔
183
        }
184
        if (parameterTypes.length == 0) return new Object[0];
1✔
185

186
        final Object[] methodArgs = new Object[parameterTypes.length];
1✔
187

188
        final int lastArgumentIdx = parameterTypes.length - 1;
1✔
189
        if (parameterTypes[(lastArgumentIdx)] == String[].class) {
1✔
190
            // set all tail method argument of type String[]
191
            String[] varArgs = new String[arguments.length - lastArgumentIdx];
1✔
192
            System.arraycopy(arguments, lastArgumentIdx, varArgs, 0, varArgs.length);
1✔
193
            methodArgs[(lastArgumentIdx)] = varArgs;
1✔
194
        } else if (arguments.length > parameterTypes.length) {
1✔
195
            String message = format("Too many arguments for action %s! arguments: %s", action, Arrays.toString(arguments));
1✔
196
            throw new IllegalStateException(message);
1✔
197
        }
198

199
        for (int i = 0; i < parameterTypes.length; i++) {
1✔
200
            // already set
201
            if (methodArgs[i] != null) continue;
1✔
202

203
            Class<?> parameterType = parameterTypes[i];
1✔
204
            final String argument = arguments[i];
1✔
205
            if (parameterType.equals(String.class)) {
1✔
206
                methodArgs[i] = argument;
1✔
207
            } else if (parameterType.equals(int.class)) {
1✔
208
                methodArgs[i] = Integer.parseInt(argument);
1✔
209
            } else {
210
                final String message = format("Unexpected method type %s! Misused or Bug!!", parameterType.getName());
×
211
                throw new IllegalStateException(message);
×
212
            }
213
        }
214

215
        return methodArgs;
1✔
216
    }
217

218
    private static void printActionResult(String action, Object result, PrintWriter writer) {
219
        if (writer == null) return;
1✔
220

221
        final Method method = action2Method.get(action);
1✔
222
        if (method.getReturnType() == void.class) return;
1✔
223

224
        if (result == null) {
×
225
            writer.println((Object) null);
×
226
        } else if (result instanceof DnsCacheEntry) {
×
227
            printDnsCacheEntry((DnsCacheEntry) result, writer);
×
228
        } else if (result instanceof DnsCache) {
×
229
            DnsCache dnsCache = (DnsCache) result;
×
230

231
            printDnsCacheEntryList("Dns cache: ", dnsCache.getCache(), writer);
×
232

233
            writer.println();
×
234
            printDnsCacheEntryList("Dns negative cache: ", dnsCache.getNegativeCache(), writer);
×
235
        } else {
×
236
            writer.println(result);
×
237
        }
238
    }
×
239

240
    private static void printDnsCacheEntryList(String msg, List<DnsCacheEntry> dnsCacheEntries, PrintWriter writer) {
241
        writer.println(msg);
×
242
        for (DnsCacheEntry entry : dnsCacheEntries) {
×
243
            printDnsCacheEntry(entry, writer);
×
244
        }
×
245
    }
×
246

247
    private static void printDnsCacheEntry(DnsCacheEntry entry, PrintWriter writer) {
248
        final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
×
249
        writer.printf("    %s %s %s%n", entry.getHost(), join(Arrays.asList(entry.getIps()), ","), dateFormat.format(entry.getExpiration()));
×
250
    }
×
251

252
    private static volatile Map<String, Method> action2Method;
253
    private static volatile ArrayList<String> actionList;
254

255
    private static synchronized void initAction2Method() throws NoSuchMethodException {
256
        if (action2Method != null) return;
1✔
257

258
        Map<String, Method> map = new LinkedHashMap<>();
1✔
259
        map.put("set", DnsCacheManipulator.class.getMethod("setDnsCache", String.class, String[].class));
1✔
260
        map.put("get", DnsCacheManipulator.class.getMethod("getDnsCache", String.class));
1✔
261
        map.put("rm", DnsCacheManipulator.class.getMethod("removeDnsCache", String.class));
1✔
262

263
        map.put("list", DnsCacheManipulator.class.getMethod("getWholeDnsCache"));
1✔
264
        map.put("ls", DnsCacheManipulator.class.getMethod("getWholeDnsCache"));
1✔
265
        map.put("clear", DnsCacheManipulator.class.getMethod("clearDnsCache"));
1✔
266

267
        map.put("setPolicy", DnsCacheManipulator.class.getMethod("setDnsCachePolicy", int.class));
1✔
268
        map.put("getPolicy", DnsCacheManipulator.class.getMethod("getDnsCachePolicy"));
1✔
269
        map.put("setNegativePolicy", DnsCacheManipulator.class.getMethod("setDnsNegativeCachePolicy", int.class));
1✔
270
        map.put("getNegativePolicy", DnsCacheManipulator.class.getMethod("getDnsNegativeCachePolicy"));
1✔
271

272
        actionList = new ArrayList<>(map.keySet());
1✔
273

274
        map.put(FILE_KEY, null); // FAKE KEY
1✔
275

276
        action2Method = map;
1✔
277
    }
1✔
278

279
    /**
280
     * the action list for DCM agent.
281
     *
282
     * @since 1.6.0
283
     */
284
    @SuppressWarnings("unchecked")
285
    @SuppressFBWarnings("THROWS_METHOD_THROWS_RUNTIMEEXCEPTION")
286
    public static List<String> getActionList() {
287
        try {
288
            initAction2Method();
×
289

290
            return (List<String>) actionList.clone();
×
291
        } catch (Exception e) {
×
292
            throw new RuntimeException("fail to getActionList, cause: " + e, e);
×
293
        }
294
    }
295

296
    ///////////////////////////////////////////////
297
    // util methods
298
    ///////////////////////////////////////////////
299

300
    @Nullable
301
    private static String getConfig(@Nonnull String name) {
302
        String var = System.getenv(name);
1✔
303
        if (var == null || var.trim().length() == 0) {
1✔
304
            var = System.getProperty(name);
×
305
        }
306
        return var;
1✔
307
    }
308

309
    @Nonnull
310
    private static String join(@Nonnull List<String> list) {
311
        return join(list, " ");
1✔
312
    }
313

314
    @Nonnull
315
    private static String join(@Nonnull List<String> list, @Nonnull String separator) {
316
        StringBuilder ret = new StringBuilder();
1✔
317
        for (String argument : list) {
1✔
318
            if (ret.length() > 0) {
1✔
319
                ret.append(separator);
1✔
320
            }
321
            ret.append(argument);
1✔
322
        }
1✔
323
        return ret.toString();
1✔
324
    }
325

326
    @Nonnull
327
    private static String throwable2StackString(@Nonnull Throwable e) {
328
        final StringWriter w = new StringWriter();
1✔
329
        e.printStackTrace(new PrintWriter(w, true));
1✔
330
        return w.toString();
1✔
331
    }
332
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc