Author: markt Date: Sun Jan 20 17:54:03 2013 New Revision: 1435904 URL: http://svn.apache.org/viewvc?rev=1435904&view=rev Log: Move server side specific code to separate package. Further refactoring is likely to be required as client side code is developed.
Added: tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointBase.java - copied, changed from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpoint.java tomcat/trunk/java/org/apache/tomcat/websocket/server/ tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java (with props) tomcat/trunk/java/org/apache/tomcat/websocket/server/LocalStrings.properties (with props) tomcat/trunk/java/org/apache/tomcat/websocket/server/ServerContainerImpl.java - copied, changed from r1434927, tomcat/trunk/java/org/apache/tomcat/websocket/ServerContainerImpl.java tomcat/trunk/java/org/apache/tomcat/websocket/server/WsProtocolHandler.java - copied, changed from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java tomcat/trunk/java/org/apache/tomcat/websocket/server/WsRemoteEndpointServer.java (with props) tomcat/trunk/java/org/apache/tomcat/websocket/server/WsSci.java - copied, changed from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsSci.java tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java - copied, changed from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsServlet.java tomcat/trunk/java/org/apache/tomcat/websocket/server/package-info.java (with props) tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java (with props) tomcat/trunk/test/org/apache/tomcat/websocket/server/ tomcat/trunk/test/org/apache/tomcat/websocket/server/TestServerContainerImpl.java - copied, changed from r1434921, tomcat/trunk/test/org/apache/tomcat/websocket/TestUtil.java Removed: tomcat/trunk/java/org/apache/tomcat/websocket/ServerContainerImpl.java tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpoint.java tomcat/trunk/java/org/apache/tomcat/websocket/WsSci.java tomcat/trunk/java/org/apache/tomcat/websocket/WsServlet.java tomcat/trunk/test/org/apache/tomcat/websocket/TestUtil.java Modified: tomcat/trunk/java/javax/websocket/WebSocketContainer.java tomcat/trunk/java/org/apache/tomcat/websocket/Constants.java tomcat/trunk/java/org/apache/tomcat/websocket/Util.java tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java tomcat/trunk/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer tomcat/trunk/res/checkstyle/org-import-control.xml tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/echo/WsConfigListener.java Modified: tomcat/trunk/java/javax/websocket/WebSocketContainer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/javax/websocket/WebSocketContainer.java?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/java/javax/websocket/WebSocketContainer.java (original) +++ tomcat/trunk/java/javax/websocket/WebSocketContainer.java Sun Jan 20 17:54:03 2013 @@ -28,6 +28,21 @@ public interface WebSocketContainer { Session connectToServer(Class<?> annotatedEndpointClass, URI path) throws DeploymentException; + /** + * Creates a new connection to the WebSocket. + * + * @param endpoint + * An instance of this class will be created to handle responses + * from the server + * @param clientEndpointConfiguration + * Used to configure the new connection + * @param path + * The full URL of the WebSocket endpoint to connect to + * + * @return The WebSocket session for the connection + * + * @throws DeploymentException If the connection can not be established + */ Session connectToServer(Class<? extends Endpoint> endpoint, ClientEndpointConfiguration clientEndpointConfiguration, URI path) throws DeploymentException; Modified: tomcat/trunk/java/org/apache/tomcat/websocket/Constants.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/Constants.java?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/Constants.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/Constants.java Sun Jan 20 17:54:03 2013 @@ -23,7 +23,6 @@ public class Constants { protected static final String PACKAGE_NAME = Constants.class.getPackage().getName(); - protected static final String SERVLET_NAME = WsServlet.class.getName(); // OP Codes public static final byte OPCODE_CONTINUATION = 0x00; public static final byte OPCODE_TEXT = 0x01; Modified: tomcat/trunk/java/org/apache/tomcat/websocket/Util.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/Util.java?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/Util.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/Util.java Sun Jan 20 17:54:03 2013 @@ -30,32 +30,6 @@ class Util { } - /** - * Converts a path defined for a WebSocket endpoint into a path that can be - * used as a servlet mapping. - * - * @param wsPath The WebSocket endpoint path to convert - * @return The servlet mapping - */ - static String getServletPath(String wsPath) { - int templateStart = wsPath.indexOf('{'); - if (templateStart == -1) { - if (wsPath.charAt(wsPath.length() - 1) == '/') { - return wsPath + '*'; - } else { - return wsPath + "/*"; - } - } else { - String temp = wsPath.substring(0, templateStart); - if (temp.charAt(temp.length() - 1) == '/') { - return temp + '*'; - } else { - return temp.substring(0, temp.lastIndexOf('/') + 1) + '*'; - } - } - } - - static CloseCode getCloseCode(int code) { if (code > 2999 && code < 5000) { return CloseCodes.NORMAL_CLOSURE; Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java Sun Jan 20 17:54:03 2013 @@ -83,8 +83,13 @@ public class WsFrame { this.sis = sis; this.wsSession = wsSession; + // TODO This needs to work for client and server side code + /* int readBufferSize = ServerContainerImpl.getServerContainer().getReadBufferSize(); + */ + // Temp hack until the above is resolved + int readBufferSize = 8192; inputBuffer = new byte[readBufferSize]; messageBufferBinary = ByteBuffer.allocate(readBufferSize); Copied: tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointBase.java (from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpoint.java) URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointBase.java?p2=tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointBase.java&p1=tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpoint.java&r1=1434921&r2=1435904&rev=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpoint.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/WsRemoteEndpointBase.java Sun Jan 20 17:54:03 2013 @@ -24,35 +24,21 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; -import java.util.concurrent.BrokenBarrierException; -import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Future; -import javax.servlet.ServletOutputStream; import javax.websocket.EncodeException; import javax.websocket.RemoteEndpoint; import javax.websocket.SendHandler; import javax.websocket.SendResult; -public class WsRemoteEndpoint implements RemoteEndpoint { +public abstract class WsRemoteEndpointBase implements RemoteEndpoint { - private final Object messageWriteLock = new Object(); - - private final ServletOutputStream sos; - private final WsSession wsSession; // Max length for outgoing WebSocket frame header is 10 bytes private final ByteBuffer header = ByteBuffer.allocate(10); private final ByteBuffer textToByte = ByteBuffer.allocate(8192); private final CharsetEncoder encoder = Charset.forName("UTF8").newEncoder(); private volatile Boolean isText = null; - private volatile CyclicBarrier writeBarrier = new CyclicBarrier(2); - - - public WsRemoteEndpoint(WsSession wsSession, ServletOutputStream sos) { - this.wsSession = wsSession; - this.sos = sos; - } @Override @@ -200,14 +186,7 @@ public class WsRemoteEndpoint implements } - public void onWritePossible() { - try { - writeBarrier.await(); - } catch (InterruptedException | BrokenBarrierException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } + public abstract void onWritePossible(); protected void sendMessage(byte opCode, ByteBuffer message, @@ -250,50 +229,13 @@ public class WsRemoteEndpoint implements } header.flip(); - // Could sync on sos but don't as other (user or container) code may - // sync on this creating the potential for deadlocks. - synchronized (messageWriteLock) { - doBlockingWrite(header); - doBlockingWrite(message); - try { - sos.flush(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - if (Constants.OPCODE_CLOSE == opCode) { - try { - sos.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - if (opCode == Constants.OPCODE_CLOSE) { - // Connection is closing - ensure no threads are stuck waiting on - // the write barrier - writeBarrier.reset(); - } - } - - private void doBlockingWrite(ByteBuffer data) { - if (!sos.canWrite()) { - try { - writeBarrier.await(); - } catch (InterruptedException | BrokenBarrierException e) { - wsSession.getLocalEndpoint().onError(wsSession, e); - } - } - try { - sos.write(data.array(), data.arrayOffset(), data.limit()); - } catch (IOException e) { - wsSession.getLocalEndpoint().onError(wsSession, e); - } + writeMessage(opCode, header, message); } + protected abstract void writeMessage(int opCode, ByteBuffer header, + ByteBuffer message); + @Override public void setBatchingAllowed(boolean batchingAllowed) { Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/WsSession.java Sun Jan 20 17:54:03 2013 @@ -43,12 +43,12 @@ public class WsSession implements Sessio private static final Charset UTF8 = Charset.forName("UTF8"); private final Endpoint localEndpoint; - private WsRemoteEndpoint wsRemoteEndpoint; + private WsRemoteEndpointBase wsRemoteEndpoint; private MessageHandler textMessageHandler = null; private MessageHandler binaryMessageHandler = null; private MessageHandler.Basic<PongMessage> pongMessageHandler = null; - protected WsSession(Endpoint localEndpoint) { + public WsSession(Endpoint localEndpoint) { this.localEndpoint = localEndpoint; } @@ -250,7 +250,7 @@ public class WsSession implements Sessio } - protected void setRemote(WsRemoteEndpoint wsRemoteEndpoint) { + public void setRemote(WsRemoteEndpointBase wsRemoteEndpoint) { this.wsRemoteEndpoint = wsRemoteEndpoint; } @@ -269,12 +269,12 @@ public class WsSession implements Sessio return pongMessageHandler; } - protected void onClose(CloseReason closeReason) { + public void onClose(CloseReason closeReason) { localEndpoint.onClose(this, closeReason); } - protected Endpoint getLocalEndpoint() { + public Endpoint getLocalEndpoint() { return localEndpoint; } Added: tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java?rev=1435904&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java (added) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java Sun Jan 20 17:54:03 2013 @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.tomcat.websocket.server; + +/** + * Internal implementation constants. + */ +public class Constants { + + protected static final String PACKAGE_NAME = + Constants.class.getPackage().getName(); + protected static final String SERVLET_NAME = WsServlet.class.getName(); + + private Constants() { + // Hide default constructor + } +} Propchange: tomcat/trunk/java/org/apache/tomcat/websocket/server/Constants.java ------------------------------------------------------------------------------ svn:eol-style = native Added: tomcat/trunk/java/org/apache/tomcat/websocket/server/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/LocalStrings.properties?rev=1435904&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/server/LocalStrings.properties (added) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/LocalStrings.properties Sun Jan 20 17:54:03 2013 @@ -0,0 +1,37 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +sci.newInstance.fail=Failed to create an Endpoint instance of type [{0}] +serverContainer.endpointDeploy=Endpoint class [{0}] deploying to path [{1}] in ServletContext [{2}] +serverContainer.missingEndpoint=An Endpoint instance has been request for path [{0}] but no matching Endpoint class was found +serverContainer.pojoDeploy=POJO class [{0}] deploying to path [{1}] in ServletContext [{2}] +serverContainer.servletContextMismatch=Attempted to register a POJO annotated for WebSocket at path [{0}] in the ServletContext with context path [{1}] when the WebSocket ServerContainer is allocated to the ServletContext with context path [{2}] +serverContainer.servletContextMissing=No ServletContext was specified +uriTemplate.noMatch=The input template [{0}] generated the pattern [{1}] which did not match the supplied pathInfo [{2}] +# Note the wsFrame.* messages are used as close reasons in WebSocket control +# frames and therefore must be 123 bytes (not characters) or less in length. +# Messages are encoded using UTF-8 where a single character may be encoded in +# as many as 4 bytes. +wsFrame.byteToLongFail=Too many bytes ([{0}]) were provided to be converted into a long +wsFrame.controlFragmented=A fragmented control frame was received but control frames may not be fragmented +wsFrame.controlPayloadTooBig=A control frame was sent with a payload of size [{0}] which is larger than the maximum permitted of 125 bytes +wsFrame.controlNoFin=A control frame was sent that did not have the fin bit set. Control frames are not permitted to use continuation frames. +wsFrame.invalidOpCode= A WebSocket frame was sent with an unrecognised opCode of [{0}] +wsFrame.invalidUtf8=A WebSocket text frame was received that could not be decoded to UTF-8 because it contained invalid byte sequences +wsFrame.invalidUtf8Close=A WebSocket close frame was received with a close reason that contained invalid UTF-8 byte sequences +wsFrame.noContinuation=A new message was started when a continuation frame was expected +wsFrame.notMasked=The client frame was not masked but all client frames must be masked +wsFrame.oneByteCloseCode=The client sent a close frame with a single byte payload which is not valid +wsFrame.textMessageTooBig=The decoded text message was too big for the output buffer and the endpoint does not support partial messages +wsFrame.wrongRsv=The client frame set the reserved bits to [{0}] which was not supported by this endpoint \ No newline at end of file Propchange: tomcat/trunk/java/org/apache/tomcat/websocket/server/LocalStrings.properties ------------------------------------------------------------------------------ svn:eol-style = native Copied: tomcat/trunk/java/org/apache/tomcat/websocket/server/ServerContainerImpl.java (from r1434927, tomcat/trunk/java/org/apache/tomcat/websocket/ServerContainerImpl.java) URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/ServerContainerImpl.java?p2=tomcat/trunk/java/org/apache/tomcat/websocket/server/ServerContainerImpl.java&p1=tomcat/trunk/java/org/apache/tomcat/websocket/ServerContainerImpl.java&r1=1434927&r2=1435904&rev=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/ServerContainerImpl.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/ServerContainerImpl.java Sun Jan 20 17:54:03 2013 @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.tomcat.websocket; +package org.apache.tomcat.websocket.server; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -31,6 +31,7 @@ import javax.websocket.server.ServerEndp import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; +import org.apache.tomcat.websocket.WsWebSocketContainer; import org.apache.tomcat.websocket.pojo.PojoEndpointConfiguration; import org.apache.tomcat.websocket.pojo.PojoMethodMapping; @@ -111,7 +112,7 @@ public class ServerContainerImpl extends throw new DeploymentException(sm.getString("sci.newInstance.fail", endpointClass.getName()), e); } - String servletPath = Util.getServletPath(path); + String servletPath = getServletPath(path); if (log.isDebugEnabled()) { log.debug(sm.getString("serverContainer.endpointDeploy", endpointClass.getName(), path, @@ -150,7 +151,7 @@ public class ServerContainerImpl extends log.debug(sm.getString("serverContainer.pojoDeploy", pojo.getName(), wsPath, servletContext.getContextPath())); } - String servletPath = Util.getServletPath(wsPath); + String servletPath = getServletPath(wsPath); // Remove the trailing /* before adding it to the map pojoMap.put(servletPath.substring(0, servletPath.length() - 2), pojo); pojoMethodMap.put(pojo, @@ -201,4 +202,30 @@ public class ServerContainerImpl extends public void setReadBufferSize(int readBufferSize) { this.readBufferSize = readBufferSize; } + + + /** + * Converts a path defined for a WebSocket endpoint into a path that can be + * used as a servlet mapping. + * + * @param wsPath The WebSocket endpoint path to convert + * @return The servlet mapping + */ + static String getServletPath(String wsPath) { + int templateStart = wsPath.indexOf('{'); + if (templateStart == -1) { + if (wsPath.charAt(wsPath.length() - 1) == '/') { + return wsPath + '*'; + } else { + return wsPath + "/*"; + } + } else { + String temp = wsPath.substring(0, templateStart); + if (temp.charAt(temp.length() - 1) == '/') { + return temp + '*'; + } else { + return temp.substring(0, temp.lastIndexOf('/') + 1) + '*'; + } + } + } } Copied: tomcat/trunk/java/org/apache/tomcat/websocket/server/WsProtocolHandler.java (from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java) URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/WsProtocolHandler.java?p2=tomcat/trunk/java/org/apache/tomcat/websocket/server/WsProtocolHandler.java&p1=tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java&r1=1434921&r2=1435904&rev=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/WsProtocolHandler.java Sun Jan 20 17:54:03 2013 @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.tomcat.websocket; +package org.apache.tomcat.websocket.server; import java.io.EOFException; import java.io.IOException; @@ -30,6 +30,10 @@ import javax.websocket.CloseReason.Close import javax.websocket.Endpoint; import javax.websocket.EndpointConfiguration; +import org.apache.tomcat.websocket.WsFrame; +import org.apache.tomcat.websocket.WsIOException; +import org.apache.tomcat.websocket.WsSession; + /** * Servlet 3.1 HTTP upgrade handler for WebSocket connections. */ @@ -69,10 +73,11 @@ public class WsProtocolHandler implement try { WsFrame wsFrame = new WsFrame(sis, wsSession); sis.setReadListener(new WsReadListener(this, wsFrame, wsSession)); - WsRemoteEndpoint wsRemoteEndpoint = - new WsRemoteEndpoint(wsSession, sos); - wsSession.setRemote(wsRemoteEndpoint); - sos.setWriteListener(new WsWriteListener(this, wsRemoteEndpoint)); + WsRemoteEndpointServer wsRemoteEndpointServer = + new WsRemoteEndpointServer(wsSession, sos); + wsSession.setRemote(wsRemoteEndpointServer); + sos.setWriteListener( + new WsWriteListener(this, wsRemoteEndpointServer)); ep.onOpen(wsSession, endpointConfig); } finally { t.setContextClassLoader(cl); @@ -150,18 +155,18 @@ public class WsProtocolHandler implement private static class WsWriteListener implements WriteListener { private final WsProtocolHandler wsProtocolHandler; - private final WsRemoteEndpoint wsRemoteEndpoint; + private final WsRemoteEndpointServer wsRemoteEndpointServer; private WsWriteListener(WsProtocolHandler wsProtocolHandler, - WsRemoteEndpoint wsRemoteEndpoint) { + WsRemoteEndpointServer wsRemoteEndpointServer) { this.wsProtocolHandler = wsProtocolHandler; - this.wsRemoteEndpoint = wsRemoteEndpoint; + this.wsRemoteEndpointServer = wsRemoteEndpointServer; } @Override public void onWritePossible() { - wsRemoteEndpoint.onWritePossible(); + wsRemoteEndpointServer.onWritePossible(); } Added: tomcat/trunk/java/org/apache/tomcat/websocket/server/WsRemoteEndpointServer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/WsRemoteEndpointServer.java?rev=1435904&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/server/WsRemoteEndpointServer.java (added) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/WsRemoteEndpointServer.java Sun Jan 20 17:54:03 2013 @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.tomcat.websocket.server; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; + +import javax.servlet.ServletOutputStream; + +import org.apache.tomcat.websocket.Constants; +import org.apache.tomcat.websocket.WsRemoteEndpointBase; +import org.apache.tomcat.websocket.WsSession; + +/** + * This is the server side {@link RemoteEndpoint} implementation - i.e. what the + * server uses to send data to the client. Communication is over a + * {@link ServletOutputStream}. + */ +public class WsRemoteEndpointServer extends WsRemoteEndpointBase { + + private final WsSession wsSession; + private final ServletOutputStream sos; + private final Object messageWriteLock = new Object(); + + private volatile CyclicBarrier writeBarrier = new CyclicBarrier(2); + + + public WsRemoteEndpointServer(WsSession wsSession, + ServletOutputStream sos) { + this.wsSession = wsSession; + this.sos = sos; + } + + + @Override + public void onWritePossible() { + try { + writeBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + @Override + protected void writeMessage(int opCode, ByteBuffer header, + ByteBuffer message) { + // Could sync on sos but don't as other (user or container) code may + // sync on this creating the potential for deadlocks. + synchronized (messageWriteLock) { + doBlockingWrite(header); + doBlockingWrite(message); + try { + sos.flush(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + if (Constants.OPCODE_CLOSE == opCode) { + try { + sos.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + if (opCode == Constants.OPCODE_CLOSE) { + // Connection is closing - ensure no threads are stuck waiting on + // the write barrier + writeBarrier.reset(); + } + } + + + private void doBlockingWrite(ByteBuffer data) { + if (!sos.canWrite()) { + try { + writeBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + wsSession.getLocalEndpoint().onError(wsSession, e); + } + } + try { + sos.write(data.array(), data.arrayOffset(), data.limit()); + } catch (IOException e) { + wsSession.getLocalEndpoint().onError(wsSession, e); + } + } +} Propchange: tomcat/trunk/java/org/apache/tomcat/websocket/server/WsRemoteEndpointServer.java ------------------------------------------------------------------------------ svn:eol-style = native Copied: tomcat/trunk/java/org/apache/tomcat/websocket/server/WsSci.java (from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsSci.java) URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/WsSci.java?p2=tomcat/trunk/java/org/apache/tomcat/websocket/server/WsSci.java&p1=tomcat/trunk/java/org/apache/tomcat/websocket/WsSci.java&r1=1434921&r2=1435904&rev=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsSci.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/WsSci.java Sun Jan 20 17:54:03 2013 @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.tomcat.websocket; +package org.apache.tomcat.websocket.server; import java.util.Set; @@ -24,6 +24,7 @@ import javax.servlet.ServletException; import javax.servlet.annotation.HandlesTypes; import javax.websocket.server.WebSocketEndpoint; + /** * Registers an interest in any class that is annotated with * {@link WebSocketEndpoint} so that Endpoint can be published via the WebSocket Copied: tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java (from r1434921, tomcat/trunk/java/org/apache/tomcat/websocket/WsServlet.java) URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java?p2=tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java&p1=tomcat/trunk/java/org/apache/tomcat/websocket/WsServlet.java&r1=1434921&r2=1435904&rev=1435904&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/WsServlet.java (original) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/WsServlet.java Sun Jan 20 17:54:03 2013 @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.tomcat.websocket; +package org.apache.tomcat.websocket.server; import java.io.IOException; import java.nio.charset.Charset; @@ -38,6 +38,7 @@ import javax.websocket.Extension; import javax.websocket.server.ServerEndpointConfiguration; import javax.xml.bind.DatatypeConverter; + /** * Handles the initial HTTP connection for WebSocket connections. */ Added: tomcat/trunk/java/org/apache/tomcat/websocket/server/package-info.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/server/package-info.java?rev=1435904&view=auto ============================================================================== --- tomcat/trunk/java/org/apache/tomcat/websocket/server/package-info.java (added) +++ tomcat/trunk/java/org/apache/tomcat/websocket/server/package-info.java Sun Jan 20 17:54:03 2013 @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +/** + * Server-side specific implementation classes. These are in a separate package + * to make packaging a pure client JAR simpler. + */ +package org.apache.tomcat.websocket.server; \ No newline at end of file Propchange: tomcat/trunk/java/org/apache/tomcat/websocket/server/package-info.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/trunk/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer URL: http://svn.apache.org/viewvc/tomcat/trunk/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer (original) +++ tomcat/trunk/res/META-INF/tomcat-websocket.jar/services/javax.servlet.ServletContainerInitializer Sun Jan 20 17:54:03 2013 @@ -1 +1 @@ -org.apache.tomcat.websocket.WsSci +org.apache.tomcat.websocket.server.WsSci Modified: tomcat/trunk/res/checkstyle/org-import-control.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/res/checkstyle/org-import-control.xml?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/res/checkstyle/org-import-control.xml (original) +++ tomcat/trunk/res/checkstyle/org-import-control.xml Sun Jan 20 17:54:03 2013 @@ -138,12 +138,16 @@ </subpackage> </subpackage> <subpackage name="websocket"> - <allow pkg="javax.servlet"/> <allow pkg="javax.websocket"/> <allow pkg="org.apache.juli"/> <allow pkg="org.apache.tomcat.util"/> <!-- Ideally want to remove this --> <allow pkg="org.apache.tomcat.websocket.pojo"/> + <disallow pkg="javax.servlet"/> + <subpackage name="server"> + <allow pkg="javax.servlet"/> + <allow pkg="org.apache.tomcat.websocket"/> + </subpackage> </subpackage> </subpackage> </import-control> \ No newline at end of file Added: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java?rev=1435904&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java (added) +++ tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java Sun Jan 20 17:54:03 2013 @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.tomcat.websocket; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.websocket.ContainerProvider; +import javax.websocket.DefaultClientConfiguration; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfiguration; +import javax.websocket.MessageHandler; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; + +public class TestWsWebSocketContainer extends TomcatBaseTest { + + private static final String MESSAGE_STRING_1 = "qwerty"; + + @Test + public void testConnectToServerEndpoint() throws Exception { + // Examples app includes WebSocket Echo endpoint + Tomcat tomcat = getTomcatInstance(); + File appDir = new File(getBuildDirectory(), "webapps/examples"); + tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + + tomcat.start(); + + WebSocketContainer wsContainer = ContainerProvider.getClientContainer(); + Session wsSession = wsContainer.connectToServer(TesterEndpoint.class, + new DefaultClientConfiguration(), new URI("http://localhost:" + + getPort() + "/examples/echoAnnotation")); + TesterMessageHandlerString handler = new TesterMessageHandlerString(1); + wsSession.addMessageHandler(handler); + wsSession.getRemote().sendString(MESSAGE_STRING_1); + + boolean latchResult = handler.getLatch().await(10, TimeUnit.SECONDS); + + Assert.assertTrue(latchResult); + + List<String> messages = handler.getMessages(); + Assert.assertEquals(1, messages.size()); + Assert.assertEquals(MESSAGE_STRING_1, messages.get(0)); + } + + private static class TesterMessageHandlerString + implements MessageHandler.Basic<String> { + + private final CountDownLatch latch; + + private List<String> messages = new ArrayList<>(); + + public TesterMessageHandlerString(int latchCount) { + if (latchCount > 0) { + latch = new CountDownLatch(latchCount); + } else { + latch = null; + } + } + + public List<String> getMessages() { + return messages; + } + + public CountDownLatch getLatch() { + return latch; + } + + @Override + public void onMessage(String message) { + if (latch != null) { + latch.countDown(); + } + messages.add(message); + } + } + + private static class TesterEndpoint extends Endpoint { + + @Override + public void onOpen(Session session, EndpointConfiguration config) { + // TODO Auto-generated method stub + } + } +} Propchange: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsWebSocketContainer.java ------------------------------------------------------------------------------ svn:eol-style = native Copied: tomcat/trunk/test/org/apache/tomcat/websocket/server/TestServerContainerImpl.java (from r1434921, tomcat/trunk/test/org/apache/tomcat/websocket/TestUtil.java) URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/server/TestServerContainerImpl.java?p2=tomcat/trunk/test/org/apache/tomcat/websocket/server/TestServerContainerImpl.java&p1=tomcat/trunk/test/org/apache/tomcat/websocket/TestUtil.java&r1=1434921&r2=1435904&rev=1435904&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/tomcat/websocket/TestUtil.java (original) +++ tomcat/trunk/test/org/apache/tomcat/websocket/server/TestServerContainerImpl.java Sun Jan 20 17:54:03 2013 @@ -14,30 +14,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.tomcat.websocket; +package org.apache.tomcat.websocket.server; import org.junit.Assert; import org.junit.Test; -public class TestUtil { +public class TestServerContainerImpl { @Test public void testGetServletMappingPath() throws Exception { Assert.assertEquals("/foo/*", - Util.getServletPath("/foo")); + ServerContainerImpl.getServletPath("/foo")); Assert.assertEquals("/foo/*", - Util.getServletPath("/foo/")); + ServerContainerImpl.getServletPath("/foo/")); Assert.assertEquals("/foo/bar/*", - Util.getServletPath("/foo/bar")); + ServerContainerImpl.getServletPath("/foo/bar")); Assert.assertEquals("/foo/bar/*", - Util.getServletPath("/foo/bar/")); + ServerContainerImpl.getServletPath("/foo/bar/")); Assert.assertEquals("/foo/*", - Util.getServletPath("/foo/{bar}")); + ServerContainerImpl.getServletPath("/foo/{bar}")); Assert.assertEquals("/foo/*", - Util.getServletPath("/foo/{bar}/")); + ServerContainerImpl.getServletPath("/foo/{bar}/")); Assert.assertEquals("/foo/*", - Util.getServletPath("/foo/x{bar}")); + ServerContainerImpl.getServletPath("/foo/x{bar}")); Assert.assertEquals("/foo/*", - Util.getServletPath("/foo/x{bar}/")); + ServerContainerImpl.getServletPath("/foo/x{bar}/")); } } Modified: tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/echo/WsConfigListener.java URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/echo/WsConfigListener.java?rev=1435904&r1=1435903&r2=1435904&view=diff ============================================================================== --- tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/echo/WsConfigListener.java (original) +++ tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/echo/WsConfigListener.java Sun Jan 20 17:54:03 2013 @@ -23,7 +23,7 @@ import javax.servlet.annotation.WebListe import javax.websocket.DeploymentException; import javax.websocket.server.DefaultServerConfiguration; -import org.apache.tomcat.websocket.ServerContainerImpl; +import org.apache.tomcat.websocket.server.ServerContainerImpl; @WebListener public class WsConfigListener implements ServletContextListener { --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org