/*******************************************************************************
* 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.commons.logging.impl.Log4JLogger;
import org.apache.commons.vfs.CacheStrategy;
import org.apache.commons.vfs.Capability;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystem;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileType;
import org.apache.commons.vfs.impl.DefaultFileReplicator;
import org.apache.commons.vfs.impl.StandardFileSystemManager;
import org.apache.log4j.Logger;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.Dom4JDriver;
import de.innovationgate.utils.DESEncrypter;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGCSSJSModule;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDatabaseConnectListener;
import de.innovationgate.webgate.api.WGDatabaseEvent;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGFileContainer;
import de.innovationgate.webgate.api.WGTMLModule;
import de.innovationgate.wga.common.Constants;
import de.innovationgate.wga.common.DesignDirectory;
import de.innovationgate.wga.common.DesignDirectory.ScriptInformation;
import de.innovationgate.wga.common.beans.DesignDefinition;
import de.innovationgate.wga.common.beans.csconfig.v1.CSConfig;
import de.innovationgate.wga.common.beans.csconfig.v1.InvalidCSConfigVersionException;
import de.innovationgate.wga.common.beans.csconfig.v1.PluginConfig;
import de.innovationgate.wga.common.beans.csconfig.v1.PublisherOption;
import de.innovationgate.wgpublisher.ManagedDBAttribute;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.design.sync.DesignSyncManager;
import de.innovationgate.wgpublisher.design.sync.WGDesignSyncException;
import de.innovationgate.wgpublisher.design.sync.WGInitialDeployException;
/**
* Common base class of {@link FileSystemDesignProvider} and {@link DesignSyncManager}
* implementing common functionalities
*
*/
public abstract class FileSystemDesignManager implements WGDatabaseConnectListener, ManagedDBAttribute {
public static final String OPTION_DESIGNKEY = "designkey";
public static final String LOGGER_DESIGNSYNC = "wga.designsync";
public static final String SYNCSTATUS_MODULE = "designsyncstatus";
public static final String DIRECTORY_DIVIDER = ":";
public static Map<Integer, DesignInformation> _designTypes = new HashMap<Integer, DesignInformation>();
protected static XStream _xstream = new XStream(new Dom4JDriver());
private Pattern _validDesignName = Constants.PATTERN_FILENAMES;
public static class FileSystemResources {
private FileObject _baseFolder;
private FileObject _filesFolder;
private FileObject _tmlFolder;
private FileObject _scriptFolder;
private FileObject _javaFolder;
private FileObject _designDefinitionFile;
private boolean _updateableFileSystem = true;
public FileSystemResources(FileObject baseFolder) throws FileSystemException {
_baseFolder = baseFolder;
_tmlFolder = _baseFolder.resolveFile(DesignDirectory.FOLDERNAME_TML);
_scriptFolder = _baseFolder.resolveFile(DesignDirectory.FOLDERNAME_SCRIPT);
_filesFolder = _baseFolder.resolveFile(DesignDirectory.FOLDERNAME_FILES);
_javaFolder = _baseFolder.resolveFile(DesignDirectory.FOLDERNAME_JAVA);
_designDefinitionFile = DesignDirectory.getDesignDefinitionFile(_baseFolder);
if (_designDefinitionFile == null) {
_designDefinitionFile = _baseFolder.resolveFile(DesignDirectory.DESIGN_DEFINITION_FILE);
}
_updateableFileSystem = !_baseFolder.getFileSystem().hasCapability(Capability.VIRTUAL);
}
protected FileObject getBaseFolder() {
return _baseFolder;
}
protected FileObject getFilesFolder() {
return _filesFolder;
}
protected FileObject getScriptFolder() {
return _scriptFolder;
}
protected FileObject getDesignDefinitionFile() {
return _designDefinitionFile;
}
protected FileObject getTmlFolder() {
return _tmlFolder;
}
public FileObject getJavaFolder() {
return _javaFolder;
}
protected boolean isUpdateableFileSystem() {
return _updateableFileSystem;
}
public void refreshAll() throws FileSystemException {
_baseFolder.refresh();
_designDefinitionFile.refresh();
_filesFolder.refresh();
_javaFolder.refresh();
_scriptFolder.refresh();
_tmlFolder.refresh();
}
}
public class ModuleFile {
private FileObject _file;
private String _prefix;
private String _category;
private int _type;
public ModuleFile(FileObject file, String prefix, String category, int type) throws FileSystemException {
_file = file;
_prefix = prefix;
_category = category;
_type = type;
}
/**
* @return Returns the file.
*/
public FileObject getFile() {
return _file;
}
/**
* @return Returns the prefix.
*/
public String getPrefix() {
return _prefix;
}
public String getModuleName() {
String fileName = _file.getName().getBaseName().toLowerCase();
int suffixPos = fileName.lastIndexOf(".");
String nameWithoutSuffix;
if (suffixPos != -1) {
nameWithoutSuffix = fileName.substring(0, suffixPos);
}
else {
nameWithoutSuffix = fileName;
}
if (_prefix == null || _prefix.trim().equals("")) {
return nameWithoutSuffix;
}
else {
return _prefix + DIRECTORY_DIVIDER + nameWithoutSuffix;
}
}
public String getCategory() {
return _category;
}
public int getType() {
return _type;
}
}
public static class DesignInformation {
private Class<? extends DesignMetadata> _metaDataClass;
public DesignInformation(Class<? extends DesignMetadata> clazz) {
_metaDataClass = clazz;
}
/**
* @return Returns the metaDataClass.
*/
public Class<? extends DesignMetadata> getMetaDataClass() {
return _metaDataClass;
}
}
static {
// Map design types
Class<ScriptMetadata> scriptClass = ScriptMetadata.class;
_designTypes.put(new Integer(WGDocument.TYPE_CSSJS), new DesignInformation(ScriptMetadata.class));
_designTypes.put(new Integer(WGDocument.TYPE_TML), new DesignInformation(TMLMetadata.class));
_designTypes.put(new Integer(WGDocument.TYPE_FILECONTAINER), new DesignInformation(FCMetadata.class));
}
public static DesignInformation getDesignInformation(int type) {
return _designTypes.get(new Integer(type));
}
protected WGDatabase _db;
protected Logger _log;
protected String _designKey;
private String _fileEncoding = null;
private DesignDefinition _syncInfo;
protected WGACore _core;
protected StandardFileSystemManager _fsManager;
protected String _designPath;
protected static final String LOGGER_DESIGNSYNC_QUIET = "wga.designsync.quiet";
public static final String MODE_FULL = "full";
public static final String MODE_VIRTUAL = "virtual";
public static final String MODE_DIRECT = "direct";
private FileSystemResources _fsResources = null;
private DESEncrypter _cipher;
protected boolean _directAccessDefault;
private CSConfig _csconfig = null;
protected Set<Integer> _syncedDoctypes = new HashSet<Integer>();
protected Map<String, String> _designOptions;
private String _javaClassesPath;
private Long _designFileTimestamp = null;
private Long _csconfigTimestamp = null;
protected boolean _lookupVariants = false;
public FileSystemDesignManager(WGACore core, WGDatabase db, String path, Map<String,String> options) throws WGDesignSyncException, IOException, WGAPIException, InstantiationException,
IllegalAccessException, InvalidCSConfigVersionException {
_core = core;
// Init logger
if (db.getDbReference().startsWith(PluginConfig.PLUGIN_DBKEY_PREFIX)) {
_log = Logger.getLogger(LOGGER_DESIGNSYNC_QUIET);
}
else {
_log = Logger.getLogger(LOGGER_DESIGNSYNC);
}
_fsManager = new StandardFileSystemManager();
_fsManager.setCacheStrategy(CacheStrategy.MANUAL);
_fsManager.setLogger(new Log4JLogger(Logger.getLogger(LOGGER_DESIGNSYNC_QUIET)));
_fsManager.setClassLoader(WGACore.getLibraryLoader());
_fsManager.setCacheStrategy(getVFSCacheStrategy());
_fsManager.init();
_designPath = path;
_designOptions = options;
_designKey = options.get(OPTION_DESIGNKEY);
_db = db;
_directAccessDefault = db.getBooleanAttribute(WGACore.DBATTRIB_DIRECTACCESSDEFAULT, false);
// Determine provided types
String optionProviderTypes = (String) db.getCreationOptions().get(WGDatabase.COPTION_DESIGNPROVIDERTYPES);
if (optionProviderTypes != null) {
Iterator<String> providerTypes = WGUtils.deserializeCollection(optionProviderTypes, ",", true).iterator();
while (providerTypes.hasNext()) {
String providerTypeName = providerTypes.next();
int providerType = WGDocument.doctypeNameToNumber(providerTypeName);
if (providerType != 0) {
_syncedDoctypes.add(new Integer(providerType));
}
}
}
else {
_syncedDoctypes.add(new Integer(WGDocument.TYPE_FILECONTAINER));
_syncedDoctypes.add(new Integer(WGDocument.TYPE_TML));
_syncedDoctypes.add(new Integer(WGDocument.TYPE_CSSJS));
}
fetchFileSystem(core);
if (_db.isConnected()) {
init();
}
else {
_db.addDatabaseConnectListener(this);
}
}
protected CacheStrategy getVFSCacheStrategy() {
return CacheStrategy.ON_RESOLVE;
}
protected void closeFileSystem() {
if (_fsResources != null) {
FileSystem fs = _fsResources.getBaseFolder().getFileSystem();
_fsResources = null;
_fsManager.closeFileSystem(fs);
_fsManager.freeUnusedResources();
}
}
protected void fetchFileSystem(WGACore core) throws WGDesignSyncException, IOException {
// Dont run if already fetched
if (_fsResources != null) {
return;
}
FileObject baseFolder = null;
try {
baseFolder = _fsManager.resolveFile(_designPath);
if (!baseFolder.exists() || !baseFolder.getType().equals(FileType.FOLDER)) {
throw new WGDesignSyncException("Directory location " + _designPath + " either does not exist or is not directory");
}
}
catch (FileSystemException e) {
throw new WGDesignSyncException("Unparseable VFS URI: " + _designPath, e);
}
_fsResources = new FileSystemResources(baseFolder);
}
/**
* @param doInitSync
* @return
* @throws WGAPIException
* @throws IOException
* @throws WGDesignSyncException
* @throws InstantiationException
* @throws IllegalAccessException
* @throws InvalidCSConfigVersionException
*/
protected boolean init() throws WGAPIException, IOException, WGDesignSyncException, InstantiationException, IllegalAccessException, InvalidCSConfigVersionException {
// Determine the state of the directory
FileObject syncInfoFile = getSyncInfoFile();
if (!syncInfoFile.exists()) {
// A new directory. Do initial deployment
if (getBaseFolder().getFileSystem().hasCapability(Capability.CREATE)) {
_log.info("Performing initial deployment of design to directory '" + getBaseFolder().getURL().toString() + "'");
_fileEncoding = _core.getWgaConfiguration().getDesignConfiguration().getDefaultEncoding();
doInitialDeployment(_designKey);
return true;
}
else {
throw new WGDesignSyncException("Cannot perform initial deploy because the file system is readonly: " + getBaseFolder().getURL().toString());
}
}
else {
// Determine if syncdata file is present. If so, use this directory
// as sync directory.
getOrCreateFolders();
updateConfiguration();
return false;
}
}
protected void updateConfiguration() throws IOException, WGDesignSyncException, FileSystemException, InvalidCSConfigVersionException {
boolean configUpdated = false;
// Import config files
FileObject designFile = getSyncInfoFile();
long currentDesignFileTimestamp = designFile.getContent().getLastModifiedTime();
if (_designFileTimestamp == null || currentDesignFileTimestamp > _designFileTimestamp.longValue()) {
_designFileTimestamp = new Long(currentDesignFileTimestamp);
importSyncInfo(designFile);
configUpdated = true;
}
FileObject csconfigFile = getFilesFolder().resolveFile("system/csconfig.xml");
if (csconfigFile.exists()) {
long currentCsconfigTimestamp = csconfigFile.getContent().getLastModifiedTime();
if (_csconfigTimestamp == null || currentCsconfigTimestamp > _csconfigTimestamp.longValue()) {
_csconfigTimestamp = new Long(currentCsconfigTimestamp);
importCSConfig(csconfigFile);
configUpdated = true;
}
}
// Load configuration settings
if (configUpdated) {
determineFileEncoding();
determineEncryption();
determineSyncDefaults();
}
}
private void determineSyncDefaults() {
if (_csconfig != null) {
PublisherOption option = _csconfig.findPublisherOption(WGACore.DBATTRIB_DIRECTACCESSDEFAULT);
if (option != null) {
_directAccessDefault = Boolean.valueOf(option.getValue()).booleanValue();
}
}
}
private void importCSConfig(FileObject csconfigFile) throws FileSystemException, IOException, WGDesignSyncException, InvalidCSConfigVersionException {
if (csconfigFile.exists()) {
_csconfig = CSConfig.load(csconfigFile);
}
}
private void determineEncryption() throws WGDesignSyncException {
try {
FileObject file = getBaseFolder().resolveFile(DesignDirectory.OBFUSCATE_FLAGFILE);
if (file.exists()) {
_cipher = new DESEncrypter();
_cipher.initObfuscation();
}
}
catch (Exception e) {
throw new WGDesignSyncException("Exception inizializing deobfuscation", e);
}
}
private FileObject getSyncInfoFile() throws WGDesignSyncException {
if (_fsResources != null) {
return _fsResources.getDesignDefinitionFile();
}
else {
throw new WGDesignSyncException("The base folder was retrieved from an illegal code position");
}
}
public void close() {
closeFileSystem();
_fsManager.close();
}
private void importSyncInfo(FileObject syncInfoFile) throws IOException {
_syncInfo = DesignDefinition.load(syncInfoFile);
}
protected void doInitialDeployment(String designKey) throws WGAPIException, IOException, InstantiationException, IllegalAccessException, WGDesignSyncException {
// Creat folders
getOrCreateFolders();
// Deploy TMLs
Iterator modules = getDB().getTMLModules().iterator();
WGTMLModule mod;
while (modules.hasNext()) {
mod = (WGTMLModule) modules.next();
if (isValidDesignName(mod.getName())) {
initialDeployTMLModule(mod);
}
else {
_log.warn("Could not use '" + mod.getDocumentKey() + "' for design deployment since the name contains invalid characters");
}
}
// Deploy file containers
Iterator containers = getDB().getFileContainers().iterator();
WGFileContainer con;
while (containers.hasNext()) {
con = (WGFileContainer) containers.next();
if (isValidDesignName(con.getName())) {
initialDeployFileContainer(con);
}
else {
_log.warn("Could not use '" + con.getDocumentKey() + "' for design deployment since the name contains invalid characters");
}
}
// Deploy script modules
Iterator scripts = getDB().getCSSJSModules().iterator();
WGCSSJSModule script;
while (scripts.hasNext()) {
script = (WGCSSJSModule) scripts.next();
if (isValidDesignName(script.getName())) {
initialDeployScriptModule(script);
}
else {
_log.warn("Could not use '" + script.getDocumentKey() + "' for design deployment since the name contains invalid characters");
}
}
// Create sync info and store it to directory
_syncInfo = new DesignDefinition();
_syncInfo.setDesignKey(designKey);
if (_core.getWgaConfiguration().getDesignConfiguration().getDefaultEncoding() != null) {
_syncInfo.setFileEncoding(_core.getWgaConfiguration().getDesignConfiguration().getDefaultEncoding());
}
else {
// if encoding not specified set to platform encoding
_syncInfo.setFileEncoding(System.getProperty("file.encoding"));
}
FileObject syncInfoFile = getSyncInfoFile();
_log.info("Creating design definition file " + syncInfoFile.getName().getPath());
syncInfoFile.createFile();
storeSyncInfo(syncInfoFile);
_log.info("Initial deploy finished");
}
private boolean isValidDesignName(String name) {
return _validDesignName.matcher(name).matches();
}
private void getOrCreateFolders() throws IOException, WGDesignSyncException {
if (getBaseFolder().getFileSystem().hasCapability(Capability.CREATE)) {
WGUtils.getOrCreateFolder(getBaseFolder(), DesignDirectory.FOLDERNAME_TML);
WGUtils.getOrCreateFolder(getBaseFolder(), DesignDirectory.FOLDERNAME_SCRIPT);
WGUtils.getOrCreateFolder(getBaseFolder(), DesignDirectory.FOLDERNAME_FILES);
Iterator scriptInfos = DesignDirectory.getScriptTypes().values().iterator();
while (scriptInfos.hasNext()) {
ScriptInformation info = (ScriptInformation) scriptInfos.next();
if (info.isAutoCreate()) {
WGUtils.getOrCreateFolder(getScriptFolder(), info.getFolder());
}
}
if (_fsResources.getJavaFolder().exists()) {
_javaClassesPath = _fsResources.getJavaFolder().getName().getPath();
}
}
}
protected FileObject getFilesFolder() throws WGDesignSyncException {
if (_fsResources != null) {
return _fsResources.getFilesFolder();
}
else {
throw new WGDesignSyncException("The files folder was retrieved from an illegal code position");
}
}
protected FileObject getScriptTypeFolder(String folderName) throws FileSystemException, WGDesignSyncException {
FileObject scriptFolder = getScriptFolder();
if (scriptFolder.exists()) {
return scriptFolder.getChild(folderName);
}
else {
return null;
}
}
protected FileObject getScriptFolder() throws WGDesignSyncException {
if (_fsResources != null) {
return _fsResources.getScriptFolder();
}
else {
throw new WGDesignSyncException("The base folder was retrieved from an illegal code position");
}
}
private void storeSyncInfo(FileObject syncInfoFile) throws IOException {
_syncInfo.write(syncInfoFile);
}
private Writer createWriter(FileObject file) throws IOException {
return new OutputStreamWriter(file.getContent().getOutputStream(), getFileEncoding());
}
protected FileObject initialDeployScriptModule(WGCSSJSModule script) throws IOException, InstantiationException, IllegalAccessException, WGAPIException, WGDesignSyncException {
if (script.isMetadataModule()) {
return null;
}
ScriptInformation info = DesignDirectory.getScriptInformation(script.getCodeType());
if (info == null) {
_log.warn("Cannot deploy unknown script code type: " + script.getCodeType());
return null;
}
// Get script type folder
FileObject scriptTypeFolder = getScriptTypeFolder(info.getFolder());
// Eventually create intermediate directories
List<String> path = WGUtils.deserializeCollection(script.getName(), ":", true);
String localName = (String) path.get(path.size() - 1);
FileObject currentDir = scriptTypeFolder;
for (int i = 0; i < path.size() - 1; i++) {
currentDir = currentDir.resolveFile((String) path.get(i));
if (!currentDir.exists()) {
_log.info("Creating script category directory" + getRelativePath(currentDir));
try {
currentDir.createFolder();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create script category folder '" + getRelativePath(currentDir) + "'", e);
}
}
else if (!currentDir.getType().equals(FileType.FOLDER)) {
throw new WGInitialDeployException("Cannot deploy " + script.getDocumentKey() + " to sync folder because the directory name '" + path.get(i) + "' is already used by another file");
}
}
// Create code file
FileObject codeFile = currentDir.resolveFile(localName + info.getSuffix());
_log.info("Creating script module file " + getRelativePath(codeFile));
try {
codeFile.createFile();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create script code file '" + codeFile.getName().getPathDecoded() + "'");
}
Writer writer = createWriter(codeFile);
writer.write(script.getCode());
writer.close();
// Create metadata file
ScriptMetadata metaData = new ScriptMetadata(script);
FileObject metadataDir = currentDir.resolveFile(DesignDirectory.NAME_METADATADIR);
if (!metadataDir.exists()) {
_log.info("Creating script metadata directory " + getRelativePath(metadataDir));
try {
metadataDir.createFolder();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create metadata folder '" + getRelativePath(metadataDir) + "'");
}
}
FileObject metadataFile = metadataDir.resolveFile(localName + DesignDirectory.SUFFIX_METADATA);
_log.info("Creating script metadata file " + getRelativePath(metadataFile));
writer = createWriter(metadataFile);
writer.write(_xstream.toXML(metaData.getInfo()));
writer.close();
return codeFile;
}
protected FileObject initialDeployFileContainer(WGFileContainer con) throws IOException, InstantiationException, IllegalAccessException, WGAPIException, WGDesignSyncException {
// Create container folder
FileObject containerFolder = getFilesFolder().resolveFile(con.getName());
_log.info("Creating file container folder " + getRelativePath(containerFolder));
try {
containerFolder.createFolder();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create file container folder '" + containerFolder.getName().getPathDecoded() + "'", e);
}
// Create metadata file
FCMetadata metaData = new FCMetadata(con);
FileObject metadataFile = containerFolder.resolveFile(AbstractDesignFile.FILECONTAINER_METADATA_FILENAME + DesignDirectory.SUFFIX_METADATA);
_log.info("Creating file container metadata file " + getRelativePath(metadataFile));
Writer writer = createWriter(metadataFile);
writer.write(_xstream.toXML(metaData.getInfo()));
writer.close();
// Create contained files
Iterator fileNames = con.getFileNames().iterator();
String fileName;
FileObject file;
while (fileNames.hasNext()) {
fileName = (String) fileNames.next();
InputStream in = con.getFileData(fileName);
file = containerFolder.resolveFile(fileName);
_log.info("Creating file container file " + getRelativePath(file));
try {
file.createFile();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create container file '" + getRelativePath(file) + "'", e);
}
OutputStream out = file.getContent().getOutputStream();
WGUtils.inToOut(in, out, 2048);
in.close();
out.close();
}
return containerFolder;
}
protected FileObject initialDeployTMLModule(WGTMLModule mod) throws IOException, InstantiationException, IllegalAccessException, WGAPIException, WGDesignSyncException {
// Find/create media key folder
FileObject mediaKeyFolder = getTmlFolder().resolveFile(mod.getMediaKey());
if (!mediaKeyFolder.exists()) {
_log.info("Creating media key folder " + getRelativePath(mediaKeyFolder));
try {
mediaKeyFolder.createFolder();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create media key folder '" + mediaKeyFolder.getName().getPathDecoded() + "'");
}
}
// Eventually create intermediate directories
List<String> path = WGUtils.deserializeCollection(mod.getName(), ":", true);
String localName = (String) path.get(path.size() - 1);
FileObject currentDir = mediaKeyFolder;
for (int i = 0; i < path.size() - 1; i++) {
currentDir = currentDir.resolveFile((String) path.get(i));
if (!currentDir.exists()) {
_log.info("Creating tml category directory " + getRelativePath(currentDir));
try {
currentDir.createFolder();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create tml category folder '" + getRelativePath(currentDir) + "'", e);
}
}
else if (!currentDir.getType().equals(FileType.FOLDER)) {
throw new WGInitialDeployException("Cannot deploy " + mod.getDocumentKey() + " to sync folder because the directory name '" + path.get(i) + "' is already used by another file");
}
}
// Create code file
FileObject tmlCodeFile = currentDir.resolveFile(localName + DesignDirectory.SUFFIX_TML);
_log.info("Creating tml module file " + getRelativePath(tmlCodeFile));
try {
tmlCodeFile.createFile();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create tml code file '" + getRelativePath(tmlCodeFile) + "'");
}
Writer writer = createWriter(tmlCodeFile);
writer.write(mod.getCode());
writer.close();
// Create metadata file
TMLMetadata metaData = new TMLMetadata(mod);
FileObject metadataDir = currentDir.resolveFile(DesignDirectory.NAME_METADATADIR);
if (!metadataDir.exists()) {
_log.info("Creating tml metadata directory " + getRelativePath(metadataDir));
try {
metadataDir.createFolder();
}
catch (FileSystemException e) {
throw new WGInitialDeployException("Could not create metadata folder '" + metadataDir.getName().getPathDecoded() + "'");
}
}
FileObject metadataFile = metadataDir.resolveFile(localName + DesignDirectory.SUFFIX_METADATA);
_log.info("Creating tml metadata file " + getRelativePath(metadataFile));
writer = createWriter(metadataFile);
writer.write(_xstream.toXML(metaData.getInfo()));
writer.close();
return tmlCodeFile;
}
protected String getRelativePath(FileObject file) throws FileSystemException, WGDesignSyncException {
return getBaseFolder().getName().getRelativeName(file.getName());
}
public WGDatabase getDB() {
return _db;
}
private void determineFileEncoding() throws IOException, WGDesignSyncException, InvalidCSConfigVersionException {
// Either encoding is set in syncinfo.xml, or syncinfo.xml points to
// csconfig.xml
if (_syncInfo.getFileEncoding() == null) {
_fileEncoding = System.getProperty("file.encoding");
_log.debug("Design of " + _db.getDbReference() + " uses file encoding " + _fileEncoding + " to read design files (platform default)");
}
else if (_syncInfo.getFileEncoding().equals(DesignDefinition.FILEENCODING_CSCONFIG_DEFINED)) {
if (_csconfig != null) {
PublisherOption option = _csconfig.findPublisherOption(WGACore.DBATTRIB_DESIGN_ENCODING);
if (option != null) {
_fileEncoding = option.getValue();
_log.debug("Design of " + _db.getDbReference() + " uses file encoding '" + _fileEncoding + "' to read design files (as specified in design configuration)");
}
else {
_fileEncoding = System.getProperty("file.encoding");
_log.debug("Design of " + _db.getDbReference() + " uses file encoding " + _fileEncoding + " to read design files (platform default)");
}
}
else {
_fileEncoding = System.getProperty("file.encoding");
_log.warn("Error in design of " + _db.getDbReference() + ": According to the design definition the design file encoding is configured in design configuration (csconfig.xml), but there is no csconfig.xml");
_log.warn("WGA will use the platform encoding '" + _fileEncoding + "' for reading text files");
}
}
else {
_fileEncoding = _syncInfo.getFileEncoding();
_log.debug("Design of " + _db.getDbReference() + " uses file encoding '" + _fileEncoding + "' to read design files (as specified in design configuration)");
}
}
protected List<ModuleFile> getModuleFiles(int type, FileObject dir, String category) throws WGDesignSyncException, FileSystemException {
return getModuleFiles(type, dir, "", category);
}
private List<ModuleFile> getModuleFiles(int type, FileObject dir, String prefix, String category) throws WGDesignSyncException, FileSystemException {
List<ModuleFile> moduleFiles = new ArrayList<ModuleFile>();
if (dir == null || !dir.exists()) {
return moduleFiles;
}
FileObject[] files = dir.getChildren();
if (files == null) {
throw new WGDesignSyncException("Cannot collect files from directory '" + dir.getName().getPathDecoded() + "'. Please verify directory existence.");
}
FileObject file;
for (int i = 0; i < files.length; i++) {
file = files[i];
if (!isValidDesignFile(file)) {
continue;
}
if (file.getType().equals(FileType.FOLDER)) {
if (file.getName().getBaseName().equals(DesignDirectory.NAME_METADATADIR)) {
continue;
}
String newPrefix = (prefix.trim().equals("") ? file.getName().getBaseName().toLowerCase() : prefix + DIRECTORY_DIVIDER + file.getName().getBaseName().toLowerCase());
moduleFiles.addAll(getModuleFiles(type, file, newPrefix, category));
}
else {
// Test the file suffix
if (type == WGDocument.TYPE_TML) {
if (!file.getName().getBaseName().contains(DesignDirectory.SUFFIX_TML)) {
continue;
}
}
else if (type == WGDocument.TYPE_CSSJS) {
ScriptInformation scriptInfo = DesignDirectory.getScriptInformationBySuffix("." + file.getName().getExtension());
if (scriptInfo == null) {
continue;
}
}
moduleFiles.add(new ModuleFile(file, prefix, category, type));
}
}
return moduleFiles;
}
/**
* @return Returns the tmlFolder.
* @throws WGDesignSyncException
*/
public FileObject getTmlFolder() throws WGDesignSyncException {
if (_fsResources != null) {
return _fsResources.getTmlFolder();
}
else {
throw new WGDesignSyncException("The base folder was retrieved from an illegal code position");
}
}
public boolean isValidDesignFile(FileObject file) {
return !file.getName().getBaseName().startsWith(".") && _core.getDesignFileValidator().isValidFileName(file.getName().getBaseName());
}
public WGACore getWGACore() {
return _core;
}
public DesignDefinition getSyncInfo() {
return _syncInfo;
}
protected String getFileEncoding() {
return _fileEncoding;
}
protected FileObject getBaseFolder() throws WGDesignSyncException {
if (_fsResources != null) {
return _fsResources.getBaseFolder();
}
else {
throw new WGDesignSyncException("The base folder was retrieved from an illegal code position");
}
}
protected DESEncrypter getCipher() {
return _cipher;
}
public String getDesignPath() {
return _designPath;
}
public Logger getLog() {
return _log;
}
public boolean isDirectAccessDefault() {
return _directAccessDefault;
}
public Set<Integer> getSyncedDoctypes() {
return _syncedDoctypes;
}
public void databaseConnected(WGDatabaseEvent event) {
try {
fetchFileSystem(_core);
try {
init();
}
catch (Exception e) {
_log.error("Error initializing file system design", e);
}
}
catch (Exception e) {
_log.error("Error fetching file system for WGA design of lazily connected db", e);
}
}
public void databaseConnectionError(WGDatabaseEvent event) {
}
protected List<ModuleFile> getTMLModuleFiles() throws FileSystemException, WGDesignSyncException {
// Iterate thru media key folders
FileObject[] mediaKeyFolders = getTmlFolder().getChildren();
if (mediaKeyFolders == null) {
throw new WGDesignSyncException("Cannot collect modules from directory '" + getTmlFolder().getName().getPathDecoded() + "'. Please verify directory existence.");
}
FileObject mediaKeyFolder;
List<ModuleFile> files = new ArrayList();
for (int mediaKeyIdx = 0; mediaKeyIdx < mediaKeyFolders.length; mediaKeyIdx++) {
mediaKeyFolder = mediaKeyFolders[mediaKeyIdx];
if (!mediaKeyFolder.getType().equals(FileType.FOLDER)) {
continue;
}
if (!isValidDesignFile(mediaKeyFolder)) {
continue;
}
String mediaKey = mediaKeyFolder.getName().getBaseName().toLowerCase();
files.addAll(getModuleFiles(WGDocument.TYPE_TML, mediaKeyFolder, mediaKey));
}
return files;
}
protected List<ModuleFile> getScriptModuleFiles() throws WGDesignSyncException, FileSystemException {
List<ModuleFile> files = new ArrayList();
// Get all files from script folders
Iterator scriptInfos = DesignDirectory.getScriptTypes().values().iterator();
while (scriptInfos.hasNext()) {
ScriptInformation info = (ScriptInformation) scriptInfos.next();
files.addAll(getModuleFiles(WGDocument.TYPE_CSSJS, getScriptTypeFolder(info.getFolder()), info.getType()));
}
return files;
}
protected List<ModuleFile> getFileContainerFiles() throws FileSystemException, WGDesignSyncException {
FileObject filesFolder = getFilesFolder();
List<ModuleFile> designFiles = getFileContainerFiles(filesFolder, "");
return designFiles;
}
private List<ModuleFile> getFileContainerFiles(FileObject filesFolder, String prefix) throws FileSystemException {
FileObject[] files = filesFolder.getChildren();
List<ModuleFile> designFiles = new ArrayList<ModuleFile>();
for (int i = 0; i < files.length; i++) {
FileObject file = files[i];
if (file.getType().equals(FileType.FOLDER) && isValidDesignFile(file)) {
designFiles.add(new ModuleFile(file, prefix, "", WGDocument.TYPE_FILECONTAINER));
String newPrefix = (prefix.trim().equals("") ? file.getName().getBaseName().toLowerCase() : prefix + DIRECTORY_DIVIDER + file.getName().getBaseName().toLowerCase());
designFiles.addAll(getFileContainerFiles(file, newPrefix));
}
}
return designFiles;
}
protected ModuleFile getFileContainerFile(String name) throws FileSystemException, WGDesignSyncException {
FileObject folder = getFilesFolder();
List<String> paths = WGUtils.deserializeCollection(name, ":");
boolean found = false;
for (String path : paths) {
FileObject fileContainer = findMatchingChild(folder, path, true);
if (fileContainer != null && fileContainer.getType().equals(FileType.FOLDER) && isValidDesignFile(fileContainer)) {
found = true;
folder = fileContainer;
}
else {
found = false;
break;
}
}
if (found == true) {
return new ModuleFile(folder, WGUtils.serializeCollection(paths.subList(0, paths.size() - 1), ":"), "", WGDocument.TYPE_FILECONTAINER);
}
else {
return null;
}
}
protected ModuleFile getScriptModuleFile(String name, String type) throws FileSystemException, WGDesignSyncException {
ScriptInformation info = DesignDirectory.getScriptInformation(type);
if (info == null) {
return null;
}
FileObject scriptTypeFolder = getScriptTypeFolder(info.getFolder());
if (scriptTypeFolder == null) {
return null;
}
return resolveModuleFile(scriptTypeFolder, name, info.getFolder(), WGDocument.TYPE_CSSJS);
}
protected ModuleFile getTMLModuleFile(String name, String strMediaKey) throws FileSystemException, WGDesignSyncException {
return resolveModuleFile(getTmlFolder().getChild(strMediaKey.toLowerCase()), name, strMediaKey, WGDocument.TYPE_TML);
}
private ModuleFile resolveModuleFile(FileObject folder, String name, String category, int type) throws FileSystemException {
if (folder == null || !folder.exists() || !folder.getType().equals(FileType.FOLDER)) {
return null;
}
String prefix;
String baseName;
String suffix = (type == WGDocument.TYPE_TML ? DesignDirectory.SUFFIX_TML : DesignDirectory.getScriptInformation(category).getSuffix());
int lastColon = name.lastIndexOf(":");
if (lastColon != -1) {
prefix = name.substring(0, lastColon);
baseName = name.substring(lastColon + 1) + suffix;
}
else {
prefix = "";
baseName = name + suffix;
}
// Find correct folder
if (!prefix.equals("")) {
List<String> path = WGUtils.deserializeCollection(prefix, ":");
Iterator<String> elems = path.iterator();
while (elems.hasNext()) {
String elem = (String) elems.next();
FileObject matchingChild = findMatchingChild(folder, elem, true);
// Use matching child as next folder
if (matchingChild != null && matchingChild.getType().equals(FileType.FOLDER)) {
folder = matchingChild;
}
// Exit if we did not find a matching folder
else {
return null;
}
}
}
// Find the file
FileObject file = findMatchingChild(folder, baseName, false);
if (file != null) {
return new ModuleFile(file, prefix, category, type);
}
else {
return null;
}
}
/**
* Does a case insensitive search for a file of the given name in the given
* folder
*
* @param folder
* The folder to search
* @param elem
* File name to search
* @return
* @throws FileSystemException
*/
private FileObject findMatchingChild(FileObject folder, String elem, boolean exactMatch) throws FileSystemException {
FileObject matchingChild = null;
// First try fast straight fetch using lowercase file
FileObject straightChild = folder.getChild(elem.toLowerCase());
if (straightChild != null && straightChild.exists()) {
matchingChild = straightChild;
}
else {
// Secondly we iterate over all children and to a case-insensitive
// equals
FileObject[] children = folder.getChildren();
for (FileObject child : children) {
if (exactMatch) {
if (child.getName().getBaseName().equalsIgnoreCase(elem)) {
matchingChild = child;
break;
}
}
else {
String fileName = child.getName().getBaseName().toLowerCase();
String elementName = elem.toLowerCase();
if (fileName.startsWith(elementName) && fileName.indexOf(".", elem.length()) == -1) {
matchingChild = child;
break;
}
}
}
}
return matchingChild;
}
protected static String getLOGGER_DESIGNSYNC() {
return LOGGER_DESIGNSYNC;
}
protected static String getSYNCSTATUS_MODULE() {
return SYNCSTATUS_MODULE;
}
protected static String getDIRECTORY_DIVIDER() {
return DIRECTORY_DIVIDER;
}
protected static Map<Integer, DesignInformation> getDesignTypes() {
return _designTypes;
}
protected static XStream getXstream() {
return _xstream;
}
protected Pattern getValidDesignName() {
return _validDesignName;
}
protected WGDatabase getDb() {
return _db;
}
protected String getDesignKey() {
return _designKey;
}
protected WGACore getCore() {
return _core;
}
protected StandardFileSystemManager getFsManager() {
return _fsManager;
}
protected static String getLOGGER_DESIGNSYNC_QUIET() {
return LOGGER_DESIGNSYNC_QUIET;
}
protected static String getMODE_FULL() {
return MODE_FULL;
}
protected static String getMODE_VIRTUAL() {
return MODE_VIRTUAL;
}
protected static String getMODE_DIRECT() {
return MODE_DIRECT;
}
protected FileSystemResources getFsResources() {
return _fsResources;
}
protected CSConfig getCsconfig() {
return _csconfig;
}
protected Map<String, String> getDesignOptions() {
return _designOptions;
}
public String getJavaClassesPath() {
return _javaClassesPath;
}
public int designHashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((_designPath == null) ? 0 : _designPath.hashCode());
result = prime * result + ((_fileEncoding == null) ? 0 : _fileEncoding.hashCode());
return result;
}
}