Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/sample/nio/chatserver/Client.java
38829 views
/*1* Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6*7* - Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9*10* - Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* - Neither the name of Oracle nor the names of its15* contributors may be used to endorse or promote products derived16* from this software without specific prior written permission.17*18* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS19* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,20* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR21* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR22* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,23* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,24* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR25* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF26* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING27* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS28* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031/*32* This source code is provided to illustrate the usage of a given feature33* or technique and has been deliberately simplified. Additional steps34* required for a production-quality application, such as security checks,35* input validation and proper error handling, might not be present in36* this sample code.37*/383940import java.io.IOException;41import java.nio.ByteBuffer;42import java.nio.channels.AsynchronousSocketChannel;43import java.nio.channels.CompletionHandler;44import java.util.LinkedList;45import java.util.Queue;46import java.util.concurrent.atomic.AtomicReference;4748/**49* Client represents a remote connection to the chat server.50* It contains methods for reading and writing messages from the51* channel.52* Messages are considered to be separated by newline, so incomplete53* messages are buffered in the {@code Client}.54*55* All reads and writes are asynchronous and uses the nio2 asynchronous56* elements.57*/58class Client {59private final AsynchronousSocketChannel channel;60private AtomicReference<ClientReader> reader;61private String userName;62private final StringBuilder messageBuffer = new StringBuilder();6364private final Queue<ByteBuffer> queue = new LinkedList<ByteBuffer>();65private boolean writing = false;6667public Client(AsynchronousSocketChannel channel, ClientReader reader) {68this.channel = channel;69this.reader = new AtomicReference<ClientReader>(reader);70}7172/**73* Enqueues a write of the buffer to the channel.74* The call is asynchronous so the buffer is not safe to modify after75* passing the buffer here.76*77* @param buffer the buffer to send to the channel78*/79private void writeMessage(final ByteBuffer buffer) {80boolean threadShouldWrite = false;8182synchronized(queue) {83queue.add(buffer);84// Currently no thread writing, make this thread dispatch a write85if (!writing) {86writing = true;87threadShouldWrite = true;88}89}9091if (threadShouldWrite) {92writeFromQueue();93}94}9596private void writeFromQueue() {97ByteBuffer buffer;9899synchronized (queue) {100buffer = queue.poll();101if (buffer == null) {102writing = false;103}104}105106// No new data in buffer to write107if (writing) {108writeBuffer(buffer);109}110}111112private void writeBuffer(ByteBuffer buffer) {113channel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {114@Override115public void completed(Integer result, ByteBuffer buffer) {116if (buffer.hasRemaining()) {117channel.write(buffer, buffer, this);118} else {119// Go back and check if there is new data to write120writeFromQueue();121}122}123124@Override125public void failed(Throwable exc, ByteBuffer attachment) {126}127});128}129130/**131* Sends a message132* @param string the message133*/134public void writeStringMessage(String string) {135writeMessage(ByteBuffer.wrap(string.getBytes()));136}137138/**139* Send a message from a specific client140* @param client the message is sent from141* @param message to send142*/143public void writeMessageFrom(Client client, String message) {144if (reader.get().acceptsMessages()) {145writeStringMessage(client.getUserName() + ": " + message);146}147}148149/**150* Enqueue a read151* @param completionHandler callback on completed read152*/153public void read(CompletionHandler<Integer, ? super ByteBuffer> completionHandler) {154ByteBuffer input = ByteBuffer.allocate(256);155if (!channel.isOpen()) {156return;157}158channel.read(input, input, completionHandler);159}160161/**162* Closes the channel163*/164public void close() {165try {166channel.close();167} catch (IOException e) {168e.printStackTrace();169}170}171172/**173* Run the current states actions.174*/175public void run() {176reader.get().run(this);177}178179public void setUserName(String userName) {180this.userName = userName;181}182183public void setReader(ClientReader reader) {184this.reader.set(reader);185}186187public String getUserName() {188return userName;189}190191public void appendMessage(String message) {192synchronized (messageBuffer) {193messageBuffer.append(message);194}195}196197/**198* @return the next newline separated message in the buffer. null is returned if the buffer199* doesn't contain any newline.200*/201public String nextMessage() {202synchronized(messageBuffer) {203int nextNewline = messageBuffer.indexOf("\n");204if (nextNewline == -1) {205return null;206}207String message = messageBuffer.substring(0, nextNewline + 1);208messageBuffer.delete(0, nextNewline + 1);209return message;210}211}212}213214215