001/*
002 * Copyright (c) 2013 - 2016 TDP Ltd All Rights Reserved.
003 * TDP Ltd grants permission, free of charge, to any person obtaining copies
004 * of this software and its associated documentation files (the "Software"),
005 * to deal in the Software without restriction, including to use, copy, adapt,
006 * publish, distribute, display, perform, sublicense, and sell copies of the
007 * Software, subject to the following condition: You must include the above
008 * copyright notice and this permission notice in all full or partial copies
009 * of the Software.
010 * TDP LTD PROVIDES THE SOFTWARE "AS IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY,
011 * INCLUDING WITHOUT THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
012 * PARTICULAR PURPOSE, AND NON-INFRINGMENT. TDP LTD, THE AUTHORS OF THE SOFTWARE,
013 * AND THE OWNERS OF COPYRIGHT IN THE SOFTWARE ARE NOT LIABLE FOR ANY CLAIM, DAMAGES,
014 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
015 * FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
016 * THE SOFTWARE.
017 */
018
019package cz.tdp.kshield.notification;
020
021import java.util.concurrent.atomic.AtomicBoolean;
022import java.util.concurrent.atomic.AtomicInteger;
023
024import org.apache.commons.logging.Log;
025import org.apache.commons.logging.LogFactory;
026
027/**
028 * Common Abstract WebSocket Endpoint class
029 */
030public abstract class AbstractWebSocketEndpoint implements KShieldEndpoint
031{
032  private static final Log log = LogFactory.getLog(AbstractWebSocketEndpoint.class);
033  
034  private static final AtomicInteger counter = new AtomicInteger(1);
035  private final int wsId = counter.getAndIncrement();
036  
037  private final AtomicBoolean initialized = new AtomicBoolean(false);
038  
039  private String ipAddress;
040  private String user;
041  private String kshieldAuth;
042  private String lastError;
043  
044  /**
045   * Implement this to provide kshieldAuthManager functionality to WebSocket instance
046   * @return reference to kshieldAuthManager
047   */
048  protected abstract KShieldAuthManager kshieldAuthManager();
049  
050  public AbstractWebSocketEndpoint() {
051    super();
052  }
053
054  @Override
055  public boolean isActive() {
056    return ipAddress != null;
057  }
058  
059  @Override
060  public void signal() {
061    if (isActive()) {
062      send(poll());
063    }
064  }
065  
066  @Override
067  public String toString() {
068    return "user="+user+", kshield="+kshieldAuth+", id=" + wsId + (lastError!=null?" ["+lastError+"]":"");
069  }
070  
071  protected void onErrorImpl(Throwable e) {
072    onCloseImpl();
073    
074    lastError = e.getMessage();
075    
076    log.trace("WebSocket error: ", e);
077  }
078  
079  protected String onMessageImpl(String message) {
080    if (message.startsWith("POLL")) {
081      if (log.isDebugEnabled()) {
082        log.debug("WebSocket [id="+wsId+"] message: " + message);
083      }
084      
085      handleMessage(message);
086      
087      if (isActive()) {
088        if (!initialized.getAndSet(true)) {
089          kshieldAuthManager().addEndpoint(ipAddress, this);
090        }
091      
092        return poll();
093      }
094    }
095    
096    // echo - for testing purposes
097    return message;
098  }
099  
100  private void handleMessage(String message) {
101    if (!initialized.get()) {
102      final String[] tokens = message.split("[:]");
103      
104      if (tokens.length > 1) {
105        ipAddress = token(tokens, 1);
106        user = token(tokens, 2);
107        kshieldAuth = token(tokens, 3);
108      }
109    }
110  }
111  
112  protected abstract void send(String message);
113  
114  protected void onOpenImpl() {
115    reset();
116  }
117  
118  protected void onCloseImpl() {
119    if (ipAddress != null) {
120      kshieldAuthManager().removeEndpoint(ipAddress, this);
121    }
122    
123    reset();
124  }
125
126  protected void reset() {
127    ipAddress = null;
128    user = null;
129    kshieldAuth = null;
130    lastError = null;
131    
132    initialized.set(false);
133  }
134
135  private String poll() {
136    return kshieldAuthManager().poll(ipAddress, kshieldAuth, isNotEmpty(user));
137  }
138
139  private String token(String[] tokens, int idx) {
140    if (idx < tokens.length) {
141      return trimToNull(tokens[idx]);
142    }
143    
144    return null;
145  }
146
147  private String trimToNull(String str) {
148    if (isEmpty(str) || "null".equals(str)) {
149      return null;
150    }
151    return str.trim();
152  }
153  
154  private boolean isEmpty(String str) {
155    return str == null || str.trim().isEmpty();
156  }
157  
158  protected boolean isNotEmpty(String str) {
159    return str != null && !str.isEmpty();
160  }
161}