Introduce smack-websocket-okhttp

This uses Java's Service Provider Interface (SPI) to abstract
different WebSocket implementations.

SMACK-835
This commit is contained in:
Florian Schmaus 2020-09-01 21:12:30 +02:00
parent 525f27abf1
commit 6533cb7ed1
19 changed files with 189 additions and 126 deletions

View File

@ -31,6 +31,7 @@ include 'smack-core',
'smack-repl',
'smack-openpgp',
'smack-websocket',
'smack-websocket-okhttp',
'smack-xmlparser',
'smack-xmlparser-stax',
'smack-xmlparser-xpp3'

View File

@ -0,0 +1,10 @@
description = """\
Smack for XMPP connections over WebSocket (RFC 7395) using OkHttp."""
dependencies {
api project(':smack-websocket')
testFixturesApi(testFixtures(project(':smack-websocket')))
implementation("com.squareup.okhttp3:okhttp:4.6.0")
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.websocket.implementations.okhttp;
package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException;
import java.nio.charset.Charset;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.websocket.implementations.okhttp;
package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException;
import java.util.logging.Level;
@ -28,9 +28,8 @@ import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionIn
import org.jivesoftware.smack.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.websocket.WebsocketException;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints;
import org.jivesoftware.smack.websocket.elements.WebsocketOpenElement;
import org.jivesoftware.smack.websocket.implementations.AbstractWebsocket;
import org.jivesoftware.smack.websocket.impl.AbstractWebsocket;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.xml.XmlPullParserException;
@ -54,8 +53,7 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
private WebsocketConnectionPhase phase;
private WebsocketRemoteConnectionEndpoint connectedEndpoint;
public OkHttpWebsocket(ModularXmppClientToServerConnectionInternal connectionInternal,
DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints) {
public OkHttpWebsocket(ModularXmppClientToServerConnectionInternal connectionInternal) {
this.connectionInternal = connectionInternal;
if (okHttpClient == null) {

View File

@ -0,0 +1,30 @@
/**
*
* Copyright 2020 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.jivesoftware.smack.websocket.okhttp;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.impl.AbstractWebsocket;
import org.jivesoftware.smack.websocket.impl.WebsocketFactory;
public class OkHttpWebsocketFactory implements WebsocketFactory {
@Override
public AbstractWebsocket create(ModularXmppClientToServerConnectionInternal connectionInternal) {
return new OkHttpWebsocket(connectionInternal);
}
}

View File

@ -14,4 +14,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.websocket.implementations.okhttp;
package org.jivesoftware.smack.websocket.okhttp;

View File

@ -0,0 +1 @@
org.jivesoftware.smack.websocket.okhttp.OkHttpWebsocketFactory

View File

@ -0,0 +1,30 @@
/**
*
* Copyright 2020 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.jivesoftware.smack.websocket.okhttp;
import org.jivesoftware.smack.websocket.test.WebsocketFactoryServiceTestUtil;
import org.junit.jupiter.api.Test;
public class OkHttpWebsocketFactoryServiceTest {
@Test
public void createWebsocketTest() {
WebsocketFactoryServiceTestUtil.createWebsocketTest(OkHttpWebsocket.class);
}
}

View File

@ -1,10 +1,8 @@
description = """\
Smack for standard XMPP connections over Websockets."""
Smack for XMPP connections over WebSocket (RFC 7395)."""
dependencies {
compile project(':smack-core')
api project(':smack-core')
testFixturesApi(testFixtures(project(":smack-core")))
implementation("com.squareup.okhttp3:okhttp:4.6.0")
testFixturesApi(testFixtures(project(':smack-core')))
}

View File

@ -1,6 +1,6 @@
/**
*
* Copyright 2020 Aditya Borikar
* Copyright 2020 Aditya Borikar, Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,15 +16,13 @@
*/
package org.jivesoftware.smack.websocket;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.EstablishingWebsocketConnectionState;
import org.jivesoftware.smack.websocket.implementations.AbstractWebsocket;
import org.jivesoftware.smack.websocket.implementations.WebsocketImplProvider;
import org.jivesoftware.smack.websocket.implementations.okhttp.OkHttpWebsocket;
import org.jivesoftware.smack.websocket.impl.AbstractWebsocket;
import org.jivesoftware.smack.websocket.impl.WebsocketFactoryService;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
public final class WebsocketConnectionAttemptState {
@ -56,15 +54,7 @@ public final class WebsocketConnectionAttemptState {
}
List<Throwable> connectionFailureList = new ArrayList<>();
AbstractWebsocket websocket;
try {
// Obtain desired websocket implementation by using WebsocketImplProvider
websocket = WebsocketImplProvider.getWebsocketImpl(OkHttpWebsocket.class, connectionInternal, discoveredEndpoints);
} catch (NoSuchMethodException | SecurityException | InstantiationException |
IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) {
throw new WebsocketException(exception);
}
AbstractWebsocket websocket = WebsocketFactoryService.createWebsocket(connectionInternal);
// Keep iterating over available endpoints until a connection is establised or all endpoints are tried to create a connection with.
for (WebsocketRemoteConnectionEndpoint endpoint : endpoints) {

View File

@ -53,7 +53,7 @@ import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints;
import org.jivesoftware.smack.websocket.elements.WebsocketCloseElement;
import org.jivesoftware.smack.websocket.elements.WebsocketOpenElement;
import org.jivesoftware.smack.websocket.implementations.AbstractWebsocket;
import org.jivesoftware.smack.websocket.impl.AbstractWebsocket;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup.Result;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.websocket.implementations;
package org.jivesoftware.smack.websocket.impl;
import javax.net.ssl.SSLSession;

View File

@ -0,0 +1,25 @@
/**
*
* Copyright 2020 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.jivesoftware.smack.websocket.impl;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
public interface WebsocketFactory {
AbstractWebsocket create(ModularXmppClientToServerConnectionInternal connectionInternal);
}

View File

@ -0,0 +1,40 @@
/**
*
* Copyright 2020 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.jivesoftware.smack.websocket.impl;
import java.util.Iterator;
import java.util.ServiceLoader;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
public final class WebsocketFactoryService {
private static final ServiceLoader<WebsocketFactory> SERVICE_LOADER = ServiceLoader.load(WebsocketFactory.class);
public static AbstractWebsocket createWebsocket(ModularXmppClientToServerConnectionInternal connectionInternal) {
assert connectionInternal != null;
Iterator<WebsocketFactory> websocketFactories = SERVICE_LOADER.iterator();
if (!websocketFactories.hasNext()) {
throw new IllegalStateException("No smack websocket service configured");
}
WebsocketFactory websocketFactory = websocketFactories.next();
return websocketFactory.create(connectionInternal);
}
}

View File

@ -17,4 +17,4 @@
/**
* This package contains websocket implementations to be plugged inside websocket transport.
*/
package org.jivesoftware.smack.websocket.implementations;
package org.jivesoftware.smack.websocket.impl;

View File

@ -1,35 +0,0 @@
/**
*
* Copyright 2020 Aditya Borikar.
*
* 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.jivesoftware.smack.websocket.implementations;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints;
public final class WebsocketImplProvider {
public static AbstractWebsocket getWebsocketImpl(Class<? extends AbstractWebsocket> websocketImpl, ModularXmppClientToServerConnectionInternal connectionInternal, DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Objects.requireNonNull(connectionInternal, "ConnectionInternal cannot be null");
// Creates an instance of the constructor for the desired websocket implementation.
Constructor<? extends AbstractWebsocket> constructor = websocketImpl.getConstructor(ModularXmppClientToServerConnectionInternal.class, DiscoveredWebsocketEndpoints.class);
return (AbstractWebsocket) constructor.newInstance(connectionInternal, discoveredWebsocketEndpoints);
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smack.websocket.implementations;
package org.jivesoftware.smack.websocket.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;

View File

@ -1,61 +0,0 @@
/**
*
* Copyright 2020 Aditya Borikar.
*
* 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.jivesoftware.smack.websocket.implementations;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints;
import org.jivesoftware.smack.websocket.implementations.okhttp.OkHttpWebsocket;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup.Result;
import org.junit.jupiter.api.Test;
public class ProviderTest {
@Test
public void providerTest() {
assertThrows(IllegalArgumentException.class, () -> WebsocketImplProvider.getWebsocketImpl(OkHttpWebsocket.class, null, null));
}
@Test
public void getImplTest() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, URISyntaxException {
WebsocketRemoteConnectionEndpoint endpoint = new WebsocketRemoteConnectionEndpoint("wss://localhost.org:7443/ws/");
List<WebsocketRemoteConnectionEndpoint> discoveredRemoteConnectionEndpoints = new ArrayList<>();
discoveredRemoteConnectionEndpoints.add(endpoint);
Result result = new Result(discoveredRemoteConnectionEndpoints, null);
DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints = mock(DiscoveredWebsocketEndpoints.class);
when(discoveredWebsocketEndpoints.getResult()).thenReturn(result);
ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class);
assertNotNull(WebsocketImplProvider.getWebsocketImpl(OkHttpWebsocket.class, connectionInternal, discoveredWebsocketEndpoints));
}
}

View File

@ -0,0 +1,36 @@
/**
*
* Copyright 2020 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.jivesoftware.smack.websocket.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.impl.AbstractWebsocket;
import org.jivesoftware.smack.websocket.impl.WebsocketFactoryService;
public class WebsocketFactoryServiceTestUtil {
public static void createWebsocketTest(Class<? extends AbstractWebsocket> expected) {
ModularXmppClientToServerConnectionInternal connectionInternal = mock(ModularXmppClientToServerConnectionInternal.class);
AbstractWebsocket websocket = WebsocketFactoryService.createWebsocket(connectionInternal);
assertEquals(expected, websocket.getClass());
}
}