/**
* This file Copyright (c) 2005-2010 Aptana, Inc. This program is
* dual-licensed under both the Aptana Public License and the GNU General
* Public license. You may elect to use one or the other of these licenses.
*
* This program is distributed in the hope that it will be useful, but
* AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or
* NONINFRINGEMENT. Redistribution, except as permitted by whichever of
* the GPL or APL you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or modify this
* program under the terms of the GNU General Public License,
* Version 3, as published by the Free Software Foundation. You should
* have received a copy of the GNU General Public License, Version 3 along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Aptana provides a special exception to allow redistribution of this file
* with certain Eclipse Public Licensed code and certain additional terms
* pursuant to Section 7 of the GPL. You may view the exception and these
* terms on the web at http://www.aptana.com/legal/gpl/.
*
* 2. For the Aptana Public License (APL), this program and the
* accompanying materials are made available under the terms of the APL
* v1.0 which accompanies this distribution, and is available at
* http://www.aptana.com/legal/apl/.
*
* You may view the GPL, Aptana's exception and additional terms, and the
* APL in the file titled license.html at the root of the corresponding
* plugin containing this source file.
*
* Any modifications to this file must keep this entire header intact.
*/
package com.aptana.ide.server.jetty.portal;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.aptana.ide.core.FileUtils;
import com.aptana.ide.core.IdeLog;
import com.aptana.ide.core.db.AptanaDB;
import com.aptana.ide.core.online.OnlineDetectionService;
import com.aptana.ide.core.online.OnlineDetectionService.StatusMode;
import com.aptana.ide.core.ui.CoreUIPlugin;
import com.aptana.ide.core.ui.Messages;
import com.aptana.ide.server.jetty.JettyPlugin;
/**
* This servlet is used to grab content from a url passed via a query parameter. The response of this servlet is the
* body and status code of the url streamed.
*
* @author Kevin Sawicki (ksawicki@aptana.com)
*/
public class PortalProxyServlet extends HttpServlet
{
/**
* serialVersionUID
*/
private static final long serialVersionUID = 1L;
/**
* TIMEOUT
*/
public static final int TIMEOUT = 30000;
/**
* URL
*/
public static final String URL = "url"; //$NON-NLS-1$
/**
* CLEAR_CACHE Specifies whether the cache should be cleared, a 'true' value will do this
*/
public static final String CLEAR_CACHE = "clearCache"; //$NON-NLS-1$
private static final String NO_CACHE = "noCache"; //$NON-NLS-1$
/**
*
*/
public PortalProxyServlet()
{
initCache();
}
class RemoteContents
{
long lastModifiedDate = 0;
byte[] data = null;
}
/**
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
* javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String address = request.getParameter(URL);
String clearCache = request.getParameter(CLEAR_CACHE);
String noCache = request.getParameter(NO_CACHE);
if (clearCache != null && clearCache.equals("true")) //$NON-NLS-1$
{
clearCache();
}
if (address != null)
{
// If we are offline, then we'll try serving from cache
if (OnlineDetectionService.getInstance().getStatus() == StatusMode.OFFLINE)
{
serveFromCache(response, address);
return;
}
// If we are online (or unknown state), we'll try to get the file, if it fails, then we'll try from cache
RemoteContents remoteContents = null;
try
{
long lastModifiedDate = -1; //getModifiedDateFromCache(address);
remoteContents = getRemoteContents(lastModifiedDate, address);
}
catch (Exception e)
{
e.printStackTrace();
remoteContents = null;
}
// If we couldn't fetch the remote contents, let's try serving it from cache
if (remoteContents == null)
{
serveFromCache(response, address);
return;
}
// Serve the file out
response.setStatus(HttpURLConnection.HTTP_OK);
response.setContentLength(remoteContents.data.length);
FileUtils.pipe(new ByteArrayInputStream(remoteContents.data), response.getOutputStream(), true);
// Only cache if the noCache option is NOT true (it doesn't exist usually for false state)
if ("true".equals(noCache) == false) //$NON-NLS-1$
{
// Save the file to the cache
saveToCache(address, remoteContents);
}
}
}
private RemoteContents getRemoteContents(long lastModifiedDate, String address) throws MalformedURLException, IOException,
ProtocolException
{
URL url = new URL(address);
URLConnection connection = url.openConnection();
if (connection instanceof HttpURLConnection)
{
connection.setReadTimeout(TIMEOUT);
connection.setDoInput(true);
long remoteLastModified = 0;
/* TODO
if (lastModifiedDate != -1)
{
((HttpURLConnection) connection).setRequestMethod("GET");
((HttpURLConnection) connection).addRequestProperty("Last-Modified", // TODO
connection.connect();
remoteLastModified = connection.getLastModified();
Map fields = ((HttpURLConnection) connection).getHeaderFields();
if(remoteLastModified <= lastModifiedDate)
{
return getFromCache(address);
}
else
{
connection = url.openConnection();
}
}
*/
((HttpURLConnection) connection).setRequestMethod("GET"); //$NON-NLS-1$
((HttpURLConnection) connection).connect();
// Copy the contents to memory first
ByteArrayOutputStream fileContents = new ByteArrayOutputStream();
FileUtils.pipe(connection.getInputStream(), fileContents, true);
RemoteContents contents = new RemoteContents();
contents.data = fileContents.toByteArray();
contents.lastModifiedDate = remoteLastModified;
return contents;
}
return null;
}
private void serveFromCache(HttpServletResponse response, String address) throws IOException
{
RemoteContents contents = getFromCache(address);
if (contents != null && contents.data != null)
{
FileUtils.pipe(new ByteArrayInputStream(contents.data), response.getOutputStream(), true);
response.setStatus(HttpURLConnection.HTTP_OK);
}
else
{
response.setStatus(HttpURLConnection.HTTP_UNAVAILABLE);
}
}
private RemoteContents getFromCache(String address)
{
AptanaDB db = AptanaDB.getInstance();
Connection conn = db.getConnection();
if (conn != null)
{
Statement s;
try
{
String addr = java.net.URLEncoder.encode(address, "UTF-8"); //$NON-NLS-1$
s = conn.createStatement();
ResultSet rs = s.executeQuery("SELECT * FROM PROXY_CACHE WHERE address='" + addr + "'"); //$NON-NLS-1$ //$NON-NLS-2$
boolean result = rs.next();
if (result == true)
{
RemoteContents contents = new RemoteContents();
contents.data = rs.getBytes("data"); //$NON-NLS-1$
contents.lastModifiedDate = rs.getLong("lastmodified"); //$NON-NLS-1$
return contents;
}
}
catch (SQLException e)
{
e.printStackTrace();
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
finally
{
try
{
conn.close();
}
catch (SQLException e)
{
}
}
}
return null;
}
/**
*
*/
private void initCache()
{
try
{
if (checkTable() == false)
{
dropTable();
AptanaDB
.getInstance()
.execute(
"CREATE TABLE PROXY_CACHE (lastmodified BIGINT, address varchar(1024) PRIMARY KEY, data BLOB(50000))"); //$NON-NLS-1$
}
}
catch (SQLException e)
{
IdeLog.logError(CoreUIPlugin.getDefault(), Messages.ImageUtils_ErrorInitializingDbConnection, e);
if (e.getNextException() != null)
{
IdeLog.logError(CoreUIPlugin.getDefault(), Messages.ImageUtils_ErrorInitializingDbConnection, e
.getNextException());
}
}
}
/**
* @return
* @throws SQLException
*/
private static boolean checkTable() throws SQLException
{
Connection conn = null;
Statement s = null;
boolean result = true;
try
{
conn = AptanaDB.getInstance().getConnection();
if (conn == null)
{
SQLException sqle = new SQLException("Connection to AptanaDB is null."); //$NON-NLS-1$
IdeLog.logError(JettyPlugin.getDefault(), sqle.getMessage());
throw sqle;
}
else
{
s = conn.createStatement();
s.execute("select address FROM PROXY_CACHE where lastmodified = 0"); //$NON-NLS-1$
}
}
catch (SQLException sqle)
{
String theError = (sqle).getSQLState();
/** If table exists will get - WARNING 02000: No row was found * */
if (theError != null && theError.equals("42X05")) // Table does not exist //$NON-NLS-1$
{
result = false;
}
else if (theError != null && theError.equals("42X04")) // The column does not exist //$NON-NLS-1$
{
result = false;
}
else if (theError != null && (theError.equals("42X14") || theError.equals("42821"))) //$NON-NLS-1$ //$NON-NLS-2$
{
IdeLog.logError(JettyPlugin.getDefault(), "IncorrectTableDefinition: " + sqle.getMessage()); //$NON-NLS-1$
throw sqle;
}
else
{
IdeLog.logError(JettyPlugin.getDefault(), "SQLException: " + sqle.getMessage()); //$NON-NLS-1$
throw sqle;
}
}
finally
{
if (s != null)
{
s.close();
}
if (conn != null)
{
conn.close();
}
}
return result;
}
private void clearCache()
{
AptanaDB.getInstance().execute("TRUNCATE PROXY_CACHE"); //$NON-NLS-1$
}
private void dropTable()
{
try
{
AptanaDB.getInstance().execute("DROP TABLE PROXY_CACHE"); //$NON-NLS-1$
}
catch (Exception e)
{
// ignore the failure in case we're dropping a table that does not exist
}
}
private void saveToCache(String address, RemoteContents contents)
{
Connection conn = null;
PreparedStatement prepStmt = null;
String sql = "insert into PROXY_CACHE (lastmodified, address, data) values (?,?,?)"; //$NON-NLS-1$
try
{
conn = AptanaDB.getInstance().getConnection();
if (conn != null)
{
prepStmt = conn.prepareStatement(sql);
prepStmt.setLong(1, contents.lastModifiedDate);
prepStmt.setString(2, URLEncoder.encode(address, "UTF-8")); //$NON-NLS-1$
prepStmt.setBytes(3, contents.data);
prepStmt.executeUpdate();
}
}
catch (Exception e)
{
try
{
sql = "update PROXY_CACHE set lastmodified = ?, data = ? where address = ?"; //$NON-NLS-1$
conn = AptanaDB.getInstance().getConnection();
if (conn != null)
{
prepStmt = conn.prepareStatement(sql);
prepStmt.setLong(1, contents.lastModifiedDate);
prepStmt.setBytes(2, contents.data);
prepStmt.setString(3, URLEncoder.encode(address, "UTF-8")); //$NON-NLS-1$
prepStmt.executeUpdate();
}
}
catch (Exception e2)
{
}
}
finally
{
try
{
if (prepStmt != null)
{
prepStmt.close();
}
if (conn != null)
{
conn.close();
}
}
catch (Exception e3)
{
}
}
}
}