diff --git a/Examples/CMakeLists.txt b/Examples/CMakeLists.txt
index 632511346882fbec57421a390eb7eec551814b4d..7db41df710f98228150f5d40ba0decdc44c71c5d 100644
--- a/Examples/CMakeLists.txt
+++ b/Examples/CMakeLists.txt
@@ -1,10 +1,14 @@
 
 gaudi_subdir(Examples v0r0)
 
+find_package(podio REQUIRED)
+find_package(plcio REQUIRED)
+
 set(Examples_srcs
     src/HelloWorld/*.cpp
     src/FirstSvc/*.cpp
     src/SecondAlg/*.cpp
+    src/PlcioTest/*.cpp
 )
 
 # Headers and Libraries
@@ -13,8 +17,8 @@ gaudi_install_headers(Examples)
 
 # Modules
 gaudi_add_module(Examples ${Examples_srcs}
-    INCLUDE_DIRS GaudiKernel
-    LINK_LIBRARIES GaudiKernel
+    INCLUDE_DIRS GaudiKernel FWCore ${plcio_INCLUDE_DIRS} ${podio_INCLUDE_DIRS}
+    LINK_LIBRARIES GaudiKernel FWCore ${podio_LIBRARIES} $ENV{PLCIO}/lib/libplcio.so
 )
 
 # Unit tests
@@ -23,3 +27,9 @@ gaudi_add_test(HelloAlg
 
 gaudi_add_test(SecondAlg
                FRAMEWORK options/secondalg.py)
+
+gaudi_add_test(PlcioWriteAlg
+               FRAMEWORK options/plcio_write.py)
+
+gaudi_add_test(PlcioReadAlg
+               FRAMEWORK options/plcio_read.py)
diff --git a/Examples/options/plcio_read.py b/Examples/options/plcio_read.py
new file mode 100644
index 0000000000000000000000000000000000000000..1ba37f81b99e83b4802b79b605f027cbcb2eab1e
--- /dev/null
+++ b/Examples/options/plcio_read.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+from Gaudi.Configuration import *
+
+from Configurables import CEPCDataSvc
+dsvc = CEPCDataSvc("EventDataSvc", input="test.root")
+
+from Configurables import PlcioReadAlg
+alg = PlcioReadAlg("PlcioReadAlg")
+
+from Configurables import PodioInput
+podioinput = PodioInput("PodioReader", collections=[
+    "MCParticleCol"
+    ])
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+ApplicationMgr( TopAlg = [podioinput, alg],
+                EvtSel = 'NONE',
+                EvtMax = 10,
+                ExtSvc = [dsvc],
+                OutputLevel=DEBUG
+)
diff --git a/Examples/options/plcio_write.py b/Examples/options/plcio_write.py
new file mode 100644
index 0000000000000000000000000000000000000000..49c02d77882edc85300a9d138ab1f44a7fc889f7
--- /dev/null
+++ b/Examples/options/plcio_write.py
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+
+from Gaudi.Configuration import *
+
+from Configurables import CEPCDataSvc
+dsvc = CEPCDataSvc("EventDataSvc")
+
+from Configurables import PlcioWriteAlg
+alg = PlcioWriteAlg("PlcioWriteAlg")
+
+from Configurables import PodioOutput
+out = PodioOutput("out")
+out.filename = "test.root"
+out.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+ApplicationMgr( TopAlg = [alg, out],
+                EvtSel = 'NONE',
+                EvtMax = 10,
+                ExtSvc=[dsvc],
+                OutputLevel=DEBUG
+)
diff --git a/Examples/src/PlcioTest/PlcioReadAlg.cpp b/Examples/src/PlcioTest/PlcioReadAlg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..224c7722d6fbf02a8a3931884d4320c6ccca9c16
--- /dev/null
+++ b/Examples/src/PlcioTest/PlcioReadAlg.cpp
@@ -0,0 +1,39 @@
+#include "PlcioReadAlg.h"
+#include "plcio/MCParticleCollection.h"
+
+DECLARE_COMPONENT(PlcioReadAlg)
+
+PlcioReadAlg::PlcioReadAlg(const std::string& name, ISvcLocator* svcLoc)
+    : GaudiAlgorithm(name, svcLoc)
+{
+    declareProperty("MCParticleCol", m_hdl, "MCParticle collection (input)");
+}
+
+StatusCode PlcioReadAlg::initialize()
+{
+    debug() << "begin initialize PlcioReadAlg" << endmsg;
+    return GaudiAlgorithm::initialize();
+}
+
+StatusCode PlcioReadAlg::execute()
+{
+    debug() << "begin execute PlcioReadAlg" << endmsg;
+    auto mcCol = m_hdl.get();
+
+    for ( auto p : *mcCol ) {
+        debug() << p.getObjectID().index << " : [";
+        for ( auto it = p.daughters_begin(), end = p.daughters_end(); it != end; ++it ) {
+            debug() << " " << it->getObjectID().index;
+        }
+        debug() << " ]; ";
+    }
+    debug() << endmsg;
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode PlcioReadAlg::finalize()
+{
+    debug() << "begin finalize PlcioReadAlg" << endmsg;
+    return GaudiAlgorithm::finalize();
+}
diff --git a/Examples/src/PlcioTest/PlcioReadAlg.h b/Examples/src/PlcioTest/PlcioReadAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..b4d58c29c77c3c8aa4c715f19275f3fd5c8e4736
--- /dev/null
+++ b/Examples/src/PlcioTest/PlcioReadAlg.h
@@ -0,0 +1,29 @@
+#ifndef TEST_PLCIO_WRITE_ALG_H
+#define TEST_PLCIO_WRITE_ALG_H
+
+#include "FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+
+namespace plcio {
+    class MCParticleCollection;
+}
+
+class PlcioReadAlg : public GaudiAlgorithm
+{
+        friend class AlgFactory<PlcioReadAlg>;
+
+    public :
+
+        PlcioReadAlg(const std::string& name, ISvcLocator* svcLoc);
+
+        virtual StatusCode initialize();
+        virtual StatusCode execute();
+        virtual StatusCode finalize();
+
+    private :
+
+        DataHandle<plcio::MCParticleCollection> m_hdl{"MCParticleCol", Gaudi::DataHandle::Reader, this};
+
+};
+
+#endif  // TEST_PLCIO_WRITE_ALG_H
diff --git a/Examples/src/PlcioTest/PlcioWriteAlg.cpp b/Examples/src/PlcioTest/PlcioWriteAlg.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..91b54a87fb36a9bd50915f943124f5a9b0fbfde7
--- /dev/null
+++ b/Examples/src/PlcioTest/PlcioWriteAlg.cpp
@@ -0,0 +1,44 @@
+#include "PlcioWriteAlg.h"
+#include "plcio/MCParticleCollection.h"
+
+DECLARE_COMPONENT(PlcioWriteAlg)
+
+PlcioWriteAlg::PlcioWriteAlg(const std::string& name, ISvcLocator* svcLoc)
+    : GaudiAlgorithm(name, svcLoc)
+{
+    declareProperty("MCParticleCol", m_hdl, "MCParticle collection (output)");
+}
+
+StatusCode PlcioWriteAlg::initialize()
+{
+    debug() << "begin initialize PlcioWriteAlg" << endmsg;
+    return GaudiAlgorithm::initialize();
+}
+
+StatusCode PlcioWriteAlg::execute()
+{
+    debug() << "begin execute PlcioWriteAlg" << endmsg;
+
+    auto mcCol = new plcio::MCParticleCollection;
+
+    auto p1 = mcCol->create();
+    auto p2 = mcCol->create();
+
+    for ( int i = 0; i < 4; ++i ) {
+        auto d = mcCol->create();
+        d.addParent(p1);
+        d.addParent(p2);
+        p1.addDaughter(d);
+        p2.addDaughter(d);
+    }
+
+    m_hdl.put(mcCol);
+
+    return StatusCode::SUCCESS;
+}
+
+StatusCode PlcioWriteAlg::finalize()
+{
+    debug() << "begin finalize PlcioWriteAlg" << endmsg;
+    return GaudiAlgorithm::finalize();
+}
diff --git a/Examples/src/PlcioTest/PlcioWriteAlg.h b/Examples/src/PlcioTest/PlcioWriteAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..053e49c986242acefbfcac943239505e69e2ffac
--- /dev/null
+++ b/Examples/src/PlcioTest/PlcioWriteAlg.h
@@ -0,0 +1,29 @@
+#ifndef TEST_PLCIO_WRITE_ALG_H
+#define TEST_PLCIO_WRITE_ALG_H
+
+#include "FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+
+namespace plcio {
+    class MCParticleCollection;
+}
+
+class PlcioWriteAlg : public GaudiAlgorithm
+{
+        friend class AlgFactory<PlcioWriteAlg>;
+
+    public :
+
+        PlcioWriteAlg(const std::string& name, ISvcLocator* svcLoc);
+
+        virtual StatusCode initialize();
+        virtual StatusCode execute();
+        virtual StatusCode finalize();
+
+    private :
+
+        DataHandle<plcio::MCParticleCollection> m_hdl{"MCParticleCol", Gaudi::DataHandle::Writer, this};
+
+};
+
+#endif  // TEST_PLCIO_WRITE_ALG_H
diff --git a/FWCore/CMakeLists.txt b/FWCore/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..07f309c5651a43f8fbaf587bd423d726d869cd31
--- /dev/null
+++ b/FWCore/CMakeLists.txt
@@ -0,0 +1,23 @@
+################################################################################
+# Package: FWCore
+################################################################################
+gaudi_subdir(FWCore v0r1)
+
+find_package(podio REQUIRED)
+find_package(ROOT COMPONENTS RIO Tree)
+
+# this declaration will not be needed in the future
+gaudi_depends_on_subdirs(GaudiAlg GaudiKernel)
+gaudi_install_scripts()
+gaudi_install_python_modules()
+
+gaudi_add_library(FWCore
+		  src/*.cpp
+                  INCLUDE_DIRS ${podio_INCLUDE_DIRS} ROOT
+                  LINK_LIBRARIES GaudiAlgLib GaudiKernel ${podio_LIBRARIES} ROOT
+                  PUBLIC_HEADERS FWCore)
+
+gaudi_add_module(FWCorePlugins
+                 src/components/*.cpp
+                 LINK_LIBRARIES GaudiAlgLib GaudiKernel FWCore ROOT)
+
diff --git a/FWCore/FWCore/DataHandle.h b/FWCore/FWCore/DataHandle.h
new file mode 100644
index 0000000000000000000000000000000000000000..642d082380a708cbe88a2becbf74fc98418a47a9
--- /dev/null
+++ b/FWCore/FWCore/DataHandle.h
@@ -0,0 +1,157 @@
+#ifndef FWCORE_DATAHANDLE_H
+#define FWCORE_DATAHANDLE_H
+
+#include "FWCore/DataWrapper.h"
+#include "FWCore/PodioDataSvc.h"
+
+#include "GaudiKernel/AlgTool.h"
+#include "GaudiKernel/Algorithm.h"
+#include <GaudiKernel/DataObjectHandle.h>
+#include <GaudiKernel/GaudiException.h>
+#include <GaudiKernel/Property.h>
+#include <GaudiKernel/ServiceLocatorHelper.h>
+
+#include "TTree.h"
+
+#include <type_traits>
+
+template <typename T>
+class DataHandle : public DataObjectHandle<DataWrapper<T>> {
+
+public:
+  friend class Algorithm;
+  friend class AlgTool;
+
+public:
+  DataHandle();
+
+  /// Initialises mother class
+  DataHandle(DataObjID& descriptor, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg);
+
+  DataHandle(const std::string& k, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg);
+  /**
+   * Retrieve object from transient data store
+   */
+  const T* get();
+
+  /**
+   * Register object in transient store
+   */
+  void put(T* object);
+
+  /**
+  * Create and register object in transient store
+  */
+  T* createAndPut();
+
+private:
+  ServiceHandle<IDataProviderSvc> m_eds;
+  bool m_isGoodType{false};
+  bool m_isCollection{false};
+  T* m_dataPtr;
+};
+
+//---------------------------------------------------------------------------
+template <typename T>
+DataHandle<T>::DataHandle(DataObjID& descriptor, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg)
+    : DataObjectHandle<DataWrapper<T>>(descriptor, a, fatherAlg), m_eds("EventDataSvc", "DataHandle") {
+      
+}
+//---------------------------------------------------------------------------
+template <typename T>
+DataHandle<T>::DataHandle(const std::string& descriptor, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg)
+    : DataObjectHandle<DataWrapper<T>>(descriptor, a, fatherAlg), m_eds("EventDataSvc", "DataHandle") {
+
+  if (a > 15) { // Gaudi::DataHandle::Mode is 'writer'
+  m_eds.retrieve();
+  PodioDataSvc* pds;
+  pds = dynamic_cast<PodioDataSvc*>( m_eds.get());
+  m_dataPtr = 0;
+  if (nullptr != pds) {
+    if (std::is_convertible<T*,podio::CollectionBase*>::value) {
+      // still handled in PodioOutput
+    } else {
+      TTree* tree = pds->eventDataTree();
+      tree->Branch(descriptor.c_str(),  &m_dataPtr);
+      }
+    }
+  }
+}
+
+/**
+ * Try to retrieve from the transient store. If the retrieval succeded and
+ * this is the first time we retrieve, perform a dynamic cast to the desired
+ * object. Then finally set the handle as Read.
+ * If this is not the first time we cast and the cast worked, just use the
+ * static cast: we do not need the checks of the dynamic cast for every access!
+ */
+template <typename T>
+const T* DataHandle<T>::get() {
+  DataObject* dataObjectp = nullptr;
+  auto sc = m_eds->retrieveObject(DataObjectHandle<DataWrapper<T>>::fullKey().key(), dataObjectp);
+
+  if (LIKELY(sc.isSuccess())) {
+    if (UNLIKELY(!m_isGoodType && !m_isCollection)) {
+      // only do this once (if both are false after this, we throw exception)
+      m_isGoodType = nullptr != dynamic_cast<DataWrapper<T>*>(dataObjectp);
+      if (!m_isGoodType) {
+        auto tmp = dynamic_cast<DataWrapper<podio::CollectionBase>*>(dataObjectp);
+        if (tmp != nullptr) {
+          m_isCollection = nullptr != dynamic_cast<T*>(tmp->collectionBase());
+        }
+      }
+    }
+    if (LIKELY(m_isGoodType)) {
+      DataObjectHandle<DataWrapper<T>>::setRead();
+      return static_cast<DataWrapper<T>*>(dataObjectp)->getData();
+    } else if (m_isCollection) {
+      // The reader does not know the specific type of the collection. So we need a reinterpret_cast if the handle was
+      // created by the reader.
+      DataWrapper<podio::CollectionBase>* tmp = static_cast<DataWrapper<podio::CollectionBase>*>(dataObjectp);
+      DataObjectHandle<DataWrapper<T>>::setRead();
+      return reinterpret_cast<const T*>(tmp->collectionBase());
+    } else {
+      std::string errorMsg("The type provided for " + DataObjectHandle<DataWrapper<T>>::toString() +
+                           " is different from the one of the object in the store.");
+      throw GaudiException(errorMsg, "wrong product type", StatusCode::FAILURE);
+    }
+  }
+  std::string msg("Could not retrieve product " + DataObjectHandle<DataWrapper<T>>::toString());
+  throw GaudiException(msg, "wrong product name", StatusCode::FAILURE);
+}
+
+//---------------------------------------------------------------------------
+template <typename T>
+void DataHandle<T>::put(T* objectp) {
+  DataWrapper<T>* dw = new DataWrapper<T>();
+  m_dataPtr = objectp;
+  dw->setData(objectp);
+  DataObjectHandle<DataWrapper<T>>::put(dw);
+
+}
+//---------------------------------------------------------------------------
+/**
+ * Create the collection, put it in the DataObjectHandle and return the
+ * pointer to the data. Call this function if you create a collection and
+ * want to save it.
+ */
+template <typename T>
+T* DataHandle<T>::createAndPut() {
+  T* objectp = new T();
+  this->put(objectp);
+  return objectp;
+}
+
+// temporary to allow property declaration
+namespace Gaudi {
+template <class T>
+class Property<::DataHandle<T>&> : public ::DataObjectHandleProperty {
+public:
+  Property(const std::string& name, ::DataHandle<T>& value) : ::DataObjectHandleProperty(name, value) {}
+
+  /// virtual Destructor
+  virtual ~Property() {}
+};
+}
+
+#endif
diff --git a/FWCore/FWCore/DataWrapper.h b/FWCore/FWCore/DataWrapper.h
new file mode 100644
index 0000000000000000000000000000000000000000..a6ec7a48d1628767e94b527a875495ad720426c3
--- /dev/null
+++ b/FWCore/FWCore/DataWrapper.h
@@ -0,0 +1,46 @@
+#ifndef FWCORE_DATAWRAPPER_H
+#define FWCORE_DATAWRAPPER_H
+
+#include <type_traits>
+
+// Include files
+#include "GaudiKernel/DataObject.h"
+#include "podio/CollectionBase.h"
+
+class GAUDI_API DataWrapperBase : public DataObject {
+public:
+  // ugly hack to circumvent the usage of boost::any yet
+  // DataSvc would need a templated register method
+  virtual podio::CollectionBase* collectionBase() = 0;
+  virtual ~DataWrapperBase(){};
+};
+
+template <class T>
+class GAUDI_API DataWrapper : public DataWrapperBase {
+public:
+  DataWrapper() : DataWrapperBase(), m_data(nullptr){};
+  virtual ~DataWrapper();
+
+  const T* getData() { return m_data; }
+  void setData(T* data) { m_data = data; }
+  /// try to cast to collectionBase; may return nullptr;
+  virtual podio::CollectionBase* collectionBase();
+
+private:
+  T* m_data;
+};
+
+template <class T>
+DataWrapper<T>::~DataWrapper<T>() {
+  if (m_data != nullptr) delete m_data;
+}
+
+template <class T>
+podio::CollectionBase* DataWrapper<T>::collectionBase() {
+  if (std::is_base_of<podio::CollectionBase, T>::value) {
+    return reinterpret_cast<podio::CollectionBase*>(m_data);
+  }
+  return nullptr;
+}
+
+#endif
diff --git a/FWCore/FWCore/KeepDropSwitch.h b/FWCore/FWCore/KeepDropSwitch.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4bd4c0bc2d7525056b18d6f34914a7faa7a488e
--- /dev/null
+++ b/FWCore/FWCore/KeepDropSwitch.h
@@ -0,0 +1,27 @@
+#ifndef EXAMPLES_KEEPDROPSWITCH_H
+#define EXAMPLES_KEEPDROPSWITCH_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+std::vector<std::string> split(const std::string& s, char delim);
+
+int wildcmp(const char* wild, const char* string);
+
+class KeepDropSwitch {
+public:
+  enum Cmd { KEEP, DROP, UNKNOWN };
+  typedef std::vector<std::string> CommandLines;
+  KeepDropSwitch() {}
+  explicit KeepDropSwitch(const CommandLines& cmds) { m_commandlines = cmds; }
+  bool isOn(const std::string& astring) const;
+
+private:
+  bool getFlag(const std::string& astring) const;
+  Cmd extractCommand(const std::string cmdLine) const;
+  CommandLines m_commandlines;
+  mutable std::map<std::string, bool> m_cache;
+};
+
+#endif
diff --git a/FWCore/FWCore/PodioDataSvc.h b/FWCore/FWCore/PodioDataSvc.h
new file mode 100644
index 0000000000000000000000000000000000000000..09194a30c404263f1b06f4457eafb02282a1e399
--- /dev/null
+++ b/FWCore/FWCore/PodioDataSvc.h
@@ -0,0 +1,83 @@
+#ifndef FWCORE_PODIODATASVC_H
+#define FWCORE_PODIODATASVC_H
+
+#include "GaudiKernel/DataSvc.h"
+#include "GaudiKernel/IConversionSvc.h"
+// PODIO
+#include "podio/CollectionBase.h"
+#include "podio/CollectionIDTable.h"
+#include "podio/EventStore.h"
+#include "podio/ROOTReader.h"
+
+#include <utility>
+// Forward declarations
+
+/** @class PodioEvtSvc EvtDataSvc.h
+ *
+ *   An EvtDataSvc for PODIO classes
+ *
+ *  @author B. Hegner
+ */
+class PodioDataSvc : public DataSvc {
+public:
+  typedef std::vector<std::pair<std::string, podio::CollectionBase*>> CollRegistry;
+
+  virtual StatusCode initialize();
+  virtual StatusCode reinitialize();
+  virtual StatusCode finalize();
+  virtual StatusCode clearStore();
+
+  /// Standard Constructor
+  PodioDataSvc(const std::string& name, ISvcLocator* svc);
+
+  /// Standard Destructor
+  virtual ~PodioDataSvc();
+
+  // Use DataSvc functionality except where we override
+  using DataSvc::registerObject;
+  /// Overriding standard behaviour of evt service
+  /// Register object with the data store.
+  virtual StatusCode registerObject(const std::string& fullPath, DataObject* pObject) final;
+
+  StatusCode readCollection(const std::string& collectionName, int collectionID);
+
+  virtual const CollRegistry& getCollections() const { return m_collections; }
+  virtual const CollRegistry& getReadCollections() const { return m_readCollections; }
+  virtual podio::CollectionIDTable* getCollectionIDs() { return m_collectionIDs; }
+
+  /// Set the collection IDs (if reading a file)
+  void setCollectionIDs(podio::CollectionIDTable* collectionIds);
+  /// Resets caches of reader and event store, increases event counter
+  void endOfRead();
+
+
+  TTree* eventDataTree() {return m_eventDataTree;}
+
+
+private:
+
+  // eventDataTree
+  TTree* m_eventDataTree;
+  /// PODIO reader for ROOT files
+  podio::ROOTReader m_reader;
+  /// PODIO EventStore, used to initialise collections
+  podio::EventStore m_provider;
+  /// Counter of the event number
+  int m_eventNum{0};
+  /// Number of events in the file / to process
+  int m_eventMax{-1};
+
+
+  SmartIF<IConversionSvc> m_cnvSvc;
+
+  // special members for podio handling
+  std::vector<std::pair<std::string, podio::CollectionBase*>> m_collections;
+  std::vector<std::pair<std::string, podio::CollectionBase*>> m_readCollections;
+  podio::CollectionIDTable* m_collectionIDs;
+
+protected:
+  /// ROOT file name the input is read from. Set by option filename
+  std::vector<std::string> m_filenames;
+  std::string m_filename;
+};
+#endif  // CORE_PODIODATASVC_H
diff --git a/FWCore/python/FWCore/__init__.py b/FWCore/python/FWCore/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/FWCore/python/FWCore/dump_joboptions.py b/FWCore/python/FWCore/dump_joboptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..eaf71c9aa02ca604f7c25c2777ed3f9e7cd00246
--- /dev/null
+++ b/FWCore/python/FWCore/dump_joboptions.py
@@ -0,0 +1,30 @@
+from __future__ import print_function
+import argparse
+import ROOT
+
+parser = argparse.ArgumentParser(description='Job Options Dumper')
+parser.add_argument( dest='fname', type=str, help="name of file to read")
+
+
+def dump_joboptions(filename):
+  """ Simple and straightforward dump of the output of Gaudi's JobOptionsSvc
+  to stdout.
+  
+  Parameters
+  ----------
+
+  filename: str
+      The name of the CEPCSW output file containing joboptions to print
+  """
+  f = ROOT.TFile(filename)
+  t = f.metadata
+  for event in t:
+      s =  event.gaudiConfigOptions
+      for e in s:
+          print(e)
+
+if __name__ == "__main__":
+    args = parser.parse_args()
+    dump_joboptions(args.fname)
+
+  
diff --git a/FWCore/python/FWCore/joboptions.py b/FWCore/python/FWCore/joboptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..47f8173d837c452551803be2690acb6d5e513ff2
--- /dev/null
+++ b/FWCore/python/FWCore/joboptions.py
@@ -0,0 +1,15 @@
+import argparse
+
+def parse_standard_job_options(scriptname=""):
+    """
+    Returns the parsed arguments, adding a parser with commonly needed opts:
+    - args.nevents      -- number of events (int), specify with --nevents
+    - args.inputfile    -- the input file (string), specify with --inputfile
+    - args.outputfile   -- the output file (string), specify with --outputfile
+    """
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--inputfile', type=str, default='', help='specify an input file')
+    parser.add_argument('--outputfile', type=str, default='', help='specify an output file')
+    parser.add_argument('--nevents', type=int, default=None, help='specify number of events to process')
+    args, _ = parser.parse_known_args()
+    return args
diff --git a/FWCore/src/KeepDropSwitch.cpp b/FWCore/src/KeepDropSwitch.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6617c03208e39556e2594ebe48d7bf0c29796b8b
--- /dev/null
+++ b/FWCore/src/KeepDropSwitch.cpp
@@ -0,0 +1,100 @@
+#include "FWCore/KeepDropSwitch.h"
+
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+
+int wildcmp(const char* wild, const char* string) {
+  // Written by Jack Handy - <A href="mailto:jakkhandy@hotmail.com">jakkhandy@hotmail.com</A>
+  const char *cp = nullptr, *mp = nullptr;
+  while ((*string) && (*wild != '*')) {
+    if ((*wild != *string) && (*wild != '?')) {
+      return 0;
+    }
+    wild++;
+    string++;
+  }
+  while (*string) {
+    if (*wild == '*') {
+      if (!*++wild) {
+        return 1;
+      }
+      mp = wild;
+      cp = string + 1;
+    } else if ((*wild == *string) || (*wild == '?')) {
+      wild++;
+      string++;
+    } else {
+      wild = mp;
+      string = cp++;
+    }
+  }
+  while (*wild == '*') {
+    wild++;
+  }
+  return !*wild;
+}
+
+std::vector<std::string> split(const std::string& s, char delim) {
+  std::vector<std::string> elems;
+  std::stringstream ss(s);
+  std::string item;
+  while (std::getline(ss, item, delim)) {
+    if (item != "") elems.push_back(item);
+  }
+  return elems;
+}
+
+bool KeepDropSwitch::isOn(const std::string& astring) const {
+  typedef std::map<std::string, bool>::const_iterator MIter;
+  MIter im = m_cache.find(astring);
+  if (im != m_cache.end())
+    return im->second;
+  else {
+    bool val = getFlag(astring);
+    m_cache.insert(std::pair<std::string, bool>(astring, val));
+    return val;
+  }
+}
+
+bool KeepDropSwitch::getFlag(const std::string& astring) const {
+  bool flag = true;
+  for (const auto& cmdline : m_commandlines) {
+    std::vector<std::string> words = split(cmdline, ' ');
+    if (words.size() != 2) {
+      std::ostringstream msg;
+      msg << "malformed command string : " << cmdline;
+      throw std::invalid_argument(msg.str());
+    }
+    std::string cmd = words[0];
+    std::string pattern = words[1];
+    Cmd theCmd = UNKNOWN;
+    if (cmd == "keep")
+      theCmd = KEEP;
+    else if (cmd == "drop")
+      theCmd = DROP;
+    else {
+      std::ostringstream msg;
+      msg << "malformed command in line: " << std::endl;
+      msg << cmdline << std::endl;
+      msg << "should be keep or drop, lower case" << std::endl;
+      throw std::invalid_argument(msg.str());
+    }
+    bool match = wildcmp(pattern.c_str(), astring.c_str());
+    if (not match)
+      continue;
+    else if (theCmd == KEEP)
+      flag = true;
+    else
+      flag = false;
+  }
+  return flag;
+}
+
+KeepDropSwitch::Cmd KeepDropSwitch::extractCommand(const std::string cmdline) const {
+  std::vector<std::string> words = split(cmdline, ' ');
+  for (auto& word : words)
+    std::cout << "'" << word << "' ";
+  std::cout << std::endl;
+  return UNKNOWN;
+}
diff --git a/FWCore/src/PodioDataSvc.cpp b/FWCore/src/PodioDataSvc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..77da2e3644cca8be8d3d208ba78c665baef828fc
--- /dev/null
+++ b/FWCore/src/PodioDataSvc.cpp
@@ -0,0 +1,120 @@
+#include "FWCore/PodioDataSvc.h"
+#include "GaudiKernel/IConversionSvc.h"
+#include "GaudiKernel/IEventProcessor.h"
+#include "GaudiKernel/ISvcLocator.h"
+
+#include "FWCore/DataWrapper.h"
+
+#include "TTree.h"
+
+/// Service initialisation
+StatusCode PodioDataSvc::initialize() {
+  // Nothing to do: just call base class initialisation
+  StatusCode status = DataSvc::initialize();
+  ISvcLocator* svc_loc = serviceLocator();
+
+
+  // Attach data loader facility
+  m_cnvSvc = svc_loc->service("EventPersistencySvc");
+  status = setDataLoader(m_cnvSvc);
+
+  if (m_filename != "") {
+    m_filenames.push_back(m_filename);
+  }
+
+  if (m_filenames.size() > 0) {
+    if (m_filenames[0] != "") {
+      m_reader.openFiles(m_filenames);
+      m_eventMax = m_reader.getEntries();
+      auto idTable = m_reader.getCollectionIDTable();
+
+      setCollectionIDs(idTable);
+      m_provider.setReader(&m_reader);
+    }
+  }
+  return status;
+}
+/// Service reinitialisation
+StatusCode PodioDataSvc::reinitialize() {
+  // Do nothing for this service
+  return StatusCode::SUCCESS;
+}
+/// Service finalization
+StatusCode PodioDataSvc::finalize() {
+  m_cnvSvc = 0;  // release
+  DataSvc::finalize().ignore();
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PodioDataSvc::clearStore() {
+  for (auto& collNamePair : m_collections) {
+    if (collNamePair.second != nullptr) {
+      collNamePair.second->clear();
+    }
+  }
+  for (auto& collNamePair : m_readCollections) {
+    if (collNamePair.second != nullptr) {
+      collNamePair.second->clear();
+    }
+  }
+  DataSvc::clearStore().ignore();
+  m_collections.clear();
+  m_readCollections.clear();
+  return StatusCode::SUCCESS;
+}
+
+void PodioDataSvc::endOfRead() {
+  if (m_eventMax != -1) {
+    m_provider.clearCaches();
+    m_reader.endOfEvent();
+    if (m_eventNum++ > m_eventMax) {
+      info() << "Reached end of file with event " << m_eventMax << endmsg;
+      IEventProcessor* eventProcessor;
+      service("ApplicationMgr", eventProcessor);
+      eventProcessor->stopRun();
+    }
+  }
+}
+
+void PodioDataSvc::setCollectionIDs(podio::CollectionIDTable* collectionIds) {
+  if (m_collectionIDs != nullptr) {
+    delete m_collectionIDs;
+  }
+  m_collectionIDs = collectionIds;
+}
+
+/// Standard Constructor
+PodioDataSvc::PodioDataSvc(const std::string& name, ISvcLocator* svc)
+    : DataSvc(name, svc), m_collectionIDs(new podio::CollectionIDTable()) {
+
+  m_eventDataTree = new TTree("events", "Events tree");
+    }
+
+/// Standard Destructor
+PodioDataSvc::~PodioDataSvc() {}
+
+StatusCode PodioDataSvc::readCollection(const std::string& collName, int collectionID) {
+  podio::CollectionBase* collection(nullptr);
+  m_provider.get(collectionID, collection);
+  auto wrapper = new DataWrapper<podio::CollectionBase>;
+  int id = m_collectionIDs->add(collName);
+  collection->setID(id);
+  wrapper->setData(collection);
+  m_readCollections.emplace_back(std::make_pair(collName, collection));
+  return DataSvc::registerObject(collName, wrapper);
+}
+
+StatusCode PodioDataSvc::registerObject(const std::string& fullPath, DataObject* pObject) {
+  DataWrapperBase* wrapper = dynamic_cast<DataWrapperBase*>(pObject);
+  if (wrapper != nullptr) {
+    podio::CollectionBase* coll = wrapper->collectionBase();
+    if (coll != nullptr) {
+      size_t pos = fullPath.find_last_of("/");
+      std::string shortPath(fullPath.substr(pos + 1, fullPath.length()));
+      int id = m_collectionIDs->add(shortPath);
+      coll->setID(id);
+      m_collections.emplace_back(std::make_pair(shortPath, coll));
+    }
+  }
+  return DataSvc::registerObject(fullPath, pObject);
+}
diff --git a/FWCore/src/components/CEPCDataSvc.cpp b/FWCore/src/components/CEPCDataSvc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa870478432eaf570360b8bceb73406cf2e66692
--- /dev/null
+++ b/FWCore/src/components/CEPCDataSvc.cpp
@@ -0,0 +1,20 @@
+#include "CEPCDataSvc.h"
+
+#include "GaudiKernel/IConversionSvc.h"
+#include "GaudiKernel/ISvcLocator.h"
+#include "GaudiKernel/SvcFactory.h"
+
+// Instantiation of a static factory class used by clients to create
+// instances of this service
+DECLARE_SERVICE_FACTORY(CEPCDataSvc)
+
+/// Standard Constructor
+CEPCDataSvc::CEPCDataSvc(const std::string& name, ISvcLocator* svc)
+    : PodioDataSvc(name, svc)
+{
+  declareProperty("inputs", m_filenames = {}, "Names of the files to read");
+  declareProperty("input", m_filename = "", "Name of the file to read");
+}
+
+/// Standard Destructor
+CEPCDataSvc::~CEPCDataSvc() {}
diff --git a/FWCore/src/components/CEPCDataSvc.h b/FWCore/src/components/CEPCDataSvc.h
new file mode 100644
index 0000000000000000000000000000000000000000..4953799c435618413978cb309abbda04ae78e83d
--- /dev/null
+++ b/FWCore/src/components/CEPCDataSvc.h
@@ -0,0 +1,18 @@
+#ifndef FWCORE_CEPCDATASVC_H
+#define FWCORE_CEPCDATASVC_H
+
+#include "FWCore/PodioDataSvc.h"
+
+class CEPCDataSvc : public PodioDataSvc
+{
+  friend class SvcFactory<CEPCDataSvc>;
+
+public:
+  /// Standard Constructor
+  CEPCDataSvc(const std::string& name, ISvcLocator* svc);
+
+  /// Standard Destructor
+  virtual ~CEPCDataSvc();
+};
+
+#endif  // FWCORE_CEPCDATASVC_H
diff --git a/FWCore/src/components/PodioInput.cpp b/FWCore/src/components/PodioInput.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f031744cdbe8c27728eeed3356cc8bc030fcf9d5
--- /dev/null
+++ b/FWCore/src/components/PodioInput.cpp
@@ -0,0 +1,50 @@
+#include "PodioInput.h"
+
+#include "TFile.h"
+#include "TROOT.h"
+
+#include "FWCore/DataWrapper.h"
+#include "FWCore/PodioDataSvc.h"
+
+DECLARE_COMPONENT(PodioInput)
+
+PodioInput::PodioInput(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {}
+
+StatusCode PodioInput::initialize() {
+  if (GaudiAlgorithm::initialize().isFailure()) return StatusCode::FAILURE;
+
+  // check whether we have the PodioEvtSvc active
+  m_podioDataSvc = dynamic_cast<PodioDataSvc*>(evtSvc().get());
+  if (nullptr == m_podioDataSvc) return StatusCode::FAILURE;
+
+  auto idTable = m_podioDataSvc->getCollectionIDs();
+  for (auto& name : m_collectionNames) {
+    debug() << "Finding collection " << name << " in collection registry." << endmsg;
+    if (!idTable->present(name)) {
+      error() << "Requested product " << name << " not found." << endmsg;
+      return StatusCode::FAILURE;
+    }
+    m_collectionIDs.push_back(idTable->collectionID(name));
+  }
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PodioInput::execute() {
+  size_t cntr = 0;
+  // Re-create the collections from ROOT file
+  for (auto& id : m_collectionIDs) {
+    const std::string& collName = m_collectionNames.value().at(cntr++);
+    debug() << "Registering collection to read " << collName << " with id " << id << endmsg;
+    if (m_podioDataSvc->readCollection(collName, id).isFailure()) {
+      return StatusCode::FAILURE;
+    }
+  }
+  // Tell data service that we are done with requested collections
+  m_podioDataSvc->endOfRead();
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PodioInput::finalize() {
+  if (GaudiAlgorithm::finalize().isFailure()) return StatusCode::FAILURE;
+  return StatusCode::SUCCESS;
+}
diff --git a/FWCore/src/components/PodioInput.h b/FWCore/src/components/PodioInput.h
new file mode 100644
index 0000000000000000000000000000000000000000..0319d78f63a715edd5bd42d5e08823328ed759f2
--- /dev/null
+++ b/FWCore/src/components/PodioInput.h
@@ -0,0 +1,43 @@
+#ifndef FWCORE_PODIOINPUT_H
+#define FWCORE_PODIOINPUT_H
+// Gaaudi
+#include "GaudiAlg/GaudiAlgorithm.h"
+
+// STL
+#include <string>
+#include <vector>
+
+// forward declarations
+// from FWCore:
+class PodioDataSvc;
+
+/** @class PodioInput FWCore/components/PodioInput.h PodioInput.h
+ *
+ *  Class that allows to read ROOT files written with PodioOutput
+ *
+ *  @author J. Lingemann
+ */
+
+class PodioInput : public GaudiAlgorithm {
+  friend class AlgFactory<PodioInput>;
+
+public:
+  /// Constructor.
+  PodioInput(const std::string& name, ISvcLocator* svcLoc);
+  /// Initialization of PodioInput. Acquires the data service, opens root file and creates trees.
+  virtual StatusCode initialize();
+  /// Execute. Re-creates collections that are specified to be read and sets references.
+  virtual StatusCode execute();
+  /// Finalize. Closes ROOT file.
+  virtual StatusCode finalize();
+
+private:
+  /// Name of collections to read. Set by option collections (this is temporary)
+  Gaudi::Property<std::vector<std::string>> m_collectionNames{this, "collections", {}, "Places of collections to read"};
+  /// Collection IDs (retrieved with CollectionIDTable from ROOT file, using collection names)
+  std::vector<int> m_collectionIDs;
+  /// Data service: needed to register objects and get collection IDs. Just an observing pointer.
+  PodioDataSvc* m_podioDataSvc;
+};
+
+#endif
diff --git a/FWCore/src/components/PodioOutput.cpp b/FWCore/src/components/PodioOutput.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c531ad6e95ff410ed408ba3482fcaf6aef38047
--- /dev/null
+++ b/FWCore/src/components/PodioOutput.cpp
@@ -0,0 +1,133 @@
+#include "PodioOutput.h"
+#include "GaudiKernel/IJobOptionsSvc.h"
+#include "FWCore/PodioDataSvc.h"
+#include "TFile.h"
+
+DECLARE_COMPONENT(PodioOutput)
+
+PodioOutput::PodioOutput(const std::string& name, ISvcLocator* svcLoc)
+    : GaudiAlgorithm(name, svcLoc), m_firstEvent(true) {}
+
+StatusCode PodioOutput::initialize() {
+  if (GaudiAlgorithm::initialize().isFailure()) return StatusCode::FAILURE;
+
+  // check whether we have the PodioEvtSvc active
+  m_podioDataSvc = dynamic_cast<PodioDataSvc*>(evtSvc().get());
+  if (0 == m_podioDataSvc) return StatusCode::FAILURE;
+
+  m_file = std::unique_ptr<TFile>(new TFile(m_filename.value().c_str(), "RECREATE", "data file"));
+  // Both trees are written to the ROOT file and owned by it
+  // PodioDataSvc has ownership of EventDataTree
+  m_datatree = m_podioDataSvc->eventDataTree();
+  m_metadatatree = new TTree("metadata", "Metadata tree");
+  m_switch = KeepDropSwitch(m_outputCommands);
+  return StatusCode::SUCCESS;
+}
+
+void PodioOutput::resetBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections,
+                                bool prepare) {
+  for (auto& collNamePair : collections) {
+    auto collName = collNamePair.first;
+    if (m_switch.isOn(collName)) {
+      // Reconnect branches and collections
+      m_datatree->SetBranchAddress(collName.c_str(), collNamePair.second->getBufferAddress());
+      auto colls = collNamePair.second->referenceCollections();
+      if (colls != nullptr) {
+        int j = 0;
+        for (auto& c : (*colls)) {
+          m_datatree->SetBranchAddress((collName + "#" + std::to_string(j)).c_str(), &c);
+          ++j;
+        }
+      }
+    }
+    if (prepare) {
+      collNamePair.second->prepareForWrite();
+    }
+  }
+}
+
+void PodioOutput::createBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections,
+                                 bool prepare) {
+  for (auto& collNamePair : collections) {
+    auto collName = collNamePair.first;
+    // TODO: we need the class name in a better way
+    std::string className(typeid(*(collNamePair.second)).name());
+    size_t pos = className.find_first_not_of("0123456789");
+    className.erase(0, pos);
+    // demangling the namespace: due to namespace additional characters were introduced:
+    // e.g. N3fcc18TrackHit
+    // remove any number+char before the namespace:
+    pos = className.find_first_of("0123456789");
+    size_t pos1 = className.find_first_not_of("0123456789", pos);
+    className.erase(0, pos1);
+    // replace any numbers between namespace and class with "::"
+    pos = className.find_first_of("0123456789");
+    pos1 = className.find_first_not_of("0123456789", pos);
+    className.replace(pos, pos1 - pos, "::");
+
+    pos = className.find("Collection");
+    className.erase(pos, pos + 10);
+    std::string collClassName = "vector<" + className + "Data>";
+    int isOn = 0;
+    if (m_switch.isOn(collName)) {
+      isOn = 1;
+      m_datatree->Branch(collName.c_str(), collClassName.c_str(), collNamePair.second->getBufferAddress());
+      // Create branches for collections holding relations
+      auto colls = collNamePair.second->referenceCollections();
+      if (colls != nullptr) {
+        int j = 0;
+        for (auto& c : (*colls)) {
+          m_datatree->Branch((collName + "#" + std::to_string(j)).c_str(), c);
+          ++j;
+        }
+      }
+    }
+    debug() << isOn << " Registering collection " << collClassName << " " << collName.c_str() << " containing type "
+            << className << endmsg;
+    if (prepare) {
+      collNamePair.second->prepareForWrite();
+    }
+  }
+}
+
+StatusCode PodioOutput::execute() {
+  // for now assume identical content for every event
+  // register for writing
+  if (m_firstEvent) {
+    createBranches(m_podioDataSvc->getCollections(), true);
+    createBranches(m_podioDataSvc->getReadCollections(), false);
+  } else {
+    resetBranches(m_podioDataSvc->getCollections(), true);
+    resetBranches(m_podioDataSvc->getReadCollections(), false);
+  }
+  m_firstEvent = false;
+  debug() << "Filling DataTree .." << endmsg;
+  m_datatree->Fill();
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PodioOutput::finalize() {
+  if (GaudiAlgorithm::finalize().isFailure()) return StatusCode::FAILURE;
+  // retrieve the configuration of the job
+  // and write it to file as vector of strings
+  std::vector<std::string> config_data;
+  auto jobOptionsSvc = service<IJobOptionsSvc>("JobOptionsSvc");
+  auto configured_components = jobOptionsSvc->getClients();
+  for (const auto& name : configured_components) {
+      auto properties = jobOptionsSvc->getProperties(name);
+      std::stringstream config_stream;
+      for (const auto& property : *properties) {
+          config_stream << name << " : " << property->name() << " = " << property->toString() << std::endl;
+        }
+        config_data.push_back(config_stream.str());
+      }
+      m_metadatatree->Branch("gaudiConfigOptions", &config_data);
+      
+  m_metadatatree->Branch("CollectionIDs", m_podioDataSvc->getCollectionIDs());
+  m_metadatatree->Fill();
+  m_datatree->Write();
+  m_file->Write();
+  m_file->Close();
+  info() << "Data written to: " << m_filename << endmsg;
+  return StatusCode::SUCCESS;
+}
diff --git a/FWCore/src/components/PodioOutput.h b/FWCore/src/components/PodioOutput.h
new file mode 100644
index 0000000000000000000000000000000000000000..f1c93beabd1436202330011ffe741bcdfa5d2dab
--- /dev/null
+++ b/FWCore/src/components/PodioOutput.h
@@ -0,0 +1,55 @@
+#ifndef FWCORE_PODIOOUTPUT_H
+#define FWCORE_PODIOOUTPUT_H
+
+#include "FWCore/KeepDropSwitch.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include "podio/CollectionBase.h"
+
+#include "TTree.h"
+
+#include <vector>
+
+// forward declarations
+class TFile;
+class PodioDataSvc;
+
+class PodioOutput : public GaudiAlgorithm {
+  friend class AlgFactory<PodioOutput>;
+
+public:
+  /// Constructor.
+  PodioOutput(const std::string& name, ISvcLocator* svcLoc);
+
+  /// Initialization of PodioOutput. Acquires the data service, creates trees and root file.
+  virtual StatusCode initialize();
+  /// Execute. For the first event creates branches for all collections known to PodioDataSvc and prepares them for
+  /// writing. For the following events it reconnects the branches with collections and prepares them for write.
+  virtual StatusCode execute();
+  /// Finalize. Writes the meta data tree; writes file and cleans up all ROOT-pointers.
+  virtual StatusCode finalize();
+
+private:
+  void resetBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections, bool prepare);
+  void createBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections, bool prepare);
+  /// First event or not
+  bool m_firstEvent;
+  /// Root file name the output is written to
+  Gaudi::Property<std::string> m_filename{this, "filename", "output.root", "Name of the file to create"};
+  /// Commands which output is to be kept
+  Gaudi::Property<std::vector<std::string>> m_outputCommands{
+      this, "outputCommands", {"keep *"}, "A set of commands to declare which collections to keep or drop."};
+  /// Switch for keeping or dropping outputs
+  KeepDropSwitch m_switch;
+  /// Needed for collection ID table
+  PodioDataSvc* m_podioDataSvc;
+  /// The actual ROOT file
+  std::unique_ptr<TFile> m_file;
+  /// The tree to be filled with collections
+  TTree* m_datatree;
+  /// The tree to be filled with meta data
+  TTree* m_metadatatree;
+  /// The stored collections
+  std::vector<podio::CollectionBase*> m_storedCollections;
+};
+
+#endif
diff --git a/README.md b/README.md
index 8f3ff57c698387f1df5918e0e8fb825371e1abc5..74feea3c0278300955daadc8228480c03f8d9350 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,9 @@
 # CEPCSW
 
-CEPC offline software prototype based on Gaudi
+CEPC offline software prototype based on Gaudi.
+
+The core part of this project (CEPCSW/FWCore) is taken from FCCSW/FWCore.
+Please refer to https://github.com/HEP-FCC/FCCSW
 
 ## Quick start