Java tutorial
/*************************************************************** * This file is part of the [fleXive](R) framework. * * Copyright (c) 1999-2014 * UCS - unique computing solutions gmbh (http://www.ucs.at) * All rights reserved * * The [fleXive](R) project is free software; you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License version 2.1 or higher as published by the Free Software Foundation. * * The GNU Lesser General Public License can be found at * http://www.gnu.org/licenses/lgpl.html. * A copy is found in the textfile LGPL.txt and important notices to the * license from the author are found in LICENSE.txt distributed with * these libraries. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For further information about UCS - unique computing solutions gmbh, * please see the company website: http://www.ucs.at * * For further information about [fleXive](R), please see the * project website: http://www.flexive.org * * * This copyright notice MUST APPEAR in all copies of the file! ***************************************************************/ package com.flexive.shared.stream; import com.flexive.shared.CacheAdmin; import com.flexive.shared.FxSharedUtils; import com.flexive.shared.exceptions.FxApplicationException; import com.flexive.shared.exceptions.FxStreamException; import com.flexive.shared.media.FxMediaEngine; import com.flexive.shared.media.FxMediaSelector; import com.flexive.shared.media.impl.FxMimeType; import com.flexive.shared.value.BinaryDescriptor; import com.flexive.stream.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** * Utility class to access the StreamServer * * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at) */ public class FxStreamUtils { private static final Log LOG = LogFactory.getLog(FxStreamUtils.class); /** * 2 hours default time to live */ public final static long DEFAULT_TTL = 120 * 60 * 1000; /** * List of local servers */ private static final CopyOnWriteArrayList<ServerLocation> localServerLocations = new CopyOnWriteArrayList<ServerLocation>(); /** * Add a local server * * @param serverLocation local server location */ public static void addLocalServer(ServerLocation serverLocation) { localServerLocations.addIfAbsent(serverLocation); } /** * Upload a binary (using an OutputStream) to the StreamServer with a default time to live * * @param length expected length of the stream/binary * @param stream the Stream containing the binary * @return payload containing server side handle of the binary, mimeType and meta data * @throws FxStreamException on errors * @see FxStreamUtils#DEFAULT_TTL */ public static BinaryUploadPayload uploadBinary(long length, InputStream stream) throws FxStreamException { return uploadBinary(length, stream, DEFAULT_TTL, FxMimeType.UNKNOWN); } /** * Upload a binary (using an OutputStream) to the StreamServer with a default time to live * * @param length expected length of the stream/binary * @param stream the Stream containing the binary * @param mimeType the mime type to use if auto-detection fails * @return payload containing server side handle of the binary, mimeType and meta data * @throws FxStreamException on errors * @see FxStreamUtils#DEFAULT_TTL */ public static BinaryUploadPayload uploadBinary(long length, InputStream stream, String mimeType) throws FxStreamException { return uploadBinary(length, stream, DEFAULT_TTL, mimeType); } /** * Retrieve a StreamClient, local servers are preferred, remote only as fallback. * Remote servers will be fetched from the cache * * @return StreamClient * @throws FxStreamException on errors */ public static StreamClient getClient() throws FxStreamException { return getClient(null); } /** * Retrieve a StreamClient, local servers are preferred, remote only as fallback * * @param servers (optional) known remote servers * @return StreamClient * @throws FxStreamException on errors */ public static StreamClient getClient(List<ServerLocation> servers) throws FxStreamException { if (servers == null) servers = CacheAdmin.getStreamServers(); try { StreamClient client; List<ServerLocation> allServers = new ArrayList<ServerLocation>( servers.size() + localServerLocations.size()); allServers.addAll(localServerLocations); allServers.addAll(servers); if (allServers.isEmpty()) throw new FxStreamException("ex.stream.download.param.server"); client = StreamClientFactory.getClient(allServers); return client; } catch (StreamException e) { throw new FxStreamException(e); } } /** * Upload a binary (using an OutputStream) to the StreamServer with a given time to live. * Warning: if using a remote connection, this method will return a few miliseconds before * all binary data is stored in the DB! Local connected clients will return *after* all * data is stored. This is currently a 'feature' that might be fixed sometime. * * @param length expected length of the stream/binary * @param stream the Stream containing the binary * @param timeToLive time in miliseconds the binary is guaranteed to exist server side (will be removed once expired) * @param mimeType the mime type to use if auto-detection fails * @return payload containing server side handle of the binary, mimeType and meta data * @throws FxStreamException on errors * @since 3.1 */ public static BinaryUploadPayload uploadBinary(long length, InputStream stream, long timeToLive, String mimeType) throws FxStreamException { StreamClient client = null; try { client = getClient(); DataPacket<BinaryUploadPayload> req = new DataPacket<BinaryUploadPayload>( new BinaryUploadPayload(length, timeToLive, mimeType), true); DataPacket<BinaryUploadPayload> resp = client.connect(req); if (resp.getPayload().isServerError()) throw new FxStreamException("ex.stream.serverError", resp.getPayload().getErrorMessage()); if (resp.isExpectStream()) { long actualLength; if (length != -1L) actualLength = client.sendStream(stream, length); else actualLength = client.sendStream(stream); resp.getPayload().setActualLength(actualLength); } client.close(); client = null; return resp.getPayload(); } catch (StreamException e) { throw wrapStreamException(e); } finally { try { if (client != null) client.close(); } catch (StreamException e) { //ignore } } } /** * Probe all network interfaces and return the most suited to run a StreamServer on. * Preferred are interfaces that are not site local. * * @return best suited host to run a StreamServer * @throws UnknownHostException on errors */ public static InetAddress probeNetworkInterfaces() throws UnknownHostException { try { final String forcedAddress = System.getProperty("FxStreamIP"); if (forcedAddress != null) { try { InetAddress ad = InetAddress.getByName(forcedAddress); LOG.info("Binding [fleXive] streamserver to forced address [" + forcedAddress + "] ..."); return ad; } catch (UnknownHostException e) { LOG.error("Forced [fleXive] streamserver bind address [" + forcedAddress + "] can not be used: " + e.getMessage() + " - probing available network interfaces ..."); } } Enumeration<NetworkInterface> nifs = NetworkInterface.getNetworkInterfaces(); NetworkInterface nif; InetAddress preferred = null, fallback = null; while (nifs.hasMoreElements()) { nif = nifs.nextElement(); if (LOG.isDebugEnabled()) LOG.debug("Probing " + nif.getDisplayName() + " ..."); if (nif.getDisplayName().startsWith("vmnet") || nif.getDisplayName().startsWith("vnet")) continue; Enumeration<InetAddress> inas = nif.getInetAddresses(); while (inas.hasMoreElements()) { InetAddress na = inas.nextElement(); if (LOG.isDebugEnabled()) LOG.debug("Probing " + nif.getDisplayName() + na); if (!(na instanceof Inet4Address)) continue; if (!na.isLoopbackAddress() && na.isReachable(1000)) { if (preferred == null || (preferred.isSiteLocalAddress() && !na.isSiteLocalAddress())) preferred = na; } if (fallback == null && na.isLoopbackAddress()) fallback = na; } } if (LOG.isDebugEnabled()) LOG.debug("preferred: " + preferred + " fallback: " + fallback); if (preferred != null) return preferred; if (fallback != null) return fallback; return InetAddress.getLocalHost(); } catch (Exception e) { return InetAddress.getLocalHost(); } } /** * Download a binary * * @param callback callback handler to set mimetype and size for downloads * @param server (optional) list of remote stream servers * @param stream the output stream to send the binary to * @param binaryId id of the binary * @param selector selector of binary to use and for optional manipulations to perform * @throws FxStreamException on errors */ @SuppressWarnings({ "UnusedAssignment" }) public static void downloadBinary(BinaryDownloadCallback callback, List<ServerLocation> server, OutputStream stream, long binaryId, FxMediaSelector selector) throws FxStreamException { FxSharedUtils.checkParameterEmpty(stream, "stream"); StreamClient client = null; try { client = getClient(server); DataPacket<BinaryDownloadPayload> req = new DataPacket<BinaryDownloadPayload>(new BinaryDownloadPayload( binaryId, 1, 1, selector.getSize().getBlobIndex(), selector.isForceImage()), true, true); DataPacket<BinaryDownloadPayload> resp = client.connect(req); if (resp.getPayload().isServerError()) throw new FxStreamException("ex.stream.serverError", resp.getPayload().getErrorMessage()); if (!selector.isApplyManipulations()) { if (callback != null) { callback.setMimeType(resp.getPayload().getMimeType()); callback.setBinarySize(resp.getPayload().getDatasize()); } client.receiveStream(stream); client.close(); } else { //perform on-the-fly modifications ByteArrayOutputStream _out = new ByteArrayOutputStream(resp.getPayload().getDatasize()); client.receiveStream(_out); client.close(); byte[] _data = _out.toByteArray(); _out = null; try { FxMediaEngine.streamingManipulate(_data, stream, callback, resp.getPayload().getMimeType(), selector); } catch (FxApplicationException e) { throw new FxStreamException(e); } } client = null; } catch (StreamException e) { throw wrapStreamException(e); } finally { try { if (client != null) client.close(); } catch (StreamException e) { //ignore } } } /** * Download a binary * * @param server (optional) list of remote stream servers * @param stream the output stream to send the binary to * @param descriptor binary descriptor * @throws FxStreamException on errors */ public static void downloadBinary(List<ServerLocation> server, OutputStream stream, BinaryDescriptor descriptor) throws FxStreamException { downloadBinary(server, stream, descriptor, BinaryDescriptor.PreviewSizes.ORIGINAL); } /** * Return an InputStream for reading the binary. * * @param server (optional) list of remote stream servers * @param descriptor binary descriptor * @param size preview size * @return an InputStream for reading the binary. * @throws FxStreamException on errors * @since 3.1 */ public static InputStream getBinaryStream(List<ServerLocation> server, BinaryDescriptor descriptor, BinaryDescriptor.PreviewSizes size) throws FxStreamException { try { return requestBinary(server, descriptor, size).getInputStream(); } catch (StreamException e) { throw wrapStreamException(e); } } /** * Return an InputStream for reading the binary. * * @param descriptor binary descriptor * @param size preview size * @return an InputStream for reading the binary. * @throws FxStreamException on errors * @since 3.1 */ public static InputStream getBinaryStream(BinaryDescriptor descriptor, BinaryDescriptor.PreviewSizes size) throws FxStreamException { return getBinaryStream(null, descriptor, size); } /** * Download a binary with given size (Original, Screenview, Preview 1..3) * * @param server (optional) list of remote stream servers * @param stream the output stream to send the binary to * @param descriptor binary descriptor * @param size preview size * @throws FxStreamException on errors */ public static void downloadBinary(List<ServerLocation> server, OutputStream stream, BinaryDescriptor descriptor, BinaryDescriptor.PreviewSizes size) throws FxStreamException { if (stream == null) throw new FxStreamException("ex.stream.download.param.missing"); if (descriptor.getSize() <= 0) return; //no need to request a 0-byte stream StreamClient client = null; try { client = requestBinary(server, descriptor, size); client.receiveStream(stream, descriptor.getSize()); client.close(); client = null; } catch (StreamException e) { throw wrapStreamException(e); } finally { try { if (client != null) client.close(); } catch (StreamException e) { //ignore } } } protected static FxStreamException wrapStreamException(StreamException e) { return new FxStreamException(e.getMessage(), e); } private static StreamClient requestBinary(List<ServerLocation> server, BinaryDescriptor descriptor, BinaryDescriptor.PreviewSizes size) throws FxStreamException, StreamException { StreamClient client; client = getClient(server); DataPacket<BinaryDownloadPayload> req = new DataPacket<BinaryDownloadPayload>( new BinaryDownloadPayload(descriptor.getId(), descriptor.getVersion(), descriptor.getQuality(), size.getBlobIndex(), false), true, true); DataPacket<BinaryDownloadPayload> resp = client.connect(req); if (resp.getPayload().isServerError()) throw new FxStreamException("ex.stream.serverError", resp.getPayload().getErrorMessage()); return client; } }