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