class EtcdOperation implements DisposableBean {
    private static final Logger LOG = LoggerFactory.getLogger(EtcdOperation.class);

    @Autowired
    private Client client;

    public String getConfig(String group, String serviceId) throws ExecutionException, InterruptedException {
        ByteSequence byteSequence = ByteSequence.from(group + "-" + serviceId, StandardCharsets.UTF_8);

        KV kvClient = client.getKVClient();
        GetResponse getResponse = kvClient.get(byteSequence).get();
        if (getResponse.getKvs().size() > 0) {
            KeyValue keyValue = getResponse.getKvs().get(0);

            return keyValue.getValue().toString(StandardCharsets.UTF_8);
        }

        return null;
    }

    public boolean removeConfig(String group, String serviceId) throws ExecutionException, InterruptedException {
        ByteSequence byteSequence = ByteSequence.from(group + "-" + serviceId, StandardCharsets.UTF_8);

        KV kvClient = client.getKVClient();
        kvClient.delete(byteSequence);

        return true;
    }

    public boolean publishConfig(String group, String serviceId, String config) throws ExecutionException, InterruptedException {<FILL_FUNCTION_BODY>}

    public Watch subscribeConfig(String group, String serviceId, EtcdSubscribeCallback etcdSubscribeCallback) throws Exception {
        ByteSequence byteSequence = ByteSequence.from(group + "-" + serviceId, StandardCharsets.UTF_8);

        Watch watchClient = client.getWatchClient();
        Listener listener = new Listener() {
            @Override
            public void onNext(WatchResponse response) {
                for (WatchEvent event : response.getEvents()) {
                    EventType eventType = event.getEventType();
                    if (eventType == EventType.PUT) {
                        KeyValue keyValue = event.getKeyValue();
                        if (keyValue != null) {
                            String config = keyValue.getValue().toString(StandardCharsets.UTF_8);

                            etcdSubscribeCallback.callback(config);
                        }
                    } else if (eventType == EventType.DELETE) {
                        etcdSubscribeCallback.callback(null);
                    }
                }
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onCompleted() {

            }
        };
        watchClient.watch(byteSequence, listener);

        return watchClient;
    }

    public void unsubscribeConfig(String group, String serviceId, Watch watchClient) {
        watchClient.close();
    }

    @Override
    public void destroy() throws Exception {
        client.close();

        LOG.info("Shutting down JEtcd client...");
    }
}

        ByteSequence keyByteSequence = ByteSequence.from(group + "-" + serviceId, StandardCharsets.UTF_8);
        ByteSequence valueByteSequence = ByteSequence.from(config, StandardCharsets.UTF_8);

        KV kvClient = client.getKVClient();
        kvClient.put(keyByteSequence, valueByteSequence);

        return true;
    
<no_super_class>