Package org.atmosphere.wasync.serial

Source Code of org.atmosphere.wasync.serial.DefaultSerializedFireStage$FirePayloadEntry

/*
* Copyright 2014 Jeanfrancois Arcand
*
* Licensed 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.atmosphere.wasync.serial;

import com.google.common.util.concurrent.SettableFuture;
import com.ning.http.client.ListenableFuture;
import com.ning.http.client.Response;

import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
* Default implementation of a {@link SerializedFireStage}.
* <p/>
* This implementation is based on an unbounded stage that enqueues the payload
* objects to be fired ({@link SerializedFireStage#enqueue(Object, SettableFuture)}) by
* means of a {@link LinkedBlockingQueue}.
* <p/>
* Every instance of this class spans its dedicated stage thread, which sequentially
* consumes payload objects off the stage queue.
* <p/>
* Binary payloads are aggregated up to a {@code maxBinaryPayloadAggregationSize}.
* <p/>
*
* @author Christian Bach
*/
public class DefaultSerializedFireStage implements SerializedFireStage {

    private volatile SerializedSocket socket;
    private final int maxBinaryMessagesAggregationSize;

    private final BlockingQueue<FirePayloadEntry> firePayloadsQueue;
    private final ExecutorService executorService;
    private final Runnable fireTask;

    public DefaultSerializedFireStage() {
        this(100);
    }

    public DefaultSerializedFireStage(int maxBinaryPayloadAggregationSize) {
        this.maxBinaryMessagesAggregationSize = maxBinaryPayloadAggregationSize;
        firePayloadsQueue = new LinkedBlockingQueue<FirePayloadEntry>();
        executorService = Executors.newSingleThreadExecutor();
        fireTask = createFireTask();
        executorService.execute(fireTask);
    }

    @Override
    public void setSocket(SerializedSocket socket) {
        this.socket = socket;
    }

    @Override
    public void enqueue(Object firePayload, SettableFuture<Response> originalFuture) {
        firePayloadsQueue.add(new FirePayloadEntry(firePayload, originalFuture));
    }

    private Runnable createFireTask() {
        return new Runnable() {
            public void run() {
                ArrayList<FirePayloadEntry> aggregatedByteArrayPayloads = new ArrayList<FirePayloadEntry>(maxBinaryMessagesAggregationSize);
                try {
                    while (!Thread.currentThread().isInterrupted()) {
                        FirePayloadEntry payloadEntry = firePayloadsQueue.take();
                        int aggregationCount = 1;

                        if (byte[].class.isAssignableFrom(payloadEntry.getFirePayload().getClass())) {
                          aggregatedByteArrayPayloads.add(payloadEntry)
                        do {
                          payloadEntry = firePayloadsQueue.poll();
                          if (payloadEntry == null) { break; }
                            if (byte[].class.isAssignableFrom(payloadEntry.getFirePayload().getClass())) {
                                aggregatedByteArrayPayloads.add(payloadEntry);
                                aggregationCount++;
                            } else {
                                if (!aggregatedByteArrayPayloads.isEmpty()) {
                                    fireSynchronously(aggregatedByteArrayPayloads);
                                    aggregatedByteArrayPayloads.clear();
                                }
                                fireSynchronously(payloadEntry);
                                break;
                            }
                        } while (aggregationCount < maxBinaryMessagesAggregationSize);
                        } else {
                          fireSynchronously(payloadEntry);
                        }

                        if (!aggregatedByteArrayPayloads.isEmpty()) {
                            fireSynchronously(aggregatedByteArrayPayloads);
                            aggregatedByteArrayPayloads.clear();
                        }

                    }
                } catch (InterruptedException consumed) {
                    // allow thread to exit
                }
            }
        };
    }

    private void fireSynchronously(ArrayList<FirePayloadEntry> aggregatedByteArrayPayloads) {
        ListenableFuture<Response> future;
        int aggregatedSize = 0;
        for (FirePayloadEntry entry : aggregatedByteArrayPayloads) {
            aggregatedSize += ((byte[]) entry.getFirePayload()).length;
        }
        byte[] aggregatedByteArray = new byte[aggregatedSize];
        int destPos = 0;
        for (FirePayloadEntry entry : aggregatedByteArrayPayloads) {
            byte[] payload = (byte[]) entry.getFirePayload();
            System.arraycopy(
                    payload, 0,
                    aggregatedByteArray, destPos,
                    payload.length);
            destPos += payload.length;
        }


        Response response = null;
        try {
            future = socket.directWrite(aggregatedByteArray);
            response = future.get();
        } catch (Exception e) {
            for (FirePayloadEntry entry : aggregatedByteArrayPayloads) {
                entry.getOriginalFuture().setException(e);
                entry.getOriginalFuture().cancel(true);
            }
        } finally {
            for (FirePayloadEntry entry : aggregatedByteArrayPayloads) {
                entry.getOriginalFuture().set(response);
            }
        }
    }

    public void fireSynchronously(FirePayloadEntry firePayloadEntry) {
        ListenableFuture<Response> future;
        Response response = null;
        try {
            future = socket.directWrite(firePayloadEntry.firePayload);
            response = future.get();
        } catch (Exception e) {
            firePayloadEntry.getOriginalFuture().setException(e);
            firePayloadEntry.getOriginalFuture().cancel(true);
        } finally {
            firePayloadEntry.getOriginalFuture().set(response);
        }
    }

    @Override
    public void shutdown() {
        executorService.shutdownNow();
        for (FirePayloadEntry entry : firePayloadsQueue) {
            entry.getOriginalFuture().cancel(true);
        }
    }

    private class FirePayloadEntry {

        private Object firePayload;
        private SettableFuture<Response> originalFuture;

        public FirePayloadEntry(Object firePayload, SettableFuture<Response> originalFuture) {
            this.firePayload = firePayload;
            this.originalFuture = originalFuture;
        }

        public Object getFirePayload() {
            return firePayload;
        }

        public SettableFuture<Response> getOriginalFuture() {
            return originalFuture;
        }

    }

}
TOP

Related Classes of org.atmosphere.wasync.serial.DefaultSerializedFireStage$FirePayloadEntry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.