reconfigure node when precondition is not met
This commit is contained in:
parent
58b1e26367
commit
3be56b6775
|
@ -45,7 +45,7 @@ public abstract class AxolotlDao {
|
||||||
|
|
||||||
@Query(
|
@Query(
|
||||||
"SELECT EXISTS(SELECT deviceId FROM axolotl_device_list JOIN axolotl_device_list_item"
|
"SELECT EXISTS(SELECT deviceId FROM axolotl_device_list JOIN axolotl_device_list_item"
|
||||||
+ " ON axolotl_device_list.id=axolotl_device_list_item.deviceId WHERE"
|
+ " ON axolotl_device_list.id=axolotl_device_list_item.deviceListId WHERE"
|
||||||
+ " accountId=:account AND address=:address AND deviceId=:deviceId)")
|
+ " accountId=:account AND address=:address AND deviceId=:deviceId)")
|
||||||
public abstract boolean hasDeviceId(final long account, final Jid address, final int deviceId);
|
public abstract boolean hasDeviceId(final long account, final Jid address, final int deviceId);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package im.conversations.android.xmpp;
|
package im.conversations.android.xmpp;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import im.conversations.android.xmpp.model.error.Condition;
|
||||||
import im.conversations.android.xmpp.model.error.Error;
|
import im.conversations.android.xmpp.model.error.Error;
|
||||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||||
|
|
||||||
|
@ -19,7 +21,12 @@ public class IqErrorException extends Exception {
|
||||||
private static String getErrorText(final Iq response) {
|
private static String getErrorText(final Iq response) {
|
||||||
final var error = response.getError();
|
final var error = response.getError();
|
||||||
final var text = error == null ? null : error.getText();
|
final var text = error == null ? null : error.getText();
|
||||||
return text == null ? null : text.getContent();
|
final var textContent = text == null ? null : text.getContent();
|
||||||
|
if (Strings.isNullOrEmpty(textContent)) {
|
||||||
|
final var condition = error == null ? null : error.getExtension(Condition.class);
|
||||||
|
return condition == null ? null : condition.getName();
|
||||||
|
}
|
||||||
|
return textContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iq getResponse() {
|
public Iq getResponse() {
|
||||||
|
|
|
@ -22,6 +22,12 @@ public class NodeConfiguration implements Map<String, Object> {
|
||||||
.put(PERSIST_ITEMS, Boolean.TRUE)
|
.put(PERSIST_ITEMS, Boolean.TRUE)
|
||||||
.put(ACCESS_MODEL, "open")
|
.put(ACCESS_MODEL, "open")
|
||||||
.build());
|
.build());
|
||||||
|
public static final NodeConfiguration PRESENCE =
|
||||||
|
new NodeConfiguration(
|
||||||
|
new ImmutableMap.Builder<String, Object>()
|
||||||
|
.put(PERSIST_ITEMS, Boolean.TRUE)
|
||||||
|
.put(ACCESS_MODEL, "presence")
|
||||||
|
.build());
|
||||||
public static final NodeConfiguration WHITELIST_MAX_ITEMS =
|
public static final NodeConfiguration WHITELIST_MAX_ITEMS =
|
||||||
new NodeConfiguration(
|
new NodeConfiguration(
|
||||||
new ImmutableMap.Builder<String, Object>()
|
new ImmutableMap.Builder<String, Object>()
|
||||||
|
|
|
@ -10,6 +10,7 @@ public class PreconditionNotMetException extends PubSubErrorException {
|
||||||
if (this.pubSubError instanceof PubSubError.PreconditionNotMet) {
|
if (this.pubSubError instanceof PubSubError.PreconditionNotMet) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new AssertionError("This exception should only be constructed for PreconditionNotMet errors");
|
throw new AssertionError(
|
||||||
|
"This exception should only be constructed for PreconditionNotMet errors");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,7 +172,7 @@ public class AxolotlManager extends AbstractManager {
|
||||||
new FutureCallback<>() {
|
new FutureCallback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Void result) {
|
public void onSuccess(Void result) {
|
||||||
LOGGER.info("Successfully publish bundle and device ID {}", myDeviceId);
|
LOGGER.info("Successfully published bundle and device ID {}", myDeviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -39,9 +39,6 @@ public class PresenceManager extends AbstractManager {
|
||||||
final var presence = new Presence();
|
final var presence = new Presence();
|
||||||
presence.addExtension(capabilities);
|
presence.addExtension(capabilities);
|
||||||
presence.addExtension(legacyCapabilities);
|
presence.addExtension(legacyCapabilities);
|
||||||
|
|
||||||
LOGGER.info(presence.toString());
|
|
||||||
|
|
||||||
connection.sendPresencePacket(presence);
|
connection.sendPresencePacket(presence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,16 @@ import im.conversations.android.xmpp.PreconditionNotMetException;
|
||||||
import im.conversations.android.xmpp.PubSubErrorException;
|
import im.conversations.android.xmpp.PubSubErrorException;
|
||||||
import im.conversations.android.xmpp.XmppConnection;
|
import im.conversations.android.xmpp.XmppConnection;
|
||||||
import im.conversations.android.xmpp.model.Extension;
|
import im.conversations.android.xmpp.model.Extension;
|
||||||
|
import im.conversations.android.xmpp.model.data.Data;
|
||||||
import im.conversations.android.xmpp.model.pubsub.Items;
|
import im.conversations.android.xmpp.model.pubsub.Items;
|
||||||
import im.conversations.android.xmpp.model.pubsub.PubSub;
|
import im.conversations.android.xmpp.model.pubsub.PubSub;
|
||||||
|
import im.conversations.android.xmpp.model.pubsub.Publish;
|
||||||
import im.conversations.android.xmpp.model.pubsub.PublishOptions;
|
import im.conversations.android.xmpp.model.pubsub.PublishOptions;
|
||||||
import im.conversations.android.xmpp.model.pubsub.error.PubSubError;
|
import im.conversations.android.xmpp.model.pubsub.error.PubSubError;
|
||||||
import im.conversations.android.xmpp.model.pubsub.event.Event;
|
import im.conversations.android.xmpp.model.pubsub.event.Event;
|
||||||
import im.conversations.android.xmpp.model.pubsub.event.Purge;
|
import im.conversations.android.xmpp.model.pubsub.event.Purge;
|
||||||
|
import im.conversations.android.xmpp.model.pubsub.owner.Configure;
|
||||||
|
import im.conversations.android.xmpp.model.pubsub.owner.PubSubOwner;
|
||||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||||
import im.conversations.android.xmpp.model.stanza.Message;
|
import im.conversations.android.xmpp.model.stanza.Message;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -229,9 +233,9 @@ public class PubSubManager extends AbstractManager {
|
||||||
iq.setTo(address);
|
iq.setTo(address);
|
||||||
final var pubSub = iq.addExtension(new PubSub());
|
final var pubSub = iq.addExtension(new PubSub());
|
||||||
pubSub.addExtension(PublishOptions.of(nodeConfiguration));
|
pubSub.addExtension(PublishOptions.of(nodeConfiguration));
|
||||||
final var pubSubItemsWrapper = pubSub.addExtension(new PubSub.ItemsWrapper());
|
final var publish = pubSub.addExtension(new Publish());
|
||||||
pubSubItemsWrapper.setNode(node);
|
publish.setNode(node);
|
||||||
final var item = pubSubItemsWrapper.addExtension(new PubSub.Item());
|
final var item = publish.addExtension(new PubSub.Item());
|
||||||
item.setId(itemId);
|
item.setId(itemId);
|
||||||
item.addExtension(itemPayload);
|
item.addExtension(itemPayload);
|
||||||
final ListenableFuture<Void> iqFuture =
|
final ListenableFuture<Void> iqFuture =
|
||||||
|
@ -248,8 +252,45 @@ public class PubSubManager extends AbstractManager {
|
||||||
|
|
||||||
private ListenableFuture<Void> reconfigureNode(
|
private ListenableFuture<Void> reconfigureNode(
|
||||||
final Jid address, final String node, final NodeConfiguration nodeConfiguration) {
|
final Jid address, final String node, final NodeConfiguration nodeConfiguration) {
|
||||||
|
final Iq iq = new Iq(Iq.Type.GET);
|
||||||
|
iq.setTo(address);
|
||||||
|
final var pubSub = iq.addExtension(new PubSubOwner());
|
||||||
|
final var configure = pubSub.addExtension(new Configure());
|
||||||
|
configure.setNode(node);
|
||||||
|
return Futures.transformAsync(
|
||||||
|
connection.sendIqPacket(iq),
|
||||||
|
result -> {
|
||||||
|
final var pubSubOwnerResult = result.getExtension(PubSubOwner.class);
|
||||||
|
final Configure configureResult =
|
||||||
|
pubSubOwnerResult == null
|
||||||
|
? null
|
||||||
|
: pubSubOwnerResult.getExtension(Configure.class);
|
||||||
|
if (configureResult == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No configuration found in configuration request result");
|
||||||
|
}
|
||||||
|
final var data = configureResult.getData();
|
||||||
|
return setNodeConfiguration(address, node, data.submit(nodeConfiguration));
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
|
}
|
||||||
|
|
||||||
return Futures.immediateVoidFuture();
|
private ListenableFuture<Void> setNodeConfiguration(
|
||||||
|
final Jid address, final String node, final Data data) {
|
||||||
|
LOGGER.info("Trying to set node configuration to {}", data.toString());
|
||||||
|
final Iq iq = new Iq(Iq.Type.SET);
|
||||||
|
iq.setTo(address);
|
||||||
|
final var pubSub = iq.addExtension(new PubSubOwner());
|
||||||
|
final var configure = pubSub.addExtension(new Configure());
|
||||||
|
configure.setNode(node);
|
||||||
|
configure.addExtension(data);
|
||||||
|
return Futures.transform(
|
||||||
|
connection.sendIqPacket(iq),
|
||||||
|
result -> {
|
||||||
|
LOGGER.info("Modified node configuration {} on {}", node, address);
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class PubSubExceptionTransformer<V>
|
private static class PubSubExceptionTransformer<V>
|
||||||
|
|
|
@ -11,6 +11,8 @@ import java.util.Map;
|
||||||
public class Data extends Extension {
|
public class Data extends Extension {
|
||||||
|
|
||||||
private static final String FORM_TYPE = "FORM_TYPE";
|
private static final String FORM_TYPE = "FORM_TYPE";
|
||||||
|
private static final String FIELD_TYPE_HIDDEN = "hidden";
|
||||||
|
private static final String FORM_TYPE_SUBMIT = "submit";
|
||||||
|
|
||||||
public Data() {
|
public Data() {
|
||||||
super(Data.class);
|
super(Data.class);
|
||||||
|
@ -28,35 +30,81 @@ public class Data extends Extension {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addField(final String name, final Object value) {
|
private void addField(final String name, final Object value) {
|
||||||
|
addField(name, value, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addField(final String name, final Object value, final String type) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
throw new IllegalArgumentException("Null values are not supported on data fields");
|
throw new IllegalArgumentException("Null values are not supported on data fields");
|
||||||
}
|
}
|
||||||
final var field = this.addExtension(new Field());
|
final var field = this.addExtension(new Field());
|
||||||
field.setFieldName(name);
|
field.setFieldName(name);
|
||||||
final var valueExtension = field.addExtension(new Value());
|
if (type != null) {
|
||||||
if (value instanceof String) {
|
field.setType(type);
|
||||||
valueExtension.setContent((String) value);
|
}
|
||||||
} else if (value instanceof Integer) {
|
if (value instanceof Collection) {
|
||||||
valueExtension.setContent(String.valueOf(value));
|
for (final Object subValue : (Collection<?>) value) {
|
||||||
} else if (value instanceof Boolean) {
|
if (subValue instanceof String) {
|
||||||
valueExtension.setContent(Boolean.TRUE.equals(value) ? "true" : "false");
|
final var valueExtension = field.addExtension(new Value());
|
||||||
|
valueExtension.setContent((String) subValue);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"%s is not a supported field value",
|
||||||
|
subValue.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException(
|
final var valueExtension = field.addExtension(new Value());
|
||||||
String.format(
|
if (value instanceof String) {
|
||||||
"%s is not a supported field value", value.getClass().getSimpleName()));
|
valueExtension.setContent((String) value);
|
||||||
|
} else if (value instanceof Integer) {
|
||||||
|
valueExtension.setContent(String.valueOf(value));
|
||||||
|
} else if (value instanceof Boolean) {
|
||||||
|
valueExtension.setContent(Boolean.TRUE.equals(value) ? "true" : "false");
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format(
|
||||||
|
"%s is not a supported field value",
|
||||||
|
value.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setFormType(final String formType) {
|
private void setFormType(final String formType) {
|
||||||
this.addField(FORM_TYPE, formType);
|
this.addField(FORM_TYPE, formType, FIELD_TYPE_HIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Data of(final String formType, final Map<String, Object> values) {
|
public static Data of(final String formType, final Map<String, Object> values) {
|
||||||
final var data = new Data();
|
final var data = new Data();
|
||||||
|
data.setType(FORM_TYPE_SUBMIT);
|
||||||
data.setFormType(formType);
|
data.setFormType(formType);
|
||||||
for (final Map.Entry<String, Object> entry : values.entrySet()) {
|
for (final Map.Entry<String, Object> entry : values.entrySet()) {
|
||||||
data.addField(entry.getKey(), entry.getValue());
|
data.addField(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Data submit(final Map<String, Object> values) {
|
||||||
|
final String formType = this.getFormType();
|
||||||
|
final var submit = new Data();
|
||||||
|
submit.setType(FORM_TYPE_SUBMIT);
|
||||||
|
if (formType != null) {
|
||||||
|
submit.setFormType(formType);
|
||||||
|
}
|
||||||
|
for (final Field existingField : this.getFields()) {
|
||||||
|
final var fieldName = existingField.getFieldName();
|
||||||
|
final Object submittedValue = values.get(fieldName);
|
||||||
|
if (submittedValue != null) {
|
||||||
|
submit.addField(fieldName, submittedValue);
|
||||||
|
} else {
|
||||||
|
submit.addField(fieldName, existingField.getValues());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return submit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setType(final String type) {
|
||||||
|
this.setAttribute("type", type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,4 +23,8 @@ public class Field extends Extension {
|
||||||
public void setFieldName(String name) {
|
public void setFieldName(String name) {
|
||||||
this.setAttribute("var", name);
|
this.setAttribute("var", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.setAttribute("type", type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package im.conversations.android.xmpp.model.pubsub;
|
||||||
|
|
||||||
|
import im.conversations.android.annotation.XmlElement;
|
||||||
|
import im.conversations.android.xmpp.model.Extension;
|
||||||
|
|
||||||
|
@XmlElement
|
||||||
|
public class Publish extends Extension {
|
||||||
|
|
||||||
|
public Publish() {
|
||||||
|
super(Publish.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNode(String node) {
|
||||||
|
this.setAttribute("node", node);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package im.conversations.android.xmpp.model.pubsub.owner;
|
||||||
|
|
||||||
|
import im.conversations.android.annotation.XmlElement;
|
||||||
|
import im.conversations.android.xmpp.model.Extension;
|
||||||
|
import im.conversations.android.xmpp.model.data.Data;
|
||||||
|
|
||||||
|
@XmlElement
|
||||||
|
public class Configure extends Extension {
|
||||||
|
|
||||||
|
public Configure() {
|
||||||
|
super(Configure.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNode(final String node) {
|
||||||
|
this.setAttribute("node", node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Data getData() {
|
||||||
|
return this.getExtension(Data.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package im.conversations.android.xmpp.model.pubsub.owner;
|
||||||
|
|
||||||
|
import im.conversations.android.annotation.XmlElement;
|
||||||
|
import im.conversations.android.xmpp.model.Extension;
|
||||||
|
|
||||||
|
@XmlElement(name = "pubsub")
|
||||||
|
public class PubSubOwner extends Extension {
|
||||||
|
|
||||||
|
public PubSubOwner() {
|
||||||
|
super(PubSubOwner.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
@XmlPackage(namespace = Namespace.PUB_SUB_OWNER)
|
||||||
|
package im.conversations.android.xmpp.model.pubsub.owner;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
|
import im.conversations.android.annotation.XmlPackage;
|
Loading…
Reference in a new issue