
org.broadleafcommerce.cms.file.service.StaticAssetStorageServiceImpl Maven / Gradle / Ivy
/*
* Copyright 2008-2012 the original author or authors.
*
* Licensed 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.broadleafcommerce.cms.file.service;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.broadleafcommerce.cms.file.dao.StaticAssetStorageDao;
import org.broadleafcommerce.cms.file.domain.StaticAsset;
import org.broadleafcommerce.cms.file.domain.StaticAssetStorage;
import org.broadleafcommerce.cms.file.service.operation.NamedOperationManager;
import org.broadleafcommerce.common.sandbox.domain.SandBox;
import org.broadleafcommerce.openadmin.server.service.artifact.ArtifactService;
import org.broadleafcommerce.openadmin.server.service.artifact.image.Operation;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Blob;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Jeff Fischer
*/
@Service("blStaticAssetStorageService")
public class StaticAssetStorageServiceImpl implements StaticAssetStorageService {
public static class CleanupOperation {
private String assetName;
private File cacheFile;
public String getAssetName() {
return assetName;
}
public void setAssetName(String assetName) {
this.assetName = assetName;
}
public File getCacheFile() {
return cacheFile;
}
public void setCacheFile(File cacheFile) {
this.cacheFile = cacheFile;
}
}
private static final Log LOG = LogFactory.getLog(StaticAssetStorageServiceImpl.class);
private static final File DEFAULTCACHEDIRECTORY = new File(System.getProperty("java.io.tmpdir"));
protected String cacheDirectory;
protected boolean cleanupThreadEnabled = true;
private final List operations = new ArrayList(100);
@Resource(name="blStaticAssetService")
protected StaticAssetService staticAssetService;
@Resource(name="blArtifactService")
protected ArtifactService artifactService;
@Resource(name="blStaticAssetStorageDao")
protected StaticAssetStorageDao staticAssetStorageDao;
@Resource(name="blNamedOperationManager")
protected NamedOperationManager namedOperationManager;
protected Thread cleanupThread = new Thread(new Runnable() {
@Override
public void run() {
checkState: {
while (cleanupThreadEnabled) {
try {
List myList;
synchronized (operations) {
myList = new ArrayList(operations.size());
myList.addAll(operations);
operations.clear();
}
for (final StaticAssetStorageServiceImpl.CleanupOperation operation : myList) {
if (!cleanupThreadEnabled) {
break checkState;
}
File parentDir = operation.cacheFile.getParentFile();
if (parentDir.exists()) {
File[] obsoleteFiles = parentDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file, String s) {
return s.startsWith(operation.assetName + "---") && !operation.getCacheFile().getName().equals(s);
}
});
if (obsoleteFiles != null) {
for (File file : obsoleteFiles) {
if (LOG.isDebugEnabled()) {
LOG.debug("Deleting obsolete asset cache file: " + file.getAbsolutePath());
}
try {
if (!file.delete()) {
LOG.warn("Unable to cleanup obsolete static file: " + file.getAbsolutePath());
}
} catch (Exception e) {
//do nothing
}
}
}
}
try {
Thread.sleep(1000);
} catch (Exception e) {
//do nothing
}
if (!cleanupThreadEnabled) {
break checkState;
}
}
} catch (Exception e) {
LOG.error("Cleanup operation failed", e);
}
try {
Thread.sleep(10000);
} catch (Exception e) {
//do nothing
}
if (!cleanupThreadEnabled) {
break checkState;
}
}
}
LOG.debug("Exiting CMS Cleanup Thread.");
}
}, "CMSStaticAssetCleanupThread");
public StaticAssetStorageServiceImpl() {
cleanupThread.start();
}
@PreDestroy
public void destroy() {
cleanupThreadEnabled = false;
try {
cleanupThread.interrupt();
} catch (Exception e) {
LOG.error("Unable to shutdown CMS Cleanup Thread", e);
}
}
protected StaticAsset findStaticAsset(String fullUrl, SandBox sandBox) {
StaticAsset staticAsset = staticAssetService.findStaticAssetByFullUrl(fullUrl, sandBox);
if (staticAsset == null && sandBox != null) {
staticAsset = staticAssetService.findStaticAssetByFullUrl(fullUrl, null);
}
return staticAsset;
}
@Transactional("blTransactionManagerAssetStorageInfo")
@Override
public Map getCacheFileModel(String fullUrl, SandBox sandBox, Map parameterMap) throws Exception {
StaticAsset staticAsset = findStaticAsset(fullUrl, sandBox);
if (staticAsset == null) {
if (sandBox == null) {
throw new RuntimeException("Unable to find an asset for the url (" + fullUrl + ") using the production sandBox.");
} else {
throw new RuntimeException("Unable to find an asset for the url (" + fullUrl + ") using the sandBox id (" + sandBox.getId() + "), or the production sandBox.");
}
}
String mimeType = staticAsset.getMimeType();
//extract the values for any named parameters
Map convertedParameters = namedOperationManager.manageNamedParameters(parameterMap);
String cacheName = constructCacheFileName(staticAsset, convertedParameters);
File cacheFile = new File(cacheDirectory!=null?new File(cacheDirectory):DEFAULTCACHEDIRECTORY, cacheName);
if (!cacheFile.exists()) {
clearObsoleteCacheFiles(staticAsset, cacheFile);
StaticAssetStorage storage = readStaticAssetStorageByStaticAssetId(staticAsset.getId());
if (!convertedParameters.isEmpty()) {
//there are filter operations to perform on the asset
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = null;
try {
is = storage.getFileData().getBinaryStream();
boolean eof = false;
while (!eof) {
int temp = is.read();
if (temp < 0) {
eof = true;
} else {
baos.write(temp);
}
}
baos.flush();
} finally {
if (is != null) {
try{
is.close();
} catch (Throwable e) {
//do nothing
}
}
}
InputStream original = new ByteArrayInputStream(baos.toByteArray());
Operation[] operations = artifactService.buildOperations(convertedParameters, original, staticAsset.getMimeType());
InputStream converted = artifactService.convert(original, operations, staticAsset.getMimeType());
createCacheFile(converted, cacheFile);
if ("image/gif".equals(mimeType)) {
mimeType = "image/png";
}
} else {
createCacheFile(storage.getFileData().getBinaryStream(), cacheFile);
}
}
Map model = new HashMap(2);
model.put("cacheFilePath", cacheFile.getAbsolutePath());
model.put("mimeType", mimeType);
return model;
}
@Transactional("blTransactionManagerAssetStorageInfo")
@Override
public StaticAssetStorage findStaticAssetStorageById(Long id) {
return staticAssetStorageDao.readStaticAssetStorageById(id);
}
@Transactional("blTransactionManagerAssetStorageInfo")
@Override
public StaticAssetStorage create() {
return staticAssetStorageDao.create();
}
@Transactional("blTransactionManagerAssetStorageInfo")
@Override
public StaticAssetStorage readStaticAssetStorageByStaticAssetId(Long id) {
return staticAssetStorageDao.readStaticAssetStorageByStaticAssetId(id);
}
@Transactional("blTransactionManagerAssetStorageInfo")
@Override
public StaticAssetStorage save(StaticAssetStorage assetStorage) {
return staticAssetStorageDao.save(assetStorage);
}
@Transactional("blTransactionManagerAssetStorageInfo")
@Override
public void delete(StaticAssetStorage assetStorage) {
staticAssetStorageDao.delete(assetStorage);
}
@Transactional("blTransactionManagerAssetStorageInfo")
@Override
public Blob createBlob(MultipartFile uploadedFile) throws IOException {
return staticAssetStorageDao.createBlob(uploadedFile);
}
protected void clearObsoleteCacheFiles(StaticAsset staticAsset, File cacheFile) {
File parentDir = cacheFile.getParentFile();
if (parentDir.exists()) {
StaticAssetStorageServiceImpl.CleanupOperation operation = new StaticAssetStorageServiceImpl.CleanupOperation();
operation.setAssetName(staticAsset.getName());
operation.setCacheFile(cacheFile);
synchronized (operations) {
operations.add(operation);
}
}
}
protected void createCacheFile(InputStream is, File cacheFile) throws SQLException, IOException {
if (!cacheFile.getParentFile().exists()) {
if (!cacheFile.getParentFile().mkdirs()) {
throw new RuntimeException("Unable to create middle directories for file: " + cacheFile.getAbsolutePath());
}
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(cacheFile));
try {
boolean eof = false;
int temp;
while (!eof) {
temp = is.read();
if (temp < 0) {
eof = true;
} else {
bos.write(temp);
}
}
} finally {
try {
bos.flush();
bos.close();
} catch (Throwable e) {
//do nothing
}
}
}
protected String constructCacheFileName(StaticAsset staticAsset, Map parameterMap) {
StringBuilder sb = new StringBuilder(200);
sb.append(staticAsset.getFullUrl().substring(0, staticAsset.getFullUrl().lastIndexOf('.')));
sb.append("---");
StringBuilder sb2 = new StringBuilder(200);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
sb2.append(format.format(staticAsset.getAuditable().getDateUpdated()==null?staticAsset.getAuditable().getDateCreated():staticAsset.getAuditable().getDateUpdated()));
for (Map.Entry entry : parameterMap.entrySet()) {
sb2.append('-');
sb2.append(entry.getKey());
sb2.append('-');
sb2.append(entry.getValue());
}
String digest;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(sb2.toString().getBytes());
BigInteger number = new BigInteger(1,messageDigest);
digest = number.toString(16);
} catch(NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
sb.append(pad(digest, 32, '0'));
sb.append('.');
sb.append(staticAsset.getFileExtension());
return sb.toString();
}
protected String pad(String s, int length, char pad) {
StringBuilder buffer = new StringBuilder(s);
while (buffer.length() < length) {
buffer.insert(0, pad);
}
return buffer.toString();
}
public String getCacheDirectory() {
return cacheDirectory;
}
public void setCacheDirectory(String cacheDirectory) {
this.cacheDirectory = cacheDirectory;
}
public boolean isCleanupThreadEnabled() {
return cleanupThreadEnabled;
}
public void setCleanupThreadEnabled(boolean cleanupThreadEnabled) {
this.cleanupThreadEnabled = cleanupThreadEnabled;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy