package org.bubblecloud.ilves.module.content;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.bubblecloud.ilves.cache.InMemoryCache;
import org.bubblecloud.ilves.cache.PrivilegeCache;
import org.bubblecloud.ilves.model.Company;
import org.bubblecloud.ilves.model.Group;
import org.bubblecloud.ilves.model.User;
import org.bubblecloud.ilves.security.CompanyDao;
import org.bubblecloud.ilves.site.DefaultSiteUI;
import org.bubblecloud.ilves.util.PropertiesUtil;
import javax.persistence.EntityManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Servlet for sharing assets.
*
* @author Tommi S.E. Laukkanen
*/
public class AssetServlet extends HttpServlet {
/** The logger. */
private static final Logger LOGGER = Logger.getLogger(AssetServlet.class);
private static Map<Company, InMemoryCache<String, Asset>> nameAssetCache =
new HashMap<Company, InMemoryCache<String, Asset>>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// Allocate entity manager.
final EntityManager entityManager = DefaultSiteUI.getEntityManagerFactory().createEntityManager();
// Find user and groups from session or assume anonymous.
final User user = (User) req.getSession().getAttribute("user");
final List<Group> groups = (List<Group>) req.getSession().getAttribute("groups");
// Find company object either from user object, session or load from database.
Company company = null;
if (user != null) {
company = user.getOwner();
} else {
company = (Company) req.getSession().getAttribute("company");
if (company == null) {
company = CompanyDao.getCompany(entityManager, req.getServerName());
req.getSession().setAttribute("company", company);
}
if (company == null) {
company = CompanyDao.getCompany(entityManager, "*");
req.getSession().setAttribute("company", company);
}
}
// Find Asset object based on name either from cache or database.
final String name = req.getRequestURI().substring(req.getRequestURI().lastIndexOf('/') + 1);
if (!nameAssetCache.containsKey(company)) {
nameAssetCache.put(company, new InMemoryCache<String, Asset>(
10 * 60 * 1000, 60 * 1000, 1000));
}
Asset asset = nameAssetCache.get(company).get(name);
if (asset == null) {
asset = ContentDao.getAsset(entityManager, company, name);
nameAssetCache.get(company).put(name, asset);
}
if (asset == null) {
resp.setStatus(404);
return;
}
if (!PrivilegeCache.hasPrivilege(entityManager, company, user, groups, "view", asset.getAssetId())) {
resp.setStatus(401);
return;
}
// Load asset from file cache if exists and not modified before last modified of the asset file.
final String assetCachePath = PropertiesUtil.getProperty("site", "asset-cache-path");
final File assetCache = new File(assetCachePath);
if (!assetCache.exists()) {
assetCache.mkdir();
}
final String assetCacheFilePath = assetCache.getCanonicalPath() + File.separator + asset.getAssetId();
final File assetCacheFile = new File(assetCacheFilePath);
if (assetCacheFile.exists()) {
if (assetCacheFile.lastModified() < asset.getModified().getTime()) {
assetCacheFile.delete();
}
}
if (!assetCacheFile.exists()) {
entityManager.getTransaction().begin();
try {
final Connection connection = entityManager.unwrap(Connection.class);
final PreparedStatement preparedStatement = connection.prepareStatement(
"SELECT data FROM asset WHERE assetid = ?");
preparedStatement.setString(1, asset.getAssetId());
final ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
final InputStream inputStream = resultSet.getBinaryStream(1);
final FileOutputStream outputStream = new FileOutputStream(assetCacheFile);
IOUtils.copy(inputStream, outputStream);
inputStream.close();
outputStream.close();
}
resultSet.close();
preparedStatement.close();
entityManager.getTransaction().rollback();
} catch (final Exception e) {
LOGGER.error("Error reading asset from database.", e);
resp.setStatus(500);
} finally {
if (entityManager.getTransaction().isActive()) {
entityManager.getTransaction().rollback();
}
}
}
if (assetCacheFile.exists()) {
final int cacheAgeSeconds = 3600;
final long expiry = System.currentTimeMillis() + cacheAgeSeconds * 1000;
resp.setDateHeader("Expires", expiry);
resp.setHeader("Cache-Control", "max-age="+ cacheAgeSeconds);
resp.setContentType(asset.getType());
resp.setContentLength(asset.getSize());
final FileInputStream inputStream = new FileInputStream(assetCacheFile);
IOUtils.copy(inputStream, resp.getOutputStream());
inputStream.close();
resp.setStatus(200);
} else {
resp.setStatus(500);
}
}
}