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-repl',
'smack-openpgp', 'smack-openpgp',
'smack-websocket', 'smack-websocket',
'smack-websocket-okhttp',
'smack-xmlparser', 'smack-xmlparser',
'smack-xmlparser-stax', 'smack-xmlparser-stax',
'smack-xmlparser-xpp3' '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 * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations.okhttp; package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset; import java.nio.charset.Charset;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations.okhttp; package org.jivesoftware.smack.websocket.okhttp;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; 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.packet.TopLevelStreamElement;
import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.PacketParserUtils;
import org.jivesoftware.smack.websocket.WebsocketException; 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.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.WebsocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
@ -54,8 +53,7 @@ public final class OkHttpWebsocket extends AbstractWebsocket {
private WebsocketConnectionPhase phase; private WebsocketConnectionPhase phase;
private WebsocketRemoteConnectionEndpoint connectedEndpoint; private WebsocketRemoteConnectionEndpoint connectedEndpoint;
public OkHttpWebsocket(ModularXmppClientToServerConnectionInternal connectionInternal, public OkHttpWebsocket(ModularXmppClientToServerConnectionInternal connectionInternal) {
DiscoveredWebsocketEndpoints discoveredWebsocketEndpoints) {
this.connectionInternal = connectionInternal; this.connectionInternal = connectionInternal;
if (okHttpClient == null) { 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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 = """\ description = """\
Smack for standard XMPP connections over Websockets.""" Smack for XMPP connections over WebSocket (RFC 7395)."""
dependencies { dependencies {
compile project(':smack-core') api project(':smack-core')
testFixturesApi(testFixtures(project(":smack-core"))) testFixturesApi(testFixtures(project(':smack-core')))
implementation("com.squareup.okhttp3:okhttp:4.6.0")
} }

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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,15 +16,13 @@
*/ */
package org.jivesoftware.smack.websocket; package org.jivesoftware.smack.websocket;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal; import org.jivesoftware.smack.c2s.internal.ModularXmppClientToServerConnectionInternal;
import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.EstablishingWebsocketConnectionState; import org.jivesoftware.smack.websocket.XmppWebsocketTransportModule.EstablishingWebsocketConnectionState;
import org.jivesoftware.smack.websocket.implementations.AbstractWebsocket; import org.jivesoftware.smack.websocket.impl.AbstractWebsocket;
import org.jivesoftware.smack.websocket.implementations.WebsocketImplProvider; import org.jivesoftware.smack.websocket.impl.WebsocketFactoryService;
import org.jivesoftware.smack.websocket.implementations.okhttp.OkHttpWebsocket;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint; import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpoint;
public final class WebsocketConnectionAttemptState { public final class WebsocketConnectionAttemptState {
@ -56,15 +54,7 @@ public final class WebsocketConnectionAttemptState {
} }
List<Throwable> connectionFailureList = new ArrayList<>(); List<Throwable> connectionFailureList = new ArrayList<>();
AbstractWebsocket websocket; AbstractWebsocket websocket = WebsocketFactoryService.createWebsocket(connectionInternal);
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);
}
// Keep iterating over available endpoints until a connection is establised or all endpoints are tried to create a connection with. // Keep iterating over available endpoints until a connection is establised or all endpoints are tried to create a connection with.
for (WebsocketRemoteConnectionEndpoint endpoint : endpoints) { 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.XmppWebsocketTransportModule.XmppWebsocketTransport.DiscoveredWebsocketEndpoints;
import org.jivesoftware.smack.websocket.elements.WebsocketCloseElement; import org.jivesoftware.smack.websocket.elements.WebsocketCloseElement;
import org.jivesoftware.smack.websocket.elements.WebsocketOpenElement; 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.WebsocketRemoteConnectionEndpoint;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup; import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup;
import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup.Result; import org.jivesoftware.smack.websocket.rce.WebsocketRemoteConnectionEndpointLookup.Result;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.websocket.implementations; package org.jivesoftware.smack.websocket.impl;
import javax.net.ssl.SSLSession; 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. * 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; 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());
}
}