/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA 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 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA 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.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher.design.fs;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.vfs.Capability;
import org.apache.commons.vfs.FileContent;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileType;
import org.apache.commons.vfs.provider.zip.ZipFileSystem;
import de.innovationgate.utils.cache.Cache;
import de.innovationgate.utils.cache.CacheException;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGBackendException;
import de.innovationgate.webgate.api.WGDesignDocument;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGDocumentCore;
import de.innovationgate.webgate.api.WGDocumentKey;
import de.innovationgate.webgate.api.WGExpressionException;
import de.innovationgate.webgate.api.WGFileContainer;
import de.innovationgate.webgate.api.WGFileMetaData;
import de.innovationgate.webgate.api.WGNotSupportedException;
import de.innovationgate.webgate.api.WGRelationData;
import de.innovationgate.webgate.api.WGScriptModule;
import de.innovationgate.webgate.api.WGTMLModule;
import de.innovationgate.wga.config.DesignReference;
import de.innovationgate.wgpublisher.design.WGADesignManager;
import de.innovationgate.wgpublisher.design.fs.FileSystemDesignManager.ModuleFile;
import de.innovationgate.wgpublisher.design.sync.WGDesignSyncException;
import de.innovationgate.wgpublisher.design.sync.FileContainerDeployment.ContainerFile;
import de.innovationgate.wgpublisher.design.sync.FileContainerDeployment.FileNameComparator;
public class DesignFileDocument extends AbstractDesignFile implements WGDocumentCore {
public static class Data {
private long _lastModified;
private DesignMetadata _metadata;
private String _code;
private String _encoding;
private boolean _mdFileExists;
public Data(DesignMetadata metadata, String code, long lastModified, String encoding, boolean mdFileExists) {
super();
_metadata = metadata;
_mdFileExists = mdFileExists;
_code = code;
_lastModified = lastModified;
_encoding = encoding;
}
public DesignMetadata getMetadata() {
return _metadata;
}
public String getCode() {
return _code;
}
public long getLastModified() {
return _lastModified;
}
public String getEncoding() {
return _encoding;
}
public boolean isMdFileExists() {
return _mdFileExists;
}
}
private FileSystemDesignProvider _manager;
private WGDocument _wgdoc;
private String _name;
private String _category;
private WGDocumentKey _docKey;
private Data _data;
protected DesignFileDocument(FileSystemDesignProvider manager, FileObject file, String name, int type, String category) throws FileSystemException, WGDesignSyncException {
super(manager, file, type);
_manager = manager;
_name = name;
_category = category;
if (type == WGDocument.TYPE_TML || type == WGDocument.TYPE_CSSJS) {
_docKey = new WGDocumentKey(type, name, category);
}
else {
_docKey = new WGDocumentKey(type, name, null);
}
}
protected DesignFileDocument(FileSystemDesignProvider fileSystemDesignManager, ModuleFile moduleFile) throws FileSystemException, WGDesignSyncException {
this(fileSystemDesignManager, moduleFile.getFile(), moduleFile.getModuleName(), moduleFile.getType(), moduleFile.getCategory());
}
public boolean attachFile(File file) throws WGAPIException {
return false;
}
public void dispose() {
}
public Object evaluateExpression(String expression) throws WGExpressionException, WGBackendException {
throw new WGExpressionException("Not supported", expression);
}
public Date getCreated() throws WGAPIException {
try {
if (getType() == WGDocument.TYPE_FILECONTAINER) {
return getObjectCreated();
}
else {
return new Date(getCodeFile().getContent().getLastModifiedTime());
}
}
catch (Exception e) {
throw new WGBackendException("Exception retrieving created date", e);
}
}
public Object getFastAccessKey() throws WGBackendException {
return null;
}
public InputStream getFileData(String strFile) throws WGAPIException {
try {
if (getType() != WGDocument.TYPE_FILECONTAINER) {
return null;
}
FileObject file = getFileContainerFile(strFile);
if (file == null) {
return null;
}
if (file.getType().equals(FileType.FILE)) {
return file.getContent().getInputStream();
} else {
return null;
}
}
catch (Exception e) {
throw new WGBackendException("Exception reading container file data", e);
}
}
public WGFileMetaData getFileMetaData(String strFile) throws WGAPIException {
try {
FileObject file = getFileContainerFile(strFile);
if (file == null) {
return null;
}
WGFileMetaData md = new WGFileMetaData();
Date lastModified = new Date(getLastModifiedTime(file));
md.setCreated(lastModified);
md.setLastmodified(lastModified);
md.setName(strFile.toLowerCase());
md.setSize(file.getContent().getSize());
return md;
}
catch (Exception e) {
throw new WGBackendException("Exception retrieving file metadata for file " + strFile + " on document " + _docKey.toString(), e);
}
}
public List getFileNames() throws WGBackendException {
try {
if (getType() != WGDocument.TYPE_FILECONTAINER) {
return null;
}
getCodeFile().refresh();
Iterator<FileObject> files = getFileContainerFiles().iterator();
List<String> fileNames = new ArrayList<String>();
while (files.hasNext()) {
FileObject file = files.next();
fileNames.add(file.getName().getBaseName().toLowerCase());
}
return fileNames;
}
catch (Exception e) {
throw new WGBackendException("Exception reading container file data", e);
}
}
public int getFileSize(String strFile) throws WGAPIException {
try {
if (getType() != WGDocument.TYPE_FILECONTAINER) {
return -1;
}
FileObject file = getFileContainerFile(strFile);
if (file == null) {
return -1;
}
return (int) file.getContent().getSize();
}
catch (Exception e) {
throw new WGBackendException("Exception reading container file data", e);
}
}
public List getItemNames() throws WGBackendException {
return Collections.emptyList();
}
public Object getItemValue(String strName) throws WGAPIException {
return null;
}
public Date getLastModified() throws WGAPIException {
try {
return new Date(getData().getLastModified());
}
catch (Exception e) {
throw new WGBackendException("Exception retrieving last modified date", e);
}
}
public Object getMetaData(String type) throws WGAPIException {
try {
if (type.equals(WGDocument.META_CREATED)) {
return getCreated();
}
else if (type.equals(WGDocument.META_LASTMODIFIED)) {
return getLastModified();
}
else if (type.equals(WGDocument.META_PASTAUTHORS)) {
return Collections.EMPTY_LIST;
}
else if (type.equals(WGDocument.META_PASTEDITDATES)) {
return Collections.EMPTY_LIST;
}
else if (type.equals(WGDocument.META_REVISION)) {
return 1;
}
else if (type.equals(WGDesignDocument.META_NAME)) {
return _name;
}
else if (type.equals(WGDesignDocument.META_DESCRIPTION)) {
return getData().getMetadata().getDescription();
}
else if (type.equals(WGDesignDocument.META_DESIGNREFERENCE)) {
return new DesignReference(_manager.getDesignReference(), _docKey.toString());
}
switch (getType()) {
case WGDocument.TYPE_TML:
if (type.equals(WGTMLModule.META_CACHEABLE)) {
TMLMetadata metadata = (TMLMetadata) getData().getMetadata();
return Boolean.valueOf(metadata.isCacheable());
}
else if (type.equals(WGTMLModule.META_CODE)) {
return getData().getCode();
}
else if (type.equals(WGTMLModule.META_DIRECTACCESS)) {
TMLMetadata metadata = (TMLMetadata) getData().getMetadata();
return Boolean.valueOf(metadata.isDirectAccess());
}
else if (type.equals(WGTMLModule.META_MEDIAKEY)) {
return _category;
}
break;
case WGDocument.TYPE_CSSJS:
if (type.equals(WGScriptModule.META_CODE)) {
return getData().getCode();
}
else if (type.equals(WGScriptModule.META_CODETYPE)) {
return _category;
}
break;
case WGDocument.TYPE_FILECONTAINER: {
break;
}
}
return null;
}
catch (Exception e) {
throw new WGBackendException("Exception reading file design metadata of " + _docKey.toString(), e);
}
}
private Data getData() throws FileNotFoundException, IOException, InstantiationException, IllegalAccessException, WGDesignSyncException {
// Look if already retrieved
if (_data != null) {
return _data;
}
// Determine current last modified time
long mdLastModifiedTime = Long.MIN_VALUE;
FileObject mdFile = getMetadataFile();
if (mdFile.exists()) {
mdLastModifiedTime = getLastModifiedTime(mdFile);
}
long codeLastModifiedTime = getLastModifiedTime(getCodeFile());
long lastModified = Math.max(mdLastModifiedTime, codeLastModifiedTime);
if (getType() == WGDocument.TYPE_FILECONTAINER && _manager.isStrictFCDateDetermination()) {
lastModified = determineFileContainerLastModified();
}
// Check the cache
Cache cache = _manager.getCore().getDesignFileCache();
// We need to include the reference of the current provider here bc. the codefile path itself may be not full-qualified
// (for example when it reflects the path inside a ZIP file)
String cacheKey = _manager.getDesignReference() + "/" + getCodeFile().getName().getPath();
try {
Data cacheData = (Data) cache.readEntry(cacheKey);
if (cacheData != null && cacheData.getLastModified() >= lastModified && cacheData.isMdFileExists() == mdFile.exists() && cacheData.getEncoding().equals(_manager.getFileEncoding())) {
_data = cacheData;
return cacheData;
}
}
catch (CacheException e) {
_manager.getLog().error("Exception checking design cache", e);
}
// Build a new data object and put it to cache
DesignMetadata metadata = (DesignMetadata) readMetaData();
String code = readCode(metadata);
_data = new Data(metadata, code, lastModified, _manager.getFileEncoding(), mdFile.exists());
try {
cache.writeEntry(cacheKey, _data);
}
catch (CacheException e) {
_manager.getLog().error("Exception writing design cache", e);
}
return _data;
}
private long getLastModifiedTime(FileObject file) throws FileSystemException {
if (file.getFileSystem() instanceof ZipFileSystem) {
return file.getFileSystem().getParentLayer().getContent().getLastModifiedTime();
}
return file.getContent().getLastModifiedTime();
}
private long determineFileContainerLastModified() throws WGDesignSyncException, InstantiationException, IllegalAccessException, IOException {
// Throw all dates that may symbolize changes to a list
List<Long> dates = new ArrayList<Long>();
// Directory date
dates.add(getLastModifiedTime(getCodeFile()));
// Metadata date
FileObject mdFile = getMetadataFile();
if (mdFile.exists()) {
dates.add(mdFile.getContent().getLastModifiedTime());
}
// Container File dates
List<FileObject> files = getFileContainerFiles();
FileObject file;
for (int i = 0; i < files.size(); i++) {
file = (FileObject) files.get(i);
if (isExcludedFileContainerFile(file)) {
continue;
}
dates.add(file.getContent().getLastModifiedTime());
}
// Sort and take the highest
Collections.sort(dates);
return dates.get(dates.size() - 1);
}
public Object getNativeObject() throws WGBackendException {
try {
return getCodeFile();
}
catch (Exception e) {
throw new WGBackendException("Exception retrieving native object of " + _docKey.toString(), e);
}
}
public String getOriginDatabase() {
return getManager().getDB().getDbReference();
}
public WGDocumentCore getRelation(String name) throws WGAPIException {
return null;
}
public List getRelationNames() throws WGAPIException {
return Collections.emptyList();
}
public boolean hasItem(String strName) throws WGBackendException {
return false;
}
public boolean isDataCacheable() {
return false;
}
public boolean isDeleted() throws WGAPIException {
try {
return !getCodeFile().exists();
}
catch (Exception e) {
throw new WGBackendException("Exception determining file deletion state", e);
}
}
public boolean isSaved() throws WGAPIException {
return true;
}
public boolean isTemporary() throws WGAPIException {
return true;
}
public boolean remove() throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public boolean removeFile(String name) throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public boolean removeItem(String Name) throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public WGDocumentCore removeRelation(String name) throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public void renameFile(String oldFileName, String newFileName) throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public boolean save(Date lastModified) throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public boolean setItemValue(String strName, Object value) throws WGAPIException {
return false;
}
public boolean setMetaData(String name, Object value) throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public WGDocumentCore setRelation(String name, WGDocumentCore target) throws WGAPIException {
throw new WGNotSupportedException("Not Supported");
}
public void setWGDocument(WGDocument doc) {
_wgdoc = doc;
}
@Override
protected FileSystemDesignManager getManager() {
return _manager;
}
public boolean hasFileMetadata() throws WGAPIException {
return true;
}
public boolean hasFile(String file) throws WGBackendException {
try {
return (getFileContainerFile(file) != null);
}
catch (Exception e) {
throw new WGBackendException("Exception checking container file existence", e);
}
}
@Override
protected FileObject getFileContainerFile(String name) throws FileSystemException, WGDesignSyncException {
FileObject file = super.getFileContainerFile(name);
if (file == null) {
getCodeFile().refresh();
}
return super.getFileContainerFile(name);
}
public WGRelationData getRelationData(String name) throws WGAPIException {
throw new WGNotSupportedException("This operation is not supported by this database type");
}
public WGDocumentCore setRelation(WGRelationData relAddress) throws WGAPIException {
throw new WGNotSupportedException("This operation is not supported by this database type");
}
public Object getExtensionData(String strName) throws WGAPIException {
try {
if (strName.equals(WGDocument.EXTDATA_META_PREFIX + WGTMLModule.META_DESCRIPTION)) {
return getData().getMetadata().getDescription();
}
else if (strName.equals(WGDocument.EXTDATA_META_PREFIX + WGTMLModule.META_CODEOFFSET)) {
return getData().getMetadata().getHeaderLines();
}
return null;
}
catch (Exception e) {
throw new WGBackendException("Exception retrieving attribute " + strName, e);
}
}
public List getExtensionDataNames() throws WGAPIException {
return Collections.EMPTY_LIST;
}
public void removeExtensionData(String strName) throws WGAPIException {
throw new WGNotSupportedException("This operation is not supported by this database type");
}
public void writeExtensionData(String strName, Object value) throws WGAPIException {
throw new WGNotSupportedException("This operation is not supported by this database type");
}
public List<String> getRelationNamesOfGroup(String group) throws WGBackendException {
return Collections.emptyList();
}
}