Package org.atmosphere.play

Source Code of org.atmosphere.play.PlayAsyncIOWriter

/*
* Copyright 2012 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.play;

import org.atmosphere.cpr.AsyncIOWriter;
import org.atmosphere.cpr.AsynchronousProcessor;
import org.atmosphere.cpr.AtmosphereInterceptorWriter;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResourceImpl;
import org.atmosphere.cpr.AtmosphereResponse;
import org.atmosphere.cpr.HeaderConfig;
import org.atmosphere.util.ByteArrayAsyncWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import play.api.mvc.Codec;
import play.core.j.JavaResults;
import play.libs.F;
import play.mvc.Http;
import play.mvc.Results;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public class PlayAsyncIOWriter extends AtmosphereInterceptorWriter implements PlayInternal<Results.Chunks<String>> {
    private static final Logger logger = LoggerFactory.getLogger(PlayAsyncIOWriter.class);
    private final AtomicInteger pendingWrite = new AtomicInteger();
    private final AtomicBoolean asyncClose = new AtomicBoolean(false);
    private final AtomicBoolean isClosed = new AtomicBoolean(false);
    private final ByteArrayAsyncWriter buffer = new ByteArrayAsyncWriter();
    protected Results.Chunks<String> chunks;
    protected Results.Chunks.Out<String> out;
    private boolean byteWritten = false;
    private long lastWrite = 0;
    private boolean resumeOnBroadcast;

    public PlayAsyncIOWriter(final Http.Request request, final Map<String, Object> additionalAttributes, final Http.Response response) {
        chunks = new Results.Chunks<String>(JavaResults.writeString(Codec.utf_8())) {
            @Override
            public void onReady(Results.Chunks.Out<String> oout) {
                out = oout;
                boolean keepAlive = false;

                try {
                    final AtmosphereRequest r = AtmosphereUtils.request(request, additionalAttributes);
                    out.onDisconnected(new F.Callback0() {
                        @Override
                        public void invoke() throws Throwable {
                            _close(r);
                        }
                    });

                    AtmosphereResponse res = new AtmosphereResponse.Builder()
                            .asyncIOWriter(PlayAsyncIOWriter.this)
                            .writeHeader(false)
                            .request(r).build();
                    keepAlive = AtmosphereCoordinator.instance().route(r, res);
                } catch (Throwable e) {
                    logger.error("", e);
                    keepAlive = true;
                } finally {
                    if (!keepAlive) {
                        out.close();
                    }
                }
            }
        };

        // TODO: Configuring headers in Atmosphere won't work as the onReady is asynchronously called.
        // TODO: Some Broadcaster's Cache won't work as well.
        String[] transport = request.queryString() != null ? request.queryString().get(HeaderConfig.X_ATMOSPHERE_TRANSPORT) : null;
        if (transport != null && transport.length > 0 && transport[0].equalsIgnoreCase(HeaderConfig.SSE_TRANSPORT)) {
            response.setContentType("text/event-stream");
        }
    }

    public Results.Chunks<String> internal() {
        return chunks;
    }

    public boolean isClosed() {
        return isClosed.get();
    }

    public boolean byteWritten() {
        return byteWritten;
    }

    public void resumeOnBroadcast(boolean resumeOnBroadcast) {
        this.resumeOnBroadcast = resumeOnBroadcast;
    }

    @Override
    public AsyncIOWriter writeError(AtmosphereResponse r, int errorCode, String message) throws IOException {
//        if (!response.isOpen()) {
//            return this;
//        }

        // TODO: Set status
        logger.error("Error {}:{}", errorCode, message);
        out.write(message);
        return this;
    }

    @Override
    public AsyncIOWriter write(AtmosphereResponse r, String data) throws IOException {
        byte[] b = data.getBytes("ISO-8859-1");
        write(r, b);
        return this;
    }

    @Override
    public AsyncIOWriter write(AtmosphereResponse r, byte[] data) throws IOException {
        write(r, data, 0, data.length);
        return this;
    }

    protected byte[] transform(AtmosphereResponse response, byte[] b, int offset, int length) throws IOException {
        AsyncIOWriter a = response.getAsyncIOWriter();
        try {
            response.asyncIOWriter(buffer);
            invokeInterceptor(response, b, offset, length);
            return buffer.stream().toByteArray();
        } finally {
            buffer.close(null);
            response.asyncIOWriter(a);
        }
    }

    @Override
    public AsyncIOWriter write(final AtmosphereResponse r, byte[] data, int offset, int length) throws IOException {
        logger.trace("Writing {} with transport {}", r.resource().uuid(), r.resource().transport());
        boolean transform = filters.size() > 0 && r.getStatus() < 400;
        if (transform) {
            data = transform(r, data, offset, length);
            offset = 0;
            length = data.length;
        }

        pendingWrite.incrementAndGet();

        out.write(new String(data, offset, length, r.getCharacterEncoding()));
        byteWritten = true;
        lastWrite = System.currentTimeMillis();
        if (resumeOnBroadcast) {
            out.close();
            _close(r.request());
        }
        return this;
    }

    private void _close(AtmosphereRequest request) {
        AtmosphereResourceImpl r = AtmosphereResourceImpl.class.cast(request.resource());
        if (request != null && r != null) {
            AsynchronousProcessor.class.cast(r.getAtmosphereConfig().framework().getAsyncSupport()).endRequest(r, true);
        }
    }

    public long lastTick() {
        return lastWrite == -1 ? System.currentTimeMillis() : lastWrite;
    }

    @Override
    public void close(AtmosphereResponse r) throws IOException {
        // Make sure we don't have bufferred bytes
        if (!byteWritten && r != null && r.getOutputStream() != null) {
            r.getOutputStream().flush();
        }

        asyncClose.set(true);
        out.close();
    }
}
TOP

Related Classes of org.atmosphere.play.PlayAsyncIOWriter

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.