org.apache.openmeetings.web.util.RecordingResourceReference.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.openmeetings.web.util.RecordingResourceReference.java

Source

/*
 * 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.openmeetings.web.util;

import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;
import static org.apache.openmeetings.web.app.Application.getBean;
import static org.apache.openmeetings.web.app.WebSession.getExternalType;
import static org.apache.openmeetings.web.app.WebSession.getRecordingId;
import static org.apache.openmeetings.web.app.WebSession.getUserId;
import static org.red5.logging.Red5LoggerFactory.getLogger;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.input.BoundedInputStream;
import org.apache.openmeetings.db.dao.record.FlvRecordingDao;
import org.apache.openmeetings.db.dao.user.OrganisationUserDao;
import org.apache.openmeetings.db.entity.record.FlvRecording;
import org.apache.openmeetings.web.app.WebSession;
import org.apache.wicket.protocol.http.servlet.ResponseIOException;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.resource.AbstractResource;
import org.apache.wicket.request.resource.ContentDisposition;
import org.apache.wicket.request.resource.IResource;
import org.apache.wicket.request.resource.IResource.Attributes;
import org.apache.wicket.request.resource.ResourceReference;
import org.apache.wicket.util.lang.Bytes;
import org.apache.wicket.util.resource.FileResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.time.Time;
import org.slf4j.Logger;

public abstract class RecordingResourceReference extends ResourceReference {
    private static final long serialVersionUID = 1L;
    private static final Logger log = getLogger(RecordingResourceReference.class, webAppRootKey);

    public RecordingResourceReference(Class<? extends RecordingResourceReference> clazz) {
        super(clazz, "recordings");
    }

    @Override
    public IResource getResource() {
        return new AbstractResource() {
            private static final long serialVersionUID = 1L;
            private final static String ACCEPT_RANGES_HEADER = "Accept-Ranges";
            private final static String RANGE_HEADER = "Range";
            private final static String CONTENT_RANGE_HEADER = "Content-Range";
            private final static String RANGES_BYTES = "bytes";
            private File file;
            private boolean isRange = false;
            private long start = 0;
            private long end = 0;

            private long getChunkLength() {
                return isRange ? end - start + 1 : (file == null ? -1 : file.length());
            }

            private IResourceStream getResourceStream() {
                return file == null ? null : new FileResourceStream(file) {
                    private static final long serialVersionUID = 2546785247219805747L;
                    private transient BoundedInputStream bi;

                    @Override
                    public InputStream getInputStream() throws ResourceStreamNotFoundException {
                        if (bi == null) {
                            //bi = new BoundedInputStream(super.getInputStream(), end + 1);
                            bi = new BoundedInputStream(super.getInputStream(),
                                    isRange ? end + 1 : (file == null ? -1 : file.length()));
                            try {
                                bi.skip(start);
                            } catch (IOException e) {
                                throw new ResourceStreamNotFoundException(e);
                            }
                        }
                        return bi;
                    }

                    @Override
                    public Bytes length() {
                        return Bytes.bytes(getChunkLength());
                    }

                    @Override
                    public void close() throws IOException {
                        if (bi != null) {
                            bi.close(); //also will close original stream
                            bi = null;
                        }
                    }

                    @Override
                    public String getContentType() {
                        return RecordingResourceReference.this.getContentType();
                    }
                };
            }

            @Override
            protected void setResponseHeaders(ResourceResponse data, Attributes attributes) {
                Response response = attributes.getResponse();
                if (response instanceof WebResponse) {
                    WebResponse webResponse = (WebResponse) response;
                    webResponse.setStatus(
                            isRange ? HttpServletResponse.SC_PARTIAL_CONTENT : HttpServletResponse.SC_OK);
                }
                super.setResponseHeaders(data, attributes);
            }

            @Override
            protected ResourceResponse newResourceResponse(Attributes attributes) {
                ResourceResponse rr = new ResourceResponse();
                FlvRecording r = getRecording(attributes);
                if (r != null) {
                    isRange = false;
                    file = getFile(r);
                    rr.setFileName(getFileName(r));
                    rr.setContentType(RecordingResourceReference.this.getContentType());
                    rr.setContentDisposition(ContentDisposition.INLINE);
                    rr.setLastModified(Time.millis(file.lastModified()));
                    rr.getHeaders().addHeader(ACCEPT_RANGES_HEADER, RANGES_BYTES);
                    String range = ((HttpServletRequest) attributes.getRequest().getContainerRequest())
                            .getHeader(RANGE_HEADER);
                    if (range != null && range.startsWith(RANGES_BYTES)) {
                        String[] bounds = range.substring(RANGES_BYTES.length() + 1).split("-");
                        if (bounds != null && bounds.length > 0) {
                            long length = file.length();
                            isRange = true;
                            start = Long.parseLong(bounds[0]);
                            end = bounds.length > 1 ? Long.parseLong(bounds[1]) : length - 1;
                            //Content-Range: bytes 229376-232468/232469
                            rr.getHeaders().addHeader(CONTENT_RANGE_HEADER,
                                    String.format("%s %d-%d/%d", RANGES_BYTES, start, end, length));
                        }
                    }
                    rr.setContentLength(getChunkLength());
                    rr.setWriteCallback(new WriteCallback() {
                        @Override
                        public void writeData(Attributes attributes) throws IOException {
                            IResourceStream rStream = getResourceStream();
                            try {
                                writeStream(attributes, rStream.getInputStream());
                            } catch (ResourceStreamNotFoundException e1) {
                            } catch (ResponseIOException e) {
                                // in case of range operations we expecting such exceptions
                                if (!isRange) {
                                    log.error("Error while playing the stream", e);
                                }
                            } finally {
                                rStream.close();
                            }
                        }
                    });
                } else {
                    rr.setError(HttpServletResponse.SC_NOT_FOUND);
                }
                return rr;
            }
        };
    }

    abstract String getContentType();

    abstract String getFileName(FlvRecording r);

    abstract File getFile(FlvRecording r);

    private Long getLong(StringValue id) {
        Long result = null;
        try {
            result = id.toLongObject();
        } catch (Exception e) {
            //no-op
        }
        return result;
    }

    private FlvRecording getRecording(Long id) {
        FlvRecording r = getBean(FlvRecordingDao.class).get(id);
        // TODO should we process public?
        // || r.getOwnerId() == 0 || r.getParentFileExplorerItemId() == null || r.getParentFileExplorerItemId() == 0
        if (r == null) {
            return r;
        }
        if (r.getOwnerId() == null || getUserId() == r.getOwnerId()) {
            return r;
        }
        if (getBean(OrganisationUserDao.class).isUserInOrganization(r.getOrganization_id(), getUserId())) {
            return r;
        }
        //TODO external group check was added for plugin recording download
        String extType = getExternalType();
        if (extType != null && extType.equals(r.getCreator().getExternalUserType())) {
            return r;
        }
        return null;
    }

    private FlvRecording getRecording(Attributes attributes) {
        PageParameters params = attributes.getParameters();
        StringValue idStr = params.get("id");
        Long id = getLong(idStr);
        WebSession ws = WebSession.get();
        if (id != null && ws.isSignedIn()) {
            return getRecording(id);
        } else {
            ws.invalidate();
            if (ws.signIn(idStr.toString())) {
                return getRecording(getRecordingId());
            }
        }
        return null;
    }
}