Index: DefaultDatabaseWorkspaceManager.java
===================================================================
--- DefaultDatabaseWorkspaceManager.java	(revision 44396)
+++ DefaultDatabaseWorkspaceManager.java	(revision 44397)
@@ -1,12 +1,18 @@
 package org.gvsig.fmap.dal.impl;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 import org.gvsig.fmap.dal.DatabaseWorkspaceManager;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.mutable.Mutable;
 import org.apache.commons.lang3.mutable.MutableObject;
 import org.gvsig.expressionevaluator.ExpressionBuilder;
 import org.gvsig.expressionevaluator.ExpressionUtils;
+import org.gvsig.expressionevaluator.SymbolTable;
+import org.gvsig.expressionevaluator.spi.AbstractSymbolTable;
 import org.gvsig.fmap.dal.DALLocator;
 import org.gvsig.fmap.dal.DataManager;
 import org.gvsig.fmap.dal.DataServerExplorer;
@@ -33,8 +39,15 @@
 import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.TABLE_RESOURCES;
 import static org.gvsig.fmap.dal.DatabaseWorkspaceManager.TABLE_RESOURCES_NAME;
 import org.gvsig.fmap.dal.feature.NewFeatureStoreParameters;
+import org.gvsig.tools.dynobject.DynClass;
+import org.gvsig.tools.dynobject.DynField;
+import org.gvsig.tools.exception.BaseException;
 import org.gvsig.tools.resourcesstorage.FilesResourcesStorage;
 import org.gvsig.tools.resourcesstorage.ResourcesStorage;
+import org.gvsig.tools.util.FileTools;
+import org.gvsig.tools.util.HasAFile;
+import org.gvsig.tools.visitor.VisitCanceledException;
+import org.gvsig.tools.visitor.Visitor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -44,19 +57,77 @@
  */
 @SuppressWarnings("UseSpecificCatch")
 public class DefaultDatabaseWorkspaceManager 
-        implements DatabaseWorkspaceManager 
+        extends AbstractSymbolTable
+        implements DatabaseWorkspaceManager
     {
     
     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDatabaseWorkspaceManager.class);
+
+    private static final String CONFIG_NAME_BASEFOLDER = "BASEFOLDER";
+    private static final String CONFIG_NAME_ALTERNATIVE_RESOURCES_PATH = "ALTERNATIVE_RESOURCES_PATH";
     
+    private static abstract class CachedValue<T> {
+
+        protected T value = null;
+        private long lastAccess = 0;
+        private final int millis;
+
+        protected CachedValue(int millis) {
+            this.millis = millis;
+        }
+        
+        protected abstract void reload();
+
+        public boolean isExpired() {
+            long now = System.currentTimeMillis();
+            return now - lastAccess > millis;
+        }
+
+        public T get() {
+            if (isExpired()) {
+                reload();
+            }
+            lastAccess = System.currentTimeMillis();
+            return value;
+        }
+        
+        public void set(T value) {
+            this.value = value;
+            lastAccess = System.currentTimeMillis();
+        }
+    }
+
+    private class BaseFolder extends CachedValue<File> {
+        public BaseFolder() {
+            super(3000);
+        }
+
+        @Override
+        protected void reload() {
+            String s = DefaultDatabaseWorkspaceManager.this.get(CONFIG_NAME_BASEFOLDER);
+            if( StringUtils.isBlank(s) ) {
+                this.value = null;
+            } else {
+                this.value = new File(s);
+            }
+        }
+
+        @Override
+        public void set(File value) {
+            DefaultDatabaseWorkspaceManager.this.set(CONFIG_NAME_BASEFOLDER, value.toString());
+            super.set(value);
+        }
+    }
+    
     private final DataServerExplorerParameters serverParameters;
     private Boolean existsConfiguration = null;
     private StoresRepository storesRepository;
     private Mutable<File> alternativeResourcesFolder = null;
+    private BaseFolder baseFolder = null;
     
-    
     public DefaultDatabaseWorkspaceManager(DataServerExplorerParameters serverParameters) {
         this.serverParameters = serverParameters;
+        this.baseFolder = new BaseFolder();
     }
 
     @Override
@@ -289,6 +360,7 @@
                 return null;
             }
             String value = feature.getString(FIELD_CONFIGURATION_VALUE);
+            value = ExpressionUtils.evaluateDynamicText(this, value);
             return value;
             
         } catch (Exception ex) {
@@ -341,7 +413,7 @@
             this.storesRepository = new DatabaseWorkspaceStoresRepository(
                     id,
                     label,
-                    this.serverParameters
+                    this
             );
         }
         return this.storesRepository;
@@ -358,8 +430,50 @@
     }
 
     @Override
+    public File getBaseFolder() {
+        return this.baseFolder.get();
+    }
+    
+    @Override
+    public void setBaseFolder(File baseFolder) {
+        this.baseFolder.set(baseFolder);
+    }
+
+    private DataStoreParameters replaceInFilesToUseBaseFolder(DataStoreParameters parameters) {
+        DynClass dynClass = parameters.getDynClass();
+        if( dynClass == null ) {
+            return parameters;
+        }
+        File theBaseFolder = this.getBaseFolder();
+        if( theBaseFolder==null ) {
+            return parameters;
+        }
+        DataStoreParameters replaced = parameters;
+        for (DynField dynField : dynClass.getDynFields()) {
+            switch(dynField.getType()) {
+                case DataTypes.FILE:
+                case DataTypes.FOLDER:
+                    File f = (File) parameters.getDynValue(dynField.getName());
+                    if( f!=null ) {
+                        File f2 = FileTools.relativizeFile(theBaseFolder, f);
+                        if( f2!=f ) {
+                            f = new File("<%="+CONFIG_NAME_BASEFOLDER+"%>/"+f2.getPath());
+                            if( replaced == parameters ) {
+                                replaced = (DataStoreParameters) replaced.getCopy();
+                            }
+                            replaced.setDynValue(dynField.getName(), f);
+                        }
+                    }
+                    break;
+            }
+        }
+        return replaced;
+    }
+    
+    @Override
     public boolean writeStoresRepositoryEntry(String name, DataStoreParameters parameters) {
         try {
+            parameters = this.replaceInFilesToUseBaseFolder(parameters);
             byte[] data = parameters.toByteArray();
             if( data == null ) {
                 throw new RuntimeException("Can't convert parameters to byte array.");
@@ -417,7 +531,7 @@
     public ResourcesStorage getAlternativeResourcesStorage(String tableName) {
         if( this.alternativeResourcesFolder==null ) {
             this.alternativeResourcesFolder = new MutableObject<>(null);
-            String resourcesPath = this.get("ALTERNATIVE_RESOURCES_PATH");
+            String resourcesPath = this.get(CONFIG_NAME_ALTERNATIVE_RESOURCES_PATH);
             if( !StringUtils.isBlank(resourcesPath) ) {
                 try {
                     resourcesPath = (String) ExpressionUtils.evaluate(resourcesPath);
@@ -467,6 +581,76 @@
         this.set(CONFIG_NAME_STORESREPOSITORYID,id);
         this.set(CONFIG_NAME_STORESREPOSITORYLABEL,description);
         this.set(CONFIG_CAN_ANONYMOUS_USER_WRITE_IN_THE_TABLES,"true");
-        this.set("ALTERNATIVE_RESOURCES_PATH","");
+        this.set(CONFIG_NAME_ALTERNATIVE_RESOURCES_PATH,"");
+        File theBaseFolder = this.getBaseFolder();
+        if( this.serverParameters instanceof HasAFile && theBaseFolder==null ) {
+            File f = ((HasAFile)this.serverParameters).getFile();
+            if( f != null ) {
+                theBaseFolder = f.getParentFile();
+                this.setBaseFolder(theBaseFolder);
+            } else {
+                this.set(CONFIG_NAME_BASEFOLDER,"");
+            }
+        } else {
+            this.set(CONFIG_NAME_BASEFOLDER,"");
+        }
     }
+
+    @Override
+    public boolean exists(String name) {
+        try {
+            if (this.existsConfiguration()) {
+                FeatureStore store = this.getTable(TABLE_CONFIGURATION);
+                if (store != null) {
+                    ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
+                    String filter = builder.eq(
+                            builder.column(FIELD_CONFIGURATION_NAME),
+                            builder.constant(name)
+                    ).toString();
+                    Feature feature = store.findFirst(filter);
+                    if (feature != null) {
+                        return true;
+                    }
+                }
+            }
+            return super.exists(name);
+        } catch (Exception ex) {
+            LOGGER.warn("Can't read configuration value '" + name + "'", ex);
+            return false;
+        }
+    }
+
+    @Override
+    public Object value(String name) {
+        String v = this.get(name);
+        if( v!=null ) {
+            return v;
+        }
+        return super.value(name);
+    }
+
+    @Override
+    public Collection<String> localvariables() {
+        try {
+            final List<String> theVars = new ArrayList<>();
+            if (this.existsConfiguration()) {
+                FeatureStore store = this.getTable(TABLE_CONFIGURATION);
+                if (store != null) {
+                    store.accept(new Visitor() {
+                        @Override
+                        public void visit(Object obj) throws VisitCanceledException, BaseException {
+                            Feature feature = (Feature) obj;
+                            theVars.add(feature.getString(FIELD_CONFIGURATION_NAME));
+                        }
+                    });
+                }
+            }
+            return theVars;
+        } catch (Exception ex) {
+            LOGGER.warn("Can't read configuration variables", ex);
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+    
 }
