/*******************************************************************************
* 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.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.vfs.AllFileSelector;
import org.apache.commons.vfs.Capability;
import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileType;
import org.apache.log4j.Logger;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGAuthorisationException;
import de.innovationgate.webgate.api.WGBackendException;
import de.innovationgate.webgate.api.WGCreationException;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDesignChangeListener;
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.WGSessionContext;
import de.innovationgate.wga.common.DesignDirectory;
import de.innovationgate.wga.common.beans.csconfig.v1.InvalidCSConfigVersionException;
import de.innovationgate.wga.config.DesignReference;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.design.DesignProviderCoreWrapper;
import de.innovationgate.wgpublisher.design.OverlayDesignProvider;
import de.innovationgate.wgpublisher.design.WGADesignProvider;
import de.innovationgate.wgpublisher.design.sync.WGDesignSyncException;
public class FileSystemDesignProvider extends FileSystemDesignManager implements WGADesignProvider {
private boolean _strictFCDateDetermination;
private DesignReference _designReference;
private String _variantSuffix;
public FileSystemDesignProvider(DesignReference ref, WGACore core, WGDatabase db, String path, Map<String,String> options) throws WGDesignSyncException, IOException, WGAPIException, InstantiationException,
IllegalAccessException, InvalidCSConfigVersionException {
super(core, db, path, options);
_designReference = ref;
_name = "Design Directory " + getBaseFolder().getURL().toString();
_strictFCDateDetermination = true;
_lookupVariants = WGUtils.getBooleanMapValue(options, OPTION_DESIGNVARIANTS, false);
_variantSuffix = "." + getDB().getDbReference();
}
private String _name;
public static final String OPTION_SYNC = "sync";
public boolean providesType(int type) {
return _syncedDoctypes.contains(new Integer(type));
}
public void addDesignChangeListener(WGDesignChangeListener changeListener) {
}
public WGDocumentCore createDesignDocument(int type, String name, String mediaKey) throws WGAuthorisationException, WGCreationException {
try {
switch (type) {
case WGDocument.TYPE_TML:{
FileObject newTml = getTmlFolder().resolveFile(mediaKey.toLowerCase() + "/" + name.replace(":", "/") + ".tml");
if (newTml.exists()) {
throw new WGCreationException("Document already exists: " + (new WGDocumentKey(type, name, mediaKey)).toString());
}
newTml.createFile();
break;
}
case WGDocument.TYPE_CSSJS: {
String suffix = DesignDirectory.getScriptInformation(mediaKey).getSuffix();
FileObject newScript = getScriptTypeFolder(mediaKey).resolveFile(mediaKey.toLowerCase() + "/" + name.replace(":", "/") + suffix);
if (newScript.exists()) {
throw new WGCreationException("Document already exists: " + (new WGDocumentKey(type, name, mediaKey)).toString());
}
newScript.createFile();
break;
}
case WGDocument.TYPE_FILECONTAINER: {
FileObject newFC = getFilesFolder().resolveFile(name.replace(":", "/"));
if (newFC.exists()) {
throw new WGCreationException("Document already exists: " + (new WGDocumentKey(type, name, mediaKey)).toString());
}
newFC.createFolder();
break;
}
}
return getDesignObject(type, name, mediaKey);
}
catch (Exception e) {
throw new WGCreationException("Exception creating design document " + (new WGDocumentKey(type, name, mediaKey)).toString(), e);
}
}
public void dispose() {
close();
}
public WGDocumentCore getDesignObject(int type, String name, String strMediaKey) throws WGBackendException {
try {
switch (type) {
case WGDocument.TYPE_TML: {
ModuleFile file = getTMLModuleFile(name, strMediaKey);
if (file == null || !file.getFile().exists()) {
refreshIfUpdatable(getFsResources().getTmlFolder());
file = getTMLModuleFile(name, strMediaKey);
}
if (file != null) {
return wrapVariantCore(new DesignFileDocument(this, file));
}
break;
}
case WGDocument.TYPE_CSSJS: {
ModuleFile file = getScriptModuleFile(name, strMediaKey);
if (file == null || !file.getFile().exists()) {
refreshIfUpdatable(getFsResources().getScriptFolder());
file = getScriptModuleFile(name, strMediaKey);
}
if (file != null) {
return wrapVariantCore(new DesignFileDocument(this, file));
}
break;
}
case WGDocument.TYPE_FILECONTAINER: {
ModuleFile file = getFileContainerFile(name);
if (file == null || !file.getFile().exists()) {
refreshIfUpdatable(getFsResources().getFilesFolder());
file = getFileContainerFile(name);
}
if (file != null) {
return wrapVariantCore(new DesignFileDocument(this, file));
}
break;
}
}
return null;
}
catch (Exception e) {
throw new WGBackendException("Exception retrieving design document '" + name + "' of type " + WGDocument.doctypeNumberToName(type), e);
}
}
public WGDocumentCore wrapVariantCore(WGDocumentCore core) throws WGAPIException {
if (_lookupVariants) {
String name = (String) core.getMetaData(WGDesignDocument.META_NAME);
if (name.endsWith(_variantSuffix)) {
return new DesignProviderCoreWrapper(core, this, true, false);
}
else {
return core;
}
}
else {
return core;
}
}
@Override
protected ModuleFile getFileContainerFile(String name) throws FileSystemException, WGDesignSyncException {
if (_lookupVariants) {
ModuleFile file = super.getFileContainerFile(name + _variantSuffix);
if (file != null) {
return file;
}
}
return super.getFileContainerFile(name);
}
@Override
protected ModuleFile getScriptModuleFile(String name, String codetype) throws FileSystemException, WGDesignSyncException {
if (_lookupVariants) {
ModuleFile file = super.getScriptModuleFile(name + _variantSuffix, codetype);
if (file != null) {
return file;
}
}
return super.getScriptModuleFile(name, codetype);
}
@Override
protected ModuleFile getTMLModuleFile(String name, String strMediaKey) throws FileSystemException, WGDesignSyncException {
if (_lookupVariants) {
ModuleFile file = super.getTMLModuleFile(name + _variantSuffix, strMediaKey);
if (file != null) {
return file;
}
}
return super.getTMLModuleFile(name, strMediaKey);
}
public List<WGDocumentCore> getDesignObjects(int type) throws WGBackendException {
try {
switch (type) {
case WGDocument.TYPE_TML: {
refreshIfUpdatable(getFsResources().getTmlFolder());
List<ModuleFile> files = getTMLModuleFiles();
return wrapModuleFiles(files);
}
case WGDocument.TYPE_CSSJS: {
refreshIfUpdatable(getFsResources().getScriptFolder());
List<ModuleFile> files = getScriptModuleFiles();
return wrapModuleFiles(files);
}
case WGDocument.TYPE_FILECONTAINER: {
refreshIfUpdatable(getFsResources().getFilesFolder());
List<ModuleFile> files = getFileContainerFiles();
return wrapFileContainerFiles(files);
}
}
return null;
}
catch (Exception e) {
throw new WGBackendException("Exception retrieving design document list of type " + WGDocument.doctypeNumberToName(type), e);
}
}
private void refreshIfUpdatable(FileObject tmlFolder) throws FileSystemException {
if (getFsResources().isUpdateableFileSystem()) {
tmlFolder.refresh();
}
}
public String getName() {
return _name;
}
public boolean isNotifying() {
return false;
}
public boolean isProviderCore(WGDocumentCore core) {
return (core instanceof DesignFileDocument);
}
public void removeDesignChangeListener(WGDesignChangeListener changeListener) {
}
protected List<WGDocumentCore> wrapFileContainerFiles(List<ModuleFile> files) {
List<WGDocumentCore> list = new ArrayList<WGDocumentCore>();
Set<String> addedNames = new HashSet<String>();
for (ModuleFile file : files) {
try {
WGDocumentCore core = wrapVariantCore(new DesignFileDocument(this, file));
// Filter out base versions of variants
if (_lookupVariants) {
String coreName = (String) core.getMetaData(WGDesignDocument.META_NAME);
// If we try to add a doc that already has been added and is NO variant, we know that this is the base version which was
// overwritten by a variant. So we skip it.
if (addedNames.contains(coreName) && !Boolean.TRUE.equals(core.getMetaData(WGDesignDocument.META_VARIANT))) {
continue;
}
else {
addedNames.add(coreName);
}
}
list.add(core);
}
catch (Exception e) {
getLog().error("Exception wrapping file container file as WGAPI document: " + file.getFile().getName().getPath(), e);
}
}
return list;
}
protected List<WGDocumentCore> wrapModuleFiles(List<ModuleFile> files) {
Iterator<ModuleFile> it = files.iterator();
Set<String> addedNames = new HashSet<String>();
List<WGDocumentCore> list = new ArrayList<WGDocumentCore>();
while (it.hasNext()) {
ModuleFile moduleFile = (ModuleFile) it.next();
try {
WGDocumentCore core = wrapVariantCore(new DesignFileDocument(this, moduleFile));
// Filter out base versions of variants
if (_lookupVariants) {
String coreName = (String) core.getMetaData(WGDesignDocument.META_NAME);
// If we try to add a doc that already has been added and is NO variant, we know that this is the base version which was
// overwritten by a variant. So we skip it.
if (addedNames.contains(coreName) && !Boolean.TRUE.equals(core.getMetaData(WGDesignDocument.META_VARIANT))) {
continue;
}
else {
addedNames.add(coreName);
}
}
list.add(core);
}
catch (Exception e) {
getLog().error("Exception wrapping module file as WGAPI document: " + moduleFile.getFile().getName().getPath(), e);
}
}
return list;
}
protected boolean isStrictFCDateDetermination() {
return _strictFCDateDetermination;
}
public WGDatabase getConsumerDatabase() {
return getDB();
}
public boolean isLookupVariants() {
return _lookupVariants;
}
public void closeSession() {
}
public void openSession(WGSessionContext context) {
if (_lookupVariants && getFsResources().isUpdateableFileSystem()) {
try {
getFsResources().refreshAll();
}
catch (FileSystemException e) {
// We dont want a failure of cache refreshing kill the whole session, so we log it instead
Logger.getLogger("wga.design").error("Exception refreshing file system design cache", e);
}
}
try {
updateConfiguration();
}
catch (Exception e) {
Logger.getLogger("wga.design").error("Exception updating file system design configuration", e);
}
}
public int designHashCode() {
return super.designHashCode();
}
public DesignReference getDesignReference() {
return _designReference;
}
public void importOverlayResources(FileSystemDesignProvider sourceDesignProvider) throws FileSystemException, WGDesignSyncException {
for (FileObject mediaKeyFolder : sourceDesignProvider.getTmlFolder().getChildren()) {
FileObject overlayFolder = mediaKeyFolder.resolveFile(OverlayDesignProvider.OVERLAY_FOLDER);
if (overlayFolder.exists()) {
FileObject targetMediaKeyFolder = getTmlFolder().resolveFile(mediaKeyFolder.getName().getBaseName());
copyNewResources(overlayFolder, targetMediaKeyFolder);
}
}
for (FileObject scriptTypeFolder : sourceDesignProvider.getScriptFolder().getChildren()) {
FileObject overlayFolder = scriptTypeFolder.resolveFile(OverlayDesignProvider.OVERLAY_FOLDER);
if (overlayFolder.exists()) {
FileObject targetScriptTypeFolder = getScriptFolder().resolveFile(scriptTypeFolder.getName().getBaseName());
copyNewResources(overlayFolder, targetScriptTypeFolder);
}
}
FileObject overlayFolder = sourceDesignProvider.getFilesFolder().resolveFile(OverlayDesignProvider.OVERLAY_FOLDER);
if (overlayFolder.exists()) {
FileObject targetFCFolder = getFilesFolder();
copyNewResources(overlayFolder, targetFCFolder);
}
// Copy an overlay flag file to the system file container
FileObject targetFCFolder = getFilesFolder();
FileObject systemFC = targetFCFolder.resolveFile("system");
if (!systemFC.exists()) {
systemFC.createFolder();
}
FileObject overlayFlag = systemFC.resolveFile("overlay.flg");
if (!overlayFlag.exists()) {
overlayFlag.createFile();
}
}
private void copyNewResources(FileObject source, FileObject target) throws FileSystemException, WGDesignSyncException {
for (FileObject sourceFile : source.getChildren()) {
if (!isValidDesignFile(sourceFile)) {
continue;
}
FileObject targetFile = target.resolveFile(sourceFile.getName().getBaseName());
if (sourceFile.getType().equals(FileType.FOLDER)) {
if (!targetFile.exists()) {
getLog().info("Adding new overlay folder " + getBaseFolder().getName().getRelativeName(targetFile.getName()));
targetFile.createFolder();
}
else if (targetFile.getType().equals(FileType.FILE)) {
throw new WGDesignSyncException("Unable to apply overlay. Folder '" + getBaseFolder().getName().getRelativeName(targetFile.getName()) + " already exists as file. Delete it enable overlay management again");
}
copyNewResources(sourceFile, targetFile);
}
else if (sourceFile.getType().equals(FileType.FILE)) {
if (!targetFile.exists()) {
getLog().info("Adding new overlay resource " + getBaseFolder().getName().getRelativeName(targetFile.getName()));
targetFile.copyFrom(sourceFile, new AllFileSelector());
}
}
}
}
}