From: AsamK Date: Sat, 16 Jan 2021 19:11:32 +0000 (+0100) Subject: Extract JsonWriter for json output X-Git-Tag: v0.7.3~10 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/03bf0439593dc1c5e02bdd49e18d649d1c24e721 Extract JsonWriter for json output --- diff --git a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java index eb135e13..818f2881 100644 --- a/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/JsonReceiveMessageHandler.java @@ -1,45 +1,42 @@ package org.asamk.signal; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; - import org.asamk.signal.json.JsonError; import org.asamk.signal.json.JsonMessageEnvelope; import org.asamk.signal.manager.Manager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.messages.SignalServiceContent; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; public class JsonReceiveMessageHandler implements Manager.ReceiveMessageHandler { - final Manager m; - private final ObjectMapper jsonProcessor; + private final static Logger logger = LoggerFactory.getLogger(JsonReceiveMessageHandler.class); + + protected final Manager m; + private final JsonWriter jsonWriter; public JsonReceiveMessageHandler(Manager m) { this.m = m; - this.jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + jsonWriter = new JsonWriter(System.out); } @Override public void handleMessage(SignalServiceEnvelope envelope, SignalServiceContent content, Throwable exception) { - ObjectNode result = jsonProcessor.createObjectNode(); + final Map object = new HashMap<>(); if (exception != null) { - result.putPOJO("error", new JsonError(exception)); + object.put("error", new JsonError(exception)); } if (envelope != null) { - result.putPOJO("envelope", new JsonMessageEnvelope(envelope, content, m)); + object.put("envelope", new JsonMessageEnvelope(envelope, content, m)); } try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } } diff --git a/src/main/java/org/asamk/signal/JsonWriter.java b/src/main/java/org/asamk/signal/JsonWriter.java new file mode 100644 index 00000000..3cc87514 --- /dev/null +++ b/src/main/java/org/asamk/signal/JsonWriter.java @@ -0,0 +1,37 @@ +package org.asamk.signal; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; + +public class JsonWriter { + + private final OutputStreamWriter writer; + private final ObjectMapper objectMapper; + + public JsonWriter(final OutputStream writer) { + this.writer = new OutputStreamWriter(writer, StandardCharsets.UTF_8); + + objectMapper = new ObjectMapper(); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + } + + public void write(final Object object) throws IOException { + try { + objectMapper.writeValue(writer, object); + } catch (JsonProcessingException e) { + // Some issue with json serialization, probably caused by a bug + throw new AssertionError(e); + } + writer.write(System.lineSeparator()); + writer.flush(); + } +} diff --git a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java index 447cbb03..103d1027 100644 --- a/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java +++ b/src/main/java/org/asamk/signal/commands/GetUserStatusCommand.java @@ -1,12 +1,10 @@ package org.asamk.signal.commands; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; - import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.manager.Manager; import org.slf4j.Logger; @@ -20,7 +18,6 @@ import java.util.stream.Collectors; public class GetUserStatusCommand implements LocalCommand { - // TODO delete later when "json" variable is removed private final static Logger logger = LoggerFactory.getLogger(GetUserStatusCommand.class); @Override @@ -35,9 +32,6 @@ public class GetUserStatusCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { // Setup the json object mapper - ObjectMapper jsonProcessor = new ObjectMapper(); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -56,16 +50,18 @@ public class GetUserStatusCommand implements LocalCommand { // Output if (inJson) { - List objects = registered.entrySet() + final JsonWriter jsonWriter = new JsonWriter(System.out); + + List jsonUserStatuses = registered.entrySet() .stream() - .map(entry -> new JsonIsRegistered(entry.getKey(), entry.getValue())) + .map(entry -> new JsonUserStatus(entry.getKey(), entry.getValue())) .collect(Collectors.toList()); try { - jsonProcessor.writeValue(System.out, objects); - System.out.println(); + jsonWriter.write(jsonUserStatuses); } catch (IOException e) { - System.err.println(e.getMessage()); + logger.error("Failed to write json object: {}", e.getMessage()); + return 3; } } else { for (Map.Entry entry : registered.entrySet()) { @@ -76,13 +72,13 @@ public class GetUserStatusCommand implements LocalCommand { return 0; } - private static final class JsonIsRegistered { + private static final class JsonUserStatus { public String name; public boolean isRegistered; - public JsonIsRegistered(String name, boolean isRegistered) { + public JsonUserStatus(String name, boolean isRegistered) { this.name = name; this.isRegistered = isRegistered; } diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 323fc098..b178b09c 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -1,18 +1,16 @@ package org.asamk.signal.commands; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; - import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.manager.Manager; import org.asamk.signal.manager.groups.GroupInviteLinkUrl; import org.asamk.signal.manager.storage.groups.GroupInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import java.io.IOException; @@ -23,6 +21,8 @@ import java.util.stream.Collectors; public class ListGroupsCommand implements LocalCommand { + private final static Logger logger = LoggerFactory.getLogger(ListGroupsCommand.class); + private static Set resolveMembers(Manager m, Set addresses) { return addresses.stream() .map(m::resolveSignalServiceAddress) @@ -30,18 +30,6 @@ public class ListGroupsCommand implements LocalCommand { .collect(Collectors.toSet()); } - private static int printGroupsJson(ObjectMapper jsonProcessor, List objects) { - try { - jsonProcessor.writeValue(System.out, objects); - System.out.println(); - } catch (IOException e) { - System.err.println(e.getMessage()); - return 3; - } - - return 0; - } - private static void printGroupPlainText(Manager m, GroupInfo group, boolean detailed) { if (detailed) { final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); @@ -77,15 +65,13 @@ public class ListGroupsCommand implements LocalCommand { @Override public int handleCommand(final Namespace ns, final Manager m) { if (ns.get("output") == OutputType.JSON) { - final ObjectMapper jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + final JsonWriter jsonWriter = new JsonWriter(System.out); - List objects = new ArrayList<>(); + List jsonGroups = new ArrayList<>(); for (GroupInfo group : m.getGroups()) { final GroupInviteLinkUrl groupInviteLink = group.getGroupInviteLink(); - objects.add(new JsonGroup(group.getGroupId().toBase64(), + jsonGroups.add(new JsonGroup(group.getGroupId().toBase64(), group.getTitle(), group.isMember(m.getSelfAddress()), group.isBlocked(), @@ -94,7 +80,15 @@ public class ListGroupsCommand implements LocalCommand { resolveMembers(m, group.getRequestingMembers()), groupInviteLink == null ? null : groupInviteLink.getUrl())); } - return printGroupsJson(jsonProcessor, objects); + + try { + jsonWriter.write(jsonGroups); + } catch (IOException e) { + logger.error("Failed to write json object: {}", e.getMessage()); + return 3; + } + + return 0; } else { boolean detailed = ns.getBoolean("detailed"); for (GroupInfo group : m.getGroups()) { diff --git a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java index e1d1581e..445b17c9 100644 --- a/src/main/java/org/asamk/signal/commands/ReceiveCommand.java +++ b/src/main/java/org/asamk/signal/commands/ReceiveCommand.java @@ -1,17 +1,12 @@ package org.asamk.signal.commands; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; - import net.sourceforge.argparse4j.impl.Arguments; import net.sourceforge.argparse4j.inf.Namespace; import net.sourceforge.argparse4j.inf.Subparser; import org.asamk.Signal; import org.asamk.signal.JsonReceiveMessageHandler; +import org.asamk.signal.JsonWriter; import org.asamk.signal.OutputType; import org.asamk.signal.ReceiveMessageHandler; import org.asamk.signal.json.JsonMessageEnvelope; @@ -24,13 +19,13 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Base64; +import java.util.Map; import java.util.concurrent.TimeUnit; import static org.asamk.signal.util.ErrorUtils.handleAssertionError; public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { - // TODO delete later when "json" variable is removed private final static Logger logger = LoggerFactory.getLogger(ReceiveCommand.class); @Override @@ -47,8 +42,6 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { } public int handleCommand(final Namespace ns, final Signal signal, DBusConnection dbusconnection) { - final ObjectMapper jsonProcessor; - boolean inJson = ns.get("output") == OutputType.JSON || ns.getBoolean("json"); // TODO delete later when "json" variable is removed @@ -56,24 +49,16 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { logger.warn("\"--json\" option has been deprecated, please use the global \"--output=json\" instead."); } - if (inJson) { - jsonProcessor = new ObjectMapper(); - jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - } else { - jsonProcessor = null; - } + final JsonWriter jsonWriter = inJson ? new JsonWriter(System.out) : null; try { dbusconnection.addSigHandler(Signal.MessageReceived.class, messageReceived -> { - if (jsonProcessor != null) { + if (jsonWriter != null) { JsonMessageEnvelope envelope = new JsonMessageEnvelope(messageReceived); - ObjectNode result = jsonProcessor.createObjectNode(); - result.putPOJO("envelope", envelope); + final Map object = Map.of("envelope", envelope); try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } else { System.out.print(String.format("Envelope from: %s\nTimestamp: %s\nBody: %s\n", @@ -95,15 +80,13 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { }); dbusconnection.addSigHandler(Signal.ReceiptReceived.class, receiptReceived -> { - if (jsonProcessor != null) { + if (jsonWriter != null) { JsonMessageEnvelope envelope = new JsonMessageEnvelope(receiptReceived); - ObjectNode result = jsonProcessor.createObjectNode(); - result.putPOJO("envelope", envelope); + final Map object = Map.of("envelope", envelope); try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } else { System.out.print(String.format("Receipt from: %s\nTimestamp: %s\n", @@ -113,15 +96,13 @@ public class ReceiveCommand implements ExtendedDbusCommand, LocalCommand { }); dbusconnection.addSigHandler(Signal.SyncMessageReceived.class, syncReceived -> { - if (jsonProcessor != null) { + if (jsonWriter != null) { JsonMessageEnvelope envelope = new JsonMessageEnvelope(syncReceived); - ObjectNode result = jsonProcessor.createObjectNode(); - result.putPOJO("envelope", envelope); + final Map object = Map.of("envelope", envelope); try { - jsonProcessor.writeValue(System.out, result); - System.out.println(); + jsonWriter.write(object); } catch (IOException e) { - e.printStackTrace(); + logger.error("Failed to write json object: {}", e.getMessage()); } } else { System.out.print(String.format("Sync Envelope from: %s to: %s\nTimestamp: %s\nBody: %s\n", diff --git a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index c9051335..a6e243a8 100644 --- a/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -91,7 +91,7 @@ public class SignalAccount implements Closeable { this.fileChannel = fileChannel; this.lock = lock; jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); // disable autodetect - jsonProcessor.enable(SerializationFeature.INDENT_OUTPUT); // for pretty print, you can disable it. + jsonProcessor.enable(SerializationFeature.INDENT_OUTPUT); // for pretty print jsonProcessor.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); jsonProcessor.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE); jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);