diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/Cache.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/Cache.java
new file mode 100644
index 0000000000000000000000000000000000000000..5ef7dc81c66d1d759b4e8e206c02d22fdee5b48b
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/Cache.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.dss.generic.server.ftp;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
+
+/**
+ * Helper class to cache objects retrieved from remote services. Used by
+ * {@link FtpPathResolverContext}.
+ * 
+ * @author Franz-Josef Elmer
+ */
+public class Cache
+{
+    private final Map<String, DataSet> dataSets = new HashMap<String, DataSet>();
+    private final Map<String, ExternalData> externalData = new HashMap<String, ExternalData>();
+    private final Map<String, Experiment> experiments = new HashMap<String, Experiment>();
+    
+    void putDataSet(DataSet dataSet)
+    {
+        dataSets.put(dataSet.getCode(), dataSet);
+    }
+    
+    DataSet getDataSet(String dataSetCode)
+    {
+        return dataSets.get(dataSetCode);
+    }
+
+    ExternalData getExternalData(String code)
+    {
+        return externalData.get(code);
+    }
+
+    void putDataSet(ExternalData dataSet)
+    {
+        externalData.put(dataSet.getCode(), dataSet);
+    }
+
+    Experiment getExperiment(String experimentId)
+    {
+        return experiments.get(experimentId);
+    }
+
+    void putDataSet(Experiment experiment)
+    {
+        experiments.put(experiment.getIdentifier(), experiment);
+    }
+    
+}
\ No newline at end of file
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java
index 75057e7aedb484197264aacd75266b5051fefd2f..49f25bdab30561ad0655bb26238bba85faa72eda 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/DSSFileSystemView.java
@@ -131,6 +131,11 @@ public class DSSFileSystemView implements FileSystemView
     }
 
     public FtpFile getFile(String path) throws FtpException
+    {
+        return getFile(path, new Cache());
+    }
+    
+    public FtpFile getFile(String path, Cache cache) throws FtpException
     {
         String normalizedPath = normalizePath(path);
 
@@ -144,7 +149,7 @@ public class DSSFileSystemView implements FileSystemView
         {
             FtpPathResolverContext context =
                     new FtpPathResolverContext(sessionToken, service, generalInfoService,
-                            pathResolverRegistry);
+                            pathResolverRegistry, cache);
             return pathResolverRegistry.resolve(normalizedPath, context);
         } catch (RuntimeException rex)
         {
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java
index 17d97ea23846f66b8697a2d6ed48a3445537fdb8..292b8ea7715169fab06cc91ae7569bf876c553b7 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpPathResolverContext.java
@@ -16,8 +16,19 @@
 
 package ch.systemsx.cisd.openbis.dss.generic.server.ftp;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
 import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentFetchOptions;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
+import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory;
 
 /**
  * An object holding all necessary context information for ftp path resolution.
@@ -35,13 +46,16 @@ public class FtpPathResolverContext
     
     private final IFtpPathResolverRegistry resolverRegistry;
 
+    private final Cache cache;
+
     public FtpPathResolverContext(String sessionToken, IETLLIMSService service, IGeneralInformationService generalInfoService,
-            IFtpPathResolverRegistry resolverRegistry)
+            IFtpPathResolverRegistry resolverRegistry, Cache cache)
     {
         this.sessionToken = sessionToken;
         this.service = service;
         this.generalInfoService = generalInfoService;
         this.resolverRegistry = resolverRegistry;
+        this.cache = cache;
     }
 
     public String getSessionToken()
@@ -53,6 +67,65 @@ public class FtpPathResolverContext
     {
         return service;
     }
+    
+    public DataSet getDataSet(String dataSetCode)
+    {
+        DataSet dataSet = cache.getDataSet(dataSetCode);
+        if (dataSet == null)
+        {
+            List<DataSet> dataSetsWithMetaData =
+                    generalInfoService.getDataSetMetaData(sessionToken,
+                            Arrays.asList(dataSetCode));
+            dataSet = dataSetsWithMetaData.get(0);
+            cache.putDataSet(dataSet);
+        }
+        return dataSet;
+    }
+    
+    public List<ExternalData> listDataSetsByCode(List<String> codes)
+    {
+        List<String> codesToAskFor = new ArrayList<String>();
+        List<ExternalData> dataSets = new ArrayList<ExternalData>();
+        for (String code : codes)
+        {
+            ExternalData dataSet = cache.getExternalData(code);
+            if (dataSet == null)
+            {
+                codesToAskFor.add(code);
+            } else
+            {
+                dataSets.add(dataSet);
+            }
+        }
+        if (codesToAskFor.isEmpty() == false)
+        {
+            List<ExternalData> newDataSets = service.listDataSetsByCode(sessionToken, codesToAskFor);
+            for (ExternalData newDataSet : newDataSets)
+            {
+                cache.putDataSet(newDataSet);
+                dataSets.add(newDataSet);
+            }
+        }
+        return dataSets;
+    }
+    
+    public Experiment getExperiment(String experimentId)
+    {
+        Experiment experiment = cache.getExperiment(experimentId);
+        if (experiment == null)
+        {
+            ExperimentIdentifier experimentIdentifier =
+                    new ExperimentIdentifierFactory(experimentId).createIdentifier();
+            
+            List<Experiment> result =
+                    service.listExperiments(sessionToken,
+                            Collections.singletonList(experimentIdentifier),
+                            new ExperimentFetchOptions());
+            experiment = result.isEmpty() ? null : result.get(0);
+            cache.putDataSet(experiment);
+        }
+        return experiment;
+    }
 
     public IGeneralInformationService getGeneralInfoService()
     {
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java
index 95fac237ab66851bfcce9946cea70012e1bd8a2f..8f99c0b1bb4ea536e4cf143e157d533c6878ac71 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/FtpServer.java
@@ -45,7 +45,6 @@ import org.apache.ftpserver.ftplet.AuthenticationFailedException;
 import org.apache.ftpserver.ftplet.DefaultFtpReply;
 import org.apache.ftpserver.ftplet.DefaultFtplet;
 import org.apache.ftpserver.ftplet.FileSystemFactory;
-import org.apache.ftpserver.ftplet.FileSystemView;
 import org.apache.ftpserver.ftplet.FtpException;
 import org.apache.ftpserver.ftplet.FtpFile;
 import org.apache.ftpserver.ftplet.FtpRequest;
@@ -250,7 +249,7 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
         }
     }
 
-    public FileSystemView createFileSystemView(User user) throws FtpException
+    public DSSFileSystemView createFileSystemView(User user) throws FtpException
     {
         if (user instanceof FtpUser)
         {
@@ -269,7 +268,7 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
         User user = session.getAttribute(USER_KEY);
         try
         {
-            final FileSystemView view = createFileSystemView(user);
+            final DSSFileSystemView view = createFileSystemView(user);
             return new org.apache.sshd.server.FileSystemView()
                 {
                     
@@ -280,7 +279,7 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
                     
                     public SshFile getFile(String file)
                     {
-                        return new FileView(view, file);
+                        return new FileView(view, file, new Cache());
                     }
                 };
         } catch (FtpException ex)
@@ -288,18 +287,20 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
             throw new IOException(ex.getMessage());
         }
     }
-
+    
     private static final class FileView implements SshFile
     {
-        private final FileSystemView fileView;
+        private final DSSFileSystemView fileView;
         private final String path;
         private final List<InputStream> inputStreams = new ArrayList<InputStream>();
         private FtpFile file;
+        private final Cache cache;
 
-        FileView(FileSystemView fileView, String path)
+        FileView(DSSFileSystemView fileView, String path, Cache cache)
         {
             this.fileView = fileView;
             this.path = path;
+            this.cache = cache;
         }
         
         private FtpFile getFile()
@@ -308,7 +309,7 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
             {
                 try
                 {
-                    file = fileView.getFile(path);
+                    file = fileView.getFile(path, cache);
                 } catch (FtpException ex)
                 {
                     throw CheckedExceptionTunnel.wrapIfNecessary(ex);
@@ -413,7 +414,7 @@ public class FtpServer implements FileSystemFactory, org.apache.sshd.server.File
             List<SshFile> result = new ArrayList<SshFile>();
             for (FtpFile child : files)
             {
-                result.add(new FileView(fileView, child.getAbsolutePath()));
+                result.add(new FileView(fileView, child.getAbsolutePath(), cache));
             }
             return result;
         }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java
index b319876429277cccdf34d873b45860355376e55a..7dea42bfc1f27c9b365b496b89ef2f8308bce9fb 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolver.java
@@ -17,7 +17,6 @@
 package ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
@@ -49,14 +48,10 @@ import ch.systemsx.cisd.openbis.dss.generic.server.ftp.resolver.FtpFileEvaluatio
 import ch.systemsx.cisd.openbis.dss.generic.shared.IHierarchicalContentProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
 import ch.systemsx.cisd.openbis.generic.shared.IETLLIMSService;
-import ch.systemsx.cisd.openbis.generic.shared.api.v1.IGeneralInformationService;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentFetchOptions;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
-import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifier;
-import ch.systemsx.cisd.openbis.generic.shared.dto.identifier.ExperimentIdentifierFactory;
 
 /**
  * Resolves paths like
@@ -114,14 +109,7 @@ public class TemplateBasedDataSetResourceResolver implements IFtpPathResolver,
             List<FtpFile> result = new ArrayList<FtpFile>();
             if (showParentsAndChildren)
             {
-                IGeneralInformationService generalInfoService =
-                        resolverContext.getGeneralInfoService();
-                String sessionToken = resolverContext.getSessionToken();
-                List<DataSet> dataSetsWithMetaData =
-                        generalInfoService.getDataSetMetaData(sessionToken,
-                                Arrays.asList(dataSet.getCode()));
-
-                DataSet dataSetWithMetaData = dataSetsWithMetaData.get(0);
+                DataSet dataSetWithMetaData = resolverContext.getDataSet(dataSet.getCode());
                 addNodesOfType(PARENT_PREFIX, result, dataSetWithMetaData.getParentCodes());
                 addNodesOfType(CHILD_PREFIX, result, dataSetWithMetaData.getChildrenCodes());
             }
@@ -156,9 +144,7 @@ public class TemplateBasedDataSetResourceResolver implements IFtpPathResolver,
             {
                 return;
             }
-            String sessionToken = resolverContext.getSessionToken();
-            List<ExternalData> dataSets =
-                    resolverContext.getService().listDataSetsByCode(sessionToken, dataSetCodes);
+            List<ExternalData> dataSets = resolverContext.listDataSetsByCode(dataSetCodes);
             for (int i = 0; i < dataSets.size(); i++)
             {
                 ExternalData ds = dataSets.get(i);
@@ -248,7 +234,7 @@ public class TemplateBasedDataSetResourceResolver implements IFtpPathResolver,
         IETLLIMSService service = resolverContext.getService();
         String sessionToken = resolverContext.getSessionToken();
 
-        Experiment experiment = tryGetExperiment(experimentId, service, sessionToken);
+        Experiment experiment = tryGetExperiment(experimentId, resolverContext);
         if (experiment == null)
         {
             return FtpPathResolverRegistry.getNonExistingFile(path, "Unknown experiment '"
@@ -403,30 +389,15 @@ public class TemplateBasedDataSetResourceResolver implements IFtpPathResolver,
         return result;
     }
 
-    private Experiment tryGetExperiment(String experimentId, IETLLIMSService service,
-            String sessionToken)
+    private Experiment tryGetExperiment(String experimentId, FtpPathResolverContext context)
     {
-        ExperimentIdentifier experimentIdentifier =
-                new ExperimentIdentifierFactory(experimentId).createIdentifier();
-
-        List<Experiment> result = null;
         try
         {
-            result =
-                    service.listExperiments(sessionToken,
-                            Collections.singletonList(experimentIdentifier),
-                            new ExperimentFetchOptions());
+            return context.getExperiment(experimentId);
         } catch (Throwable t)
         {
             operationLog.warn("Failed to get experiment with identifier :" + experimentId, t);
-        }
-
-        if (result == null || result.isEmpty())
-        {
             return null;
-        } else
-        {
-            return result.get(0);
         }
     }
 
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java
index f33201490cdf1b0ce9cbbc3d1731cf8efb298b67..140461400c0370121b41506c730bc49d79b7db05 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/generic/server/ftp/resolver/TemplateBasedDataSetResourceResolverTest.java
@@ -44,6 +44,7 @@ import ch.systemsx.cisd.common.io.hierarchical_content.api.IHierarchicalContent;
 import ch.systemsx.cisd.common.io.hierarchical_content.api.IHierarchicalContentNode;
 import ch.systemsx.cisd.common.test.TrackingMockery;
 import ch.systemsx.cisd.common.utilities.IDelegatedAction;
+import ch.systemsx.cisd.openbis.dss.generic.server.ftp.Cache;
 import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpConstants;
 import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpPathResolverContext;
 import ch.systemsx.cisd.openbis.dss.generic.server.ftp.FtpServerConfig;
@@ -166,7 +167,7 @@ public class TemplateBasedDataSetResourceResolverTest extends AbstractFileSystem
         simpleFileContentProvider = new SimpleFileContentProvider(root);
 
         resolverContext =
-                new FtpPathResolverContext(SESSION_TOKEN, service, generalInfoService, null);
+                new FtpPathResolverContext(SESSION_TOKEN, service, generalInfoService, null, new Cache());
         context.checking(new Expectations()
             {
                 {