001package com.pusher.client.util; 002 003import java.net.URI; 004import java.net.URISyntaxException; 005import java.util.concurrent.ExecutorService; 006import java.util.concurrent.Executors; 007import java.util.concurrent.ScheduledExecutorService; 008import java.util.concurrent.ThreadFactory; 009 010import javax.net.ssl.SSLException; 011 012import org.java_websocket.client.WebSocketClient; 013 014import com.pusher.client.Authorizer; 015import com.pusher.client.PusherOptions; 016import com.pusher.client.channel.impl.ChannelImpl; 017import com.pusher.client.channel.impl.ChannelManager; 018import com.pusher.client.channel.impl.PresenceChannelImpl; 019import com.pusher.client.channel.impl.PrivateChannelImpl; 020import com.pusher.client.connection.impl.InternalConnection; 021import com.pusher.client.connection.websocket.WebSocketClientWrapper; 022import com.pusher.client.connection.websocket.WebSocketConnection; 023import com.pusher.client.connection.websocket.WebSocketListener; 024 025/** 026 * This is a lightweight way of doing dependency injection and enabling classes 027 * to be unit tested in isolation. No class in this library instantiates another 028 * class directly, otherwise they would be tightly coupled. Instead, they all 029 * call the factory methods in this class when they want to create instances of 030 * another class. 031 * 032 * An instance of Factory is provided on construction to each class which may 033 * require it, the initial factory is instantiated in the Pusher constructor, 034 * the only constructor which a library consumer should need to call directly. 035 * 036 * Conventions: 037 * 038 * - any method that starts with "new", such as 039 * {@link #newPublicChannel(String)} creates a new instance of that class every 040 * time it is called. 041 * 042 * - any method that starts with "get", such as {@link #getEventQueue()} returns 043 * a singleton. These are lazily constructed and their access methods should be 044 * synchronized for this reason. 045 */ 046public class Factory { 047 048 private InternalConnection connection; 049 private ChannelManager channelManager; 050 private ExecutorService eventQueue; 051 private ScheduledExecutorService timers; 052 053 public synchronized InternalConnection getConnection(final String apiKey, final PusherOptions options) { 054 if (connection == null) { 055 try { 056 connection = new WebSocketConnection(options.buildUrl(apiKey), options.getActivityTimeout(), 057 options.getPongTimeout(), this); 058 } 059 catch (final URISyntaxException e) { 060 throw new IllegalArgumentException("Failed to initialise connection", e); 061 } 062 } 063 return connection; 064 } 065 066 public WebSocketClient newWebSocketClientWrapper(final URI uri, final WebSocketListener proxy) throws SSLException { 067 return new WebSocketClientWrapper(uri, proxy); 068 } 069 070 public synchronized ExecutorService getEventQueue() { 071 if (eventQueue == null) { 072 eventQueue = Executors.newSingleThreadExecutor(new DaemonThreadFactory("eventQueue")); 073 } 074 return eventQueue; 075 } 076 077 public synchronized ScheduledExecutorService getTimers() { 078 if (timers == null) { 079 timers = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory("timers")); 080 } 081 return timers; 082 } 083 084 public ChannelImpl newPublicChannel(final String channelName) { 085 return new ChannelImpl(channelName, this); 086 } 087 088 public PrivateChannelImpl newPrivateChannel(final InternalConnection connection, final String channelName, 089 final Authorizer authorizer) { 090 return new PrivateChannelImpl(connection, channelName, authorizer, this); 091 } 092 093 public PresenceChannelImpl newPresenceChannel(final InternalConnection connection, final String channelName, 094 final Authorizer authorizer) { 095 return new PresenceChannelImpl(connection, channelName, authorizer, this); 096 } 097 098 public synchronized ChannelManager getChannelManager() { 099 if (channelManager == null) { 100 channelManager = new ChannelManager(this); 101 } 102 return channelManager; 103 } 104 105 public synchronized void shutdownThreads() { 106 if (eventQueue != null) { 107 eventQueue.shutdown(); 108 eventQueue = null; 109 } 110 if (timers != null) { 111 timers.shutdown(); 112 timers = null; 113 } 114 } 115 116 private static class DaemonThreadFactory implements ThreadFactory { 117 private final String name; 118 119 public DaemonThreadFactory(final String name) { 120 this.name = name; 121 } 122 123 @Override 124 public Thread newThread(final Runnable r) { 125 final Thread t = new Thread(r); 126 t.setDaemon(true); 127 t.setName("pusher-java-client " + name); 128 return t; 129 } 130 } 131}