/** * * Copyright 2018-2021 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.igniterealtime.smack.inttest; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.jivesoftware.smack.AbstractXMPPConnection; import org.jivesoftware.smack.ConnectionConfiguration; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnection; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionConfiguration; import org.jivesoftware.smack.c2s.ModularXmppClientToServerConnectionModuleDescriptor; import org.jivesoftware.smack.util.Consumer; import org.jivesoftware.smack.websocket.XmppWebSocketTransportModuleDescriptor; import org.jivesoftware.smack.websocket.impl.WebSocketFactory; public final class XmppConnectionDescriptor< C extends AbstractXMPPConnection, CC extends ConnectionConfiguration, CCB extends ConnectionConfiguration.Builder > { private final Class connectionClass; private final Class connectionConfigurationClass; private final Constructor connectionConstructor; private final Method builderMethod; private final Consumer extraBuilder; private final String nickname; private XmppConnectionDescriptor(Builder builder) throws NoSuchMethodException, SecurityException { connectionClass = builder.connectionClass; connectionConfigurationClass = builder.connectionConfigurationClass; extraBuilder = builder.extraBuilder; nickname = builder.nickname; connectionConstructor = getConstructor(connectionClass, connectionConfigurationClass); builderMethod = getBuilderMethod(connectionConfigurationClass); } public C construct(Configuration sinttestConfiguration) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { return construct(sinttestConfiguration, Collections.emptyList()); } public C construct(Configuration sinttestConfiguration, ConnectionConfigurationBuilderApplier... customConnectionConfigurationAppliers) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { List customConnectionConfigurationAppliersList = new ArrayList( Arrays.asList(customConnectionConfigurationAppliers)); return construct(sinttestConfiguration, customConnectionConfigurationAppliersList); } public C construct(Configuration sinttestConfiguration, Collection customConnectionConfigurationAppliers) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { CCB connectionConfigurationBuilder = getNewBuilder(); if (extraBuilder != null) { extraBuilder.accept(connectionConfigurationBuilder); } for (ConnectionConfigurationBuilderApplier customConnectionConfigurationApplier : customConnectionConfigurationAppliers) { customConnectionConfigurationApplier.applyConfigurationTo(connectionConfigurationBuilder); } sinttestConfiguration.configurationApplier.applyConfigurationTo(connectionConfigurationBuilder); ConnectionConfiguration connectionConfiguration = connectionConfigurationBuilder.build(); CC concreteConnectionConfiguration = connectionConfigurationClass.cast(connectionConfiguration); C connection = connectionConstructor.newInstance(concreteConnectionConfiguration); connection.setReplyTimeout(sinttestConfiguration.replyTimeout); return connection; } @SuppressWarnings("unchecked") public CCB getNewBuilder() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { return (CCB) builderMethod.invoke(null); } public Class getConnectionClass() { return connectionClass; } public String getNickname() { return nickname; } private static Constructor getConstructor(Class connectionClass, Class connectionConfigurationClass) throws NoSuchMethodException, SecurityException { return connectionClass.getConstructor(connectionConfigurationClass); } private static Method getBuilderMethod(Class connectionConfigurationClass) throws NoSuchMethodException, SecurityException { Method builderMethod = connectionConfigurationClass.getMethod("builder"); if (!Modifier.isStatic(builderMethod.getModifiers())) { throw new IllegalArgumentException(); } Class returnType = builderMethod.getReturnType(); if (!ConnectionConfiguration.Builder.class.isAssignableFrom(returnType)) { throw new IllegalArgumentException(); } return builderMethod; } public static > Builder buildWith(Class connectionClass, Class connectionConfigurationClass) { return buildWith(connectionClass, connectionConfigurationClass, null); } public static > Builder buildWith(Class connectionClass, Class connectionConfigurationClass, Class connectionConfigurationBuilderClass) { return new Builder<>(connectionClass, connectionConfigurationClass, connectionConfigurationBuilderClass); } public static XmppConnectionDescriptor buildWebsocketDescriptor( String nickname, Class factoryClass) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { WebSocketFactory factory; try { Field instanceField = factoryClass.getField("INSTANCE"); factory = (WebSocketFactory) instanceField.get(null); } catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { factory = factoryClass.getConstructor().newInstance(); } WebSocketFactory finalFactory = factory; return XmppConnectionDescriptor.buildWith(ModularXmppClientToServerConnection.class, ModularXmppClientToServerConnectionConfiguration.class, ModularXmppClientToServerConnectionConfiguration.Builder.class) .withNickname("modular-websocket-java11") .applyExtraConfguration(cb -> { cb.removeAllModules(); ModularXmppClientToServerConnectionModuleDescriptor webSocketModuleDescriptor = XmppWebSocketTransportModuleDescriptor.getBuilder(cb) .setWebSocketFactory(finalFactory) .build(); cb.addModule(webSocketModuleDescriptor); }) .build(); } public static final class Builder> { private final Class connectionClass; private final Class connectionConfigurationClass; private Consumer extraBuilder; private String nickname; // The connectionConfigurationBuilderClass merely exists for type-checking purposes. @SuppressWarnings("UnusedVariable") private Builder(Class connectionClass, Class connectionConfigurationClass, Class connectionConfigurationBuilderClass) { this.connectionClass = connectionClass; this.connectionConfigurationClass = connectionConfigurationClass; nickname = connectionClass.getSimpleName(); } public Builder applyExtraConfguration(Consumer extraBuilder) { this.extraBuilder = extraBuilder; return this; } public Builder withNickname(String nickname) { this.nickname = nickname; return this; } public XmppConnectionDescriptor build() throws NoSuchMethodException, SecurityException { return new XmppConnectionDescriptor<>(this); } } }