[96] | 1 | package com.framsticks.hosting; |
---|
| 2 | |
---|
[100] | 3 | import org.apache.logging.log4j.Level; |
---|
| 4 | import org.apache.logging.log4j.Logger; |
---|
| 5 | import org.apache.logging.log4j.LogManager; |
---|
[96] | 6 | |
---|
[101] | 7 | import com.framsticks.params.ParamFlags; |
---|
[96] | 8 | import com.framsticks.params.annotations.AutoAppendAnnotation; |
---|
| 9 | import com.framsticks.params.annotations.FramsClassAnnotation; |
---|
| 10 | import com.framsticks.params.annotations.ParamAnnotation; |
---|
[105] | 11 | import com.framsticks.structure.Tree; |
---|
[96] | 12 | import com.framsticks.util.FramsticksException; |
---|
| 13 | import com.framsticks.util.dispatching.AbstractJoinable; |
---|
| 14 | import com.framsticks.util.dispatching.Dispatching; |
---|
| 15 | import com.framsticks.util.dispatching.Joinable; |
---|
| 16 | import com.framsticks.util.dispatching.JoinableCollection; |
---|
| 17 | import com.framsticks.util.dispatching.JoinableParent; |
---|
| 18 | import com.framsticks.util.dispatching.JoinableState; |
---|
| 19 | import com.framsticks.util.dispatching.RunAt; |
---|
[97] | 20 | import com.framsticks.util.dispatching.ThrowExceptionHandler; |
---|
[96] | 21 | |
---|
| 22 | import java.io.IOException; |
---|
| 23 | import java.net.InetSocketAddress; |
---|
| 24 | import java.net.ServerSocket; |
---|
| 25 | import java.net.Socket; |
---|
[101] | 26 | import java.util.TimerTask; |
---|
| 27 | |
---|
[96] | 28 | import com.framsticks.util.dispatching.Thread; |
---|
| 29 | |
---|
| 30 | @FramsClassAnnotation |
---|
| 31 | public class Server extends AbstractJoinable implements JoinableParent { |
---|
| 32 | |
---|
[100] | 33 | private final static Logger log = LogManager.getLogger(Server.class); |
---|
[96] | 34 | |
---|
| 35 | protected int port; |
---|
| 36 | |
---|
| 37 | protected ServerSocket acceptSocket; |
---|
[97] | 38 | protected Tree hosted; |
---|
[102] | 39 | protected final JoinableCollection<ClientAtServer> clients = new JoinableCollection<ClientAtServer>(JoinableCollection.FinishPolicy.Never); |
---|
[96] | 40 | |
---|
| 41 | public static class Accept { |
---|
| 42 | }; |
---|
| 43 | |
---|
| 44 | protected Thread<Accept> acceptThread = new Thread<>(); |
---|
| 45 | |
---|
| 46 | /** |
---|
| 47 | * |
---|
| 48 | */ |
---|
| 49 | public Server() { |
---|
| 50 | log.debug("created server"); |
---|
| 51 | port = 9009; |
---|
| 52 | } |
---|
| 53 | |
---|
| 54 | /** |
---|
| 55 | * @return the port |
---|
| 56 | */ |
---|
| 57 | @ParamAnnotation |
---|
| 58 | public int getPort() { |
---|
| 59 | return port; |
---|
| 60 | } |
---|
| 61 | |
---|
| 62 | /** |
---|
| 63 | * @param port the port to set |
---|
| 64 | */ |
---|
[101] | 65 | @ParamAnnotation(flags = ParamFlags.USERREADONLY) |
---|
[96] | 66 | public void setPort(int port) { |
---|
| 67 | this.port = port; |
---|
| 68 | } |
---|
| 69 | |
---|
| 70 | |
---|
| 71 | /** |
---|
| 72 | * @return the hosted |
---|
| 73 | */ |
---|
[97] | 74 | public Tree getHosted() { |
---|
[96] | 75 | return hosted; |
---|
| 76 | } |
---|
| 77 | |
---|
| 78 | @AutoAppendAnnotation |
---|
[97] | 79 | public void setHosted(Tree hosted) { |
---|
[96] | 80 | if (this.hosted != null) { |
---|
[97] | 81 | throw new FramsticksException().msg("hosted tree is already set").arg("current", this.hosted); |
---|
[96] | 82 | } |
---|
| 83 | this.hosted = hosted; |
---|
| 84 | acceptThread.setName(hosted.getName() + " acceptor"); |
---|
| 85 | clients.setObservableName(hosted.getName() + " clients"); |
---|
| 86 | } |
---|
| 87 | |
---|
| 88 | @Override |
---|
| 89 | public void childChangedState(Joinable joinable, JoinableState state) { |
---|
| 90 | proceedToState(state); |
---|
| 91 | } |
---|
| 92 | |
---|
| 93 | @Override |
---|
| 94 | @ParamAnnotation |
---|
| 95 | public String getName() { |
---|
| 96 | return hosted != null ? hosted.getName() : "server"; |
---|
| 97 | } |
---|
| 98 | |
---|
| 99 | protected void acceptNext() { |
---|
| 100 | if (!isRunning()) { |
---|
| 101 | log.debug("server is not in running state, aborting accepting"); |
---|
| 102 | return; |
---|
| 103 | } |
---|
[98] | 104 | acceptThread.dispatch(new RunAt<Accept>(hosted) { |
---|
[96] | 105 | @Override |
---|
[97] | 106 | protected void runAt() { |
---|
[96] | 107 | try { |
---|
| 108 | log.debug("accepting"); |
---|
| 109 | final Socket socket = acceptSocket.accept(); |
---|
| 110 | assert socket != null; |
---|
[100] | 111 | log.debug("accepted socket: {}", socket.getInetAddress().getHostAddress()); |
---|
[97] | 112 | hosted.dispatch(new RunAt<Tree>(this) { |
---|
[96] | 113 | @Override |
---|
[97] | 114 | protected void runAt() { |
---|
| 115 | ClientAtServer client = new ClientAtServer(Server.this, socket); |
---|
[96] | 116 | clients.add(client); |
---|
[100] | 117 | log.info("client connected: {}", client); |
---|
[96] | 118 | } |
---|
| 119 | }); |
---|
| 120 | } catch (IOException e) { |
---|
[100] | 121 | log.log((isRunning() ? Level.ERROR : Level.DEBUG), "failed to accept socket: {}", e); |
---|
[96] | 122 | } |
---|
| 123 | acceptNext(); |
---|
| 124 | } |
---|
| 125 | }); |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | protected void tryBind(int when) { |
---|
[101] | 129 | Dispatching.getTimer().schedule(new TimerTask() { |
---|
| 130 | |
---|
[96] | 131 | @Override |
---|
[101] | 132 | public void run() { |
---|
| 133 | acceptThread.dispatch(new RunAt<Accept>(ThrowExceptionHandler.getInstance()) { |
---|
| 134 | @Override |
---|
| 135 | protected void runAt() { |
---|
| 136 | try { |
---|
| 137 | acceptSocket.bind(new InetSocketAddress(port)); |
---|
| 138 | log.debug("started accepting on port {}", port); |
---|
| 139 | acceptNext(); |
---|
| 140 | return; |
---|
| 141 | } catch (IOException e) { |
---|
| 142 | log.warn("failed to accept on port {} (repeating): ", port, e); |
---|
| 143 | } |
---|
| 144 | tryBind(1000); |
---|
| 145 | } |
---|
| 146 | }); |
---|
[96] | 147 | } |
---|
[101] | 148 | |
---|
| 149 | }, when); |
---|
[96] | 150 | } |
---|
| 151 | |
---|
[102] | 152 | |
---|
[96] | 153 | @Override |
---|
[102] | 154 | protected void joinableStart() { |
---|
| 155 | Dispatching.use(acceptThread, this); |
---|
| 156 | Dispatching.use(hosted, this); |
---|
| 157 | Dispatching.use(clients, this); |
---|
| 158 | try { |
---|
| 159 | acceptSocket = new ServerSocket(); |
---|
| 160 | acceptSocket.setReuseAddress(true); |
---|
| 161 | } catch (IOException e) { |
---|
| 162 | throw new FramsticksException().msg("failed to create server socket").cause(e); |
---|
| 163 | } |
---|
| 164 | tryBind(0); |
---|
| 165 | } |
---|
| 166 | |
---|
| 167 | @Override |
---|
| 168 | protected void joinableInterrupt() { |
---|
| 169 | Dispatching.drop(acceptThread, this); |
---|
| 170 | Dispatching.drop(hosted, this); |
---|
| 171 | Dispatching.drop(clients, this); |
---|
| 172 | |
---|
| 173 | try { |
---|
| 174 | acceptSocket.close(); |
---|
| 175 | } catch (IOException e) { |
---|
| 176 | log.debug("exception caught during socket closing: ", e); |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | finishJoinable(); |
---|
| 180 | } |
---|
| 181 | |
---|
| 182 | @Override |
---|
[96] | 183 | protected void joinableFinish() { |
---|
| 184 | |
---|
| 185 | } |
---|
| 186 | |
---|
| 187 | @Override |
---|
| 188 | protected void joinableJoin() throws InterruptedException { |
---|
| 189 | Dispatching.join(acceptThread); |
---|
| 190 | Dispatching.join(hosted); |
---|
| 191 | Dispatching.join(clients); |
---|
| 192 | } |
---|
| 193 | |
---|
| 194 | } |
---|