Java tutorial
/* * IRIS -- Intelligent Roadway Information System * Copyright (C) 2003-2013 Minnesota Department of Transportation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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. */ package us.mn.state.dot.tms.client.camera; import java.awt.Dimension; import java.awt.Image; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JLabel; import org.apache.commons.net.ftp.FTPFile; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFileFilter; import us.mn.state.dot.sched.Job; import us.mn.state.dot.sched.Scheduler; import us.mn.state.dot.tms.Camera; import us.mn.state.dot.tms.StreamType; import us.mn.state.dot.tms.utils.I18N; import static us.mn.state.dot.tms.client.widget.Widgets.UI; /** * A video stream which reads a JPG or PNG still image from an FTP server. * * @author Michael Janson */ public class FTPStream implements VideoStream { /** Default timeout for direct URL Connections */ static protected final int TIMEOUT_DIRECT = 5 * 1000; /** Invalid Username or Password */ static private final int INVALID_USERNAME_PASSWORD = 430; /** Valid Username, Passwork Required */ static private final int INVALID_PASSWORD = 331; /** File Not Found or Insufficient Access */ static private final int FILE_NOT_FOUND = 550; /** Not logged in */ static private final int NOT_LOGGED_IN = 530; /** Label to display video stream */ private final JLabel screen = new JLabel(); /** Requested video size */ private final Dimension size; /** Input stream to read */ private InputStream stream; /** Count of rendered frames */ private int n_frames = 0; /** Flag to continue running stream */ private boolean running = true; /** Stream error message */ private String error_msg = null; private FTPClient ftpClient; private FTPFile ftpFile; private String ftpUsername; private String ftpPassword; private String ip; private int port; private String ftpPath; private boolean same_filename; private String baseUrl; private String ftp_filename; private FTPFileFilter ftpFilter; /** Set the stream error message */ protected void setErrorMsg(String e) { if (error_msg == null) error_msg = e; } /** Create a new JPEG stream * @throws IOException */ public FTPStream(Scheduler s, VideoRequest req, Camera c) throws IOException { size = UI.dimension(req.getSize().width, req.getSize().height); ftpClient = new FTPClient(); /** Get ip and port from encoder field */ baseUrl = c.getEncoder(); String[] params = baseUrl.split(":"); ip = params[0]; /** Default port is 80 if no port is provided */ if (params.length > 1) { port = Integer.parseInt(params[1]); } else { port = 80; } /** Get base directory of images, provide root (/) if null or blank */ ftpPath = c.getFtpPath(); if (ftpPath == null || ftpPath.trim().equals("")) { ftpPath = "/"; } /** Get filename if static image path */ same_filename = c.getSameFilename(); if (same_filename) { ftp_filename = c.getFtpFilename(); } /** Use anonymous with blank password if username field is left blank */ ftpUsername = c.getFtpUsername(); ftpPassword = c.getFtpPassword(); if (ftpUsername == null || ftpUsername.trim().equals("")) { ftpUsername = "anonomymous"; ftpPassword = ""; } ftpFilter = new FTPFileFilter() { @Override public boolean accept(FTPFile ftpFile) { return (ftpFile.isFile() && (ftpFile.getName().endsWith(".jpg") || ftpFile.getName().endsWith(".png"))); } }; /** Get the FTP Image */ getFtpImage(); /** Create thread to read image based on refresh interval */ /** @throws IOException */ final Job job = new Job(Calendar.MINUTE, c.getRefInterval()) { public void perform() throws IOException { if (running) { getFtpImage(); } } public boolean isRepeating() { return running; } }; s.addJob(job); } /** Create an input stream from an HTTP connection */ /** @throws IOException */ protected InputStream createInputStream(URL url) throws IOException { URLConnection c = url.openConnection(); c.setConnectTimeout(TIMEOUT_DIRECT); c.setReadTimeout(TIMEOUT_DIRECT); return c.getInputStream(); } protected FTPFile getMaxLastModified(FTPFile[] ftpFiles) { return Collections.max(Arrays.asList(ftpFiles), new FTPComparator()); } /** Get the FTP Image */ /** @throws IOException */ private void getFtpImage() throws IOException { /** Try connection 3 times before throwing exception */ for (int i = 0, n = 3; i < n; i++) { try { /** Connect to FTP server and change to image directory */ connectFTP(); /** Get either most recent image or filename provided */ getFTPFile(); /** Build URL for URL Connection Inputstream */ URL url = new URL( "ftp://" + ftpUsername + ":" + ftpPassword + "@" + baseUrl + ftpPath + ftpFile.getName()); /** Create URL Connection inputstream */ stream = createInputStream(url); /** Read inputstream */ readStream(); /** Close inputstream */ stream.close(); break; } catch (IOException e) { if (i == 2) { throw new IOException(I18N.get(getFtpStatus())); } } catch (Exception e) { throw new IOException(I18N.get(getFtpStatus())); } } } /** Connect to FTP server using credentials provided and change to image directory */ /** @throws IOException */ private void connectFTP() throws IOException { /** Connect to FTP server */ ftpClient.connect(ip, port); ftpClient.login(ftpUsername, ftpPassword); /** Change to base directory */ ftpClient.changeWorkingDirectory(ftpPath); } /** Is the image name constant or does it increment If it increments, grab most recent file, otherwise just use filename provided */ /** @throws IOException */ private void getFTPFile() throws IOException { if (same_filename) { ftpFile = ftpClient.listFiles(ftp_filename)[0]; } else { ftpFile = getMaxLastModified(ftpClient.listFiles("", ftpFilter)); } } /** Read a video stream */ private void readStream() { try { byte[] idata = getImage(); screen.setIcon(createIcon(idata)); } catch (IOException e) { setErrorMsg(e.getMessage()); screen.setIcon(null); running = false; } } /** Get the next image in the jpeg stream */ /** @throws IOException */ protected byte[] getImage() throws IOException { int n_size = (int) ftpFile.getSize(); byte[] image = new byte[n_size]; int n_bytes = 0; while (n_bytes < n_size) { int r = stream.read(image, n_bytes, n_size - n_bytes); if (r >= 0) n_bytes += r; else throw new IOException("End of stream"); } n_frames++; return image; } /** Create an image icon from image data */ protected ImageIcon createIcon(byte[] idata) { ImageIcon icon = new ImageIcon(idata); if (icon.getIconWidth() == size.width && icon.getIconHeight() == size.height) return icon; Image im = icon.getImage().getScaledInstance(size.width, size.height, Image.SCALE_FAST); return new ImageIcon(im); } /** Get a component for displaying the video stream */ public JComponent getComponent() { return screen; } /** Get the status of the stream */ public String getStatus() { String e = error_msg; if (e != null) return e; else return StreamType.FTP.toString(); } /** Test if the video is playing */ public boolean isPlaying() { return running; } /** Dispose of the video stream */ public void dispose() { running = false; try { if (stream != null) { stream.close(); } } catch (IOException e) { setErrorMsg(e.getMessage()); } screen.setIcon(null); } /** Get status of FTP correspondence */ public String getFtpStatus() { int c = ftpClient.getReplyCode(); switch (c) { case INVALID_USERNAME_PASSWORD: return "camera.ftp.errorform1"; case INVALID_PASSWORD: return "camera.ftp.errorform2"; case FILE_NOT_FOUND: return "camera.ftp.errorform3"; case NOT_LOGGED_IN: return "camera.ftp.errorform4"; default: return "camera.ftp.errorform5"; } } }