diff --git a/objectstore/DriveState.cpp b/objectstore/DriveState.cpp
index 5a7c16b6bfe5000eaa8318c6e7291ef6b76ca6fc..b9795e39125419b3be455aa321a562902335c9c4 100644
--- a/objectstore/DriveState.cpp
+++ b/objectstore/DriveState.cpp
@@ -281,6 +281,8 @@ void DriveState::setConfig(const cta::tape::daemon::TapedConfiguration& tapedCon
   fillConfig(config->mountCriteria);
   fillConfig(config->nbDiskThreads);
   fillConfig(config->useRAO);
+  fillConfig(config->raoLtoAlgorithm);
+  fillConfig(config->raoLtoOptions);
   fillConfig(config->wdScheduleMaxSecs);
   fillConfig(config->wdMountMaxSecs);
   fillConfig(config->wdNoBlockMoveMaxSecs);
diff --git a/tapeserver/castor/tape/tapeserver/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/CMakeLists.txt
index 55f323c01c72375ddd6c903e6781592c296e1543..9a73c25155cff2d7a70544224abdb7dc8bf5349a 100644
--- a/tapeserver/castor/tape/tapeserver/CMakeLists.txt
+++ b/tapeserver/castor/tape/tapeserver/CMakeLists.txt
@@ -41,6 +41,7 @@ add_subdirectory(drive)
 add_subdirectory(system)
 add_subdirectory(file)
 add_subdirectory(daemon)
+add_subdirectory(RAO)
 
 # .. and of course, the tests (last to use the variable definition)
 add_subdirectory(test)
diff --git a/tapeserver/castor/tape/tapeserver/RAO/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/RAO/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..152ce09ee0fe42975106778b77ca1ebde226679c
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required (VERSION 2.6)
+
+include_directories(${PROJECT_SOURCE_DIR}/tapeserver)
+include_directories(${PROJECT_SOURCE_DIR}/tapeserver/h)
+
+set(CTARAO_LIBRARY_SRCS
+  RAOConfig.cpp
+  RAOManager.cpp
+  RAOAlgorithm.cpp
+  EnterpriseRAOAlgorithm.cpp
+  RAOAlgorithmFactory.cpp
+  EnterpriseRAOAlgorithmFactory.cpp
+  LinearRAOAlgorithm.cpp
+  RandomRAOAlgorithm.cpp
+  NoParamRAOAlgorithmFactory.cpp
+  RAOAlgorithmFactoryFactory.cpp
+)
+
+add_library (ctarao SHARED
+  ${CTARAO_LIBRARY_SRCS})
+
+install(TARGETS ctarao DESTINATION usr/${CMAKE_INSTALL_LIBDIR})
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithm.cpp b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4562012ce0eba0c845b35fb7c4ba3cccbfbef84d
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithm.cpp
@@ -0,0 +1,74 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <list>
+
+#include "EnterpriseRAOAlgorithm.hpp"
+#include "castor/tape/tapeserver/SCSI/Structures.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+EnterpriseRAOAlgorithm::EnterpriseRAOAlgorithm(castor::tape::tapeserver::drive::DriveInterface * drive, const uint64_t maxFilesSupported):m_drive(drive), m_maxFilesSupported(maxFilesSupported) {
+}
+
+EnterpriseRAOAlgorithm::~EnterpriseRAOAlgorithm() {
+}
+
+std::vector<uint64_t> EnterpriseRAOAlgorithm::performRAO(const std::vector<std::unique_ptr<cta::RetrieveJob> >& jobs) {
+  std::vector<uint64_t> raoOrder;
+  uint64_t njobs = jobs.size();
+  uint32_t block_size = 262144;
+  std::list<castor::tape::SCSI::Structures::RAO::blockLims> files;
+  for (uint32_t i = 0; i < njobs; i++) {
+    cta::RetrieveJob *job = jobs.at(i).get();
+    castor::tape::SCSI::Structures::RAO::blockLims lims;
+    strncpy((char*)lims.fseq, std::to_string(i).c_str(), sizeof(i));
+    lims.begin = job->selectedTapeFile().blockId;
+    lims.end = job->selectedTapeFile().blockId + 8 +
+               /* ceiling the number of blocks */
+               ((job->archiveFile.fileSize + block_size - 1) / block_size);
+
+    files.push_back(lims);
+    if ((files.size() == m_maxFilesSupported) ||
+            ((i == njobs - 1) && (files.size() > 1))) {
+      /* We do a RAO query if:
+       *  1. the maximum number of files supported by the drive
+       *     for RAO query has been reached
+       *  2. the end of the jobs list has been reached and there are at least
+       *     2 unordered files
+       */
+      m_drive->queryRAO(files, m_maxFilesSupported);
+
+      /* Add the RAO sorted files to the new list*/
+      for (auto fit = files.begin(); fit != files.end(); fit++) {
+        uint64_t id = atoi((char*)fit->fseq);
+        raoOrder.push_back(id);
+      }
+      files.clear();
+    }
+  }
+  for (auto fit = files.begin(); fit != files.end(); fit++) {
+    uint64_t id = atoi((char*)fit->fseq);
+    raoOrder.push_back(id);
+  }
+  files.clear();
+  return raoOrder;
+}
+
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithm.hpp b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithm.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..34c3a0c5bdc9c1d043f77ffa8a96841d82d0acf1
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithm.hpp
@@ -0,0 +1,60 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "RAOAlgorithm.hpp"
+#include "castor/tape/tapeserver/drive/DriveInterface.hpp"
+#include "EnterpriseRAOAlgorithmFactory.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+class EnterpriseRAOAlgorithmFactory;
+  
+/**
+ * This class represents an EnterpriseRAOAlgorithm. 
+ */
+class EnterpriseRAOAlgorithm : public RAOAlgorithm{
+public:
+  friend EnterpriseRAOAlgorithmFactory;
+  
+  virtual ~EnterpriseRAOAlgorithm();
+
+  /**
+   * Asks the Enteprise drive to perform a RAO query in order to get the RAO of the 
+   * files represented by the jobs passed in parameter
+   * @param jobs the jobs representing the files we want to perform the RAO on
+   * @return the vector of the indexes of the jobs passed in parameters rearranged by the RAO query
+   */
+  std::vector<uint64_t> performRAO(const std::vector<std::unique_ptr<cta::RetrieveJob>> & jobs) override;
+  
+private:
+  /**
+   * Constructs an EnterpriseRAOAlgorithm
+   * @param drive the drive in order to call its RAO via its DriveInterface
+   * @param maxFilesSupported the maximum number of files supported by the drive to perform the RAO
+   */
+  EnterpriseRAOAlgorithm(castor::tape::tapeserver::drive::DriveInterface * drive, const uint64_t maxFilesSupported);
+  
+  //Interface to the drive
+  castor::tape::tapeserver::drive::DriveInterface * m_drive;
+  //Maximum number of files supported by the drive to perform the RAO
+  uint64_t m_maxFilesSupported;
+};
+
+}}}}
diff --git a/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithmFactory.cpp b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithmFactory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..36359f5c1151427b4ab5d31ce113c7625a3a4eb0
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithmFactory.cpp
@@ -0,0 +1,36 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "EnterpriseRAOAlgorithmFactory.hpp"
+#include "EnterpriseRAOAlgorithm.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+  
+EnterpriseRAOAlgorithmFactory::EnterpriseRAOAlgorithmFactory(castor::tape::tapeserver::drive::DriveInterface * drive, const uint64_t maxFilesSupported):
+m_drive(drive), m_maxFilesSupported(maxFilesSupported) {
+}
+
+EnterpriseRAOAlgorithmFactory::~EnterpriseRAOAlgorithmFactory() {
+}
+
+std::unique_ptr<RAOAlgorithm> EnterpriseRAOAlgorithmFactory::createRAOAlgorithm() {
+  return std::unique_ptr<RAOAlgorithm>(new EnterpriseRAOAlgorithm(m_drive,m_maxFilesSupported));
+}
+
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithmFactory.hpp b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithmFactory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..956a369994ef81848bf27292f3f2be4136264887
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/EnterpriseRAOAlgorithmFactory.hpp
@@ -0,0 +1,48 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "RAOAlgorithmFactory.hpp"
+#include "castor/tape/tapeserver/drive/DriveInterface.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+/**
+ * Factory of EnterpriseRAOAlgorithm. 
+ */
+class EnterpriseRAOAlgorithmFactory : public RAOAlgorithmFactory{
+public:
+  /**
+   * Constructor of this factory
+   * @param drive the DriveInterface to perform a RAO query 
+   * @param maxFilesSupported the maximum number of files the drive supports to perform a RAO query
+   */
+  EnterpriseRAOAlgorithmFactory(castor::tape::tapeserver::drive::DriveInterface * drive, const uint64_t maxFilesSupported);
+  
+  /**
+   * Returns an Enteprise RAO Algorithm
+   */
+  std::unique_ptr<RAOAlgorithm> createRAOAlgorithm() override;
+  virtual ~EnterpriseRAOAlgorithmFactory();
+private:
+  drive::DriveInterface * m_drive;
+  uint64_t m_maxFilesSupported;
+};
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/LinearRAOAlgorithm.cpp b/tapeserver/castor/tape/tapeserver/RAO/LinearRAOAlgorithm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..838e9cd0fbf0d39c1e655a2541483fdfd6e69db9
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/LinearRAOAlgorithm.cpp
@@ -0,0 +1,44 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <algorithm>
+#include <numeric>
+
+#include "LinearRAOAlgorithm.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+LinearRAOAlgorithm::LinearRAOAlgorithm() {
+}
+
+LinearRAOAlgorithm::~LinearRAOAlgorithm() {
+}
+
+std::vector<uint64_t> LinearRAOAlgorithm::performRAO(const std::vector<std::unique_ptr<cta::RetrieveJob> >& jobs) {
+  std::vector<uint64_t> raoIndices(jobs.size());
+  //Initialize the vector of indices
+  std::iota(raoIndices.begin(),raoIndices.end(),0);
+  //Sort the indices regarding the fseq of the jobs located in the vector passed in parameter
+  std::stable_sort(raoIndices.begin(),raoIndices.end(),[&jobs](const uint64_t index1, const uint64_t index2){
+    return jobs[index1]->selectedTapeFile().fSeq < jobs[index2]->selectedTapeFile().fSeq;
+  });
+  return raoIndices;
+}
+
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/LinearRAOAlgorithm.hpp b/tapeserver/castor/tape/tapeserver/RAO/LinearRAOAlgorithm.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..82a8c3ef7d85085b090dd4621fcc2a4ae7627cf5
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/LinearRAOAlgorithm.hpp
@@ -0,0 +1,49 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "RAOAlgorithm.hpp"
+#include "NoParamRAOAlgorithmFactory.hpp"
+
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+class NoParamRAOAlgorithmFactory;
+  
+/**
+ * This class represents a LinearRAOAlgorithm 
+ */
+class LinearRAOAlgorithm : public RAOAlgorithm {
+public:
+  friend NoParamRAOAlgorithmFactory;
+  
+  /**
+   * This method will return the indexes of the jobs that are reoreded in a linear way (sorted by fseq ascendant)
+   * Example : if the fseqs of jobs in parameter are arranged like this [2, 3, 1, 4] the 
+   * algorithm will return the following indexes vector : [2, 0, 1, 3]
+   * @param jobs the jobs to perform the linear RAO query
+   * @return the indexes of the jobs ordered by fseq ascendant
+   */
+  std::vector<uint64_t> performRAO(const std::vector<std::unique_ptr<cta::RetrieveJob> >& jobs) override;
+  virtual ~LinearRAOAlgorithm();
+private:
+  LinearRAOAlgorithm();
+};
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/NoParamRAOAlgorithmFactory.cpp b/tapeserver/castor/tape/tapeserver/RAO/NoParamRAOAlgorithmFactory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4941d21ab6f0c29246643e5f223f6f0a7be81f82
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/NoParamRAOAlgorithmFactory.cpp
@@ -0,0 +1,51 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "NoParamRAOAlgorithmFactory.hpp"
+#include "LinearRAOAlgorithm.hpp"
+#include "RandomRAOAlgorithm.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+NoParamRAOAlgorithmFactory::NoParamRAOAlgorithmFactory(const RAOConfig::RAOAlgorithmType & type) : m_type(type) {
+}
+
+NoParamRAOAlgorithmFactory::~NoParamRAOAlgorithmFactory() {
+}
+
+std::unique_ptr<RAOAlgorithm> NoParamRAOAlgorithmFactory::createRAOAlgorithm() {
+  std::unique_ptr<RAOAlgorithm> ret;
+  switch(m_type){
+    case RAOConfig::linear:{
+      ret.reset(new LinearRAOAlgorithm());
+      break;
+    }
+    case RAOConfig::random:{
+      ret.reset(new RandomRAOAlgorithm());
+      break;
+    }
+    default:
+    {
+      throw cta::exception::Exception("In NoParamRAOAlgorithmFactory::createRAOAlgorithm(): unknown type of algorithm");
+    }
+  }
+  return ret;
+}
+
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/NoParamRAOAlgorithmFactory.hpp b/tapeserver/castor/tape/tapeserver/RAO/NoParamRAOAlgorithmFactory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dcf05324fa45aee7249a359edb8a44000e719e6b
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/NoParamRAOAlgorithmFactory.hpp
@@ -0,0 +1,50 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once 
+
+#include "RAOConfig.hpp"
+#include "RAOAlgorithmFactory.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+  
+/**
+ * This factory allows to instanciate RAO algorithm that do not need any
+ * parameter to work. E.G the linear algorithm just does a sort of the fseqs,
+ * it does not need any parameter.
+ */
+class NoParamRAOAlgorithmFactory : public RAOAlgorithmFactory {
+public:
+  /**
+   * Constructor
+   * @param type the type given will be used by the createRAOAlgorithm() method
+   * to instanciate the correct algorithm regarding its type
+   */
+  NoParamRAOAlgorithmFactory(const RAOConfig::RAOAlgorithmType & type);
+  /**
+   * Returns the correct instance of RAO algorithm regarding the type
+   * given while constructing this factory.
+   * @throws Exception if the type is unknown
+   */
+  std::unique_ptr<RAOAlgorithm> createRAOAlgorithm() override;
+  virtual ~NoParamRAOAlgorithmFactory();
+private:
+  RAOConfig::RAOAlgorithmType m_type;
+};
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithm.cpp b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..90494f273b6ffa9127e0fb7d3920123a6c6f2861
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithm.cpp
@@ -0,0 +1,29 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "RAOAlgorithm.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+RAOAlgorithm::RAOAlgorithm() {
+}
+
+RAOAlgorithm::~RAOAlgorithm() {
+}
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithm.hpp b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithm.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c4e0fba9f680f910e09bf271283934316891592
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithm.hpp
@@ -0,0 +1,46 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <vector>
+#include <memory>
+#include "scheduler/RetrieveJob.hpp"
+
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+  
+/**
+ * Abstract class that represents an RAO algorithm
+ */
+class RAOAlgorithm {
+public:
+  RAOAlgorithm();
+  virtual ~RAOAlgorithm();
+  
+  /**
+   * Returns the vector of indexes of the jobs passed in parameter
+   * sorted according to an algorithm
+   * @param jobs the jobs to perform RAO on
+   * @return the vector of indexes sorted by an algorithm applied on the jobs passed in parameter
+   */
+  virtual std::vector<uint64_t> performRAO(const std::vector<std::unique_ptr<cta::RetrieveJob>> & jobs) = 0;
+
+};
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactory.cpp b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2cba088575c2036542243b33b2dc21b40a34a572
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactory.cpp
@@ -0,0 +1,29 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "RAOAlgorithmFactory.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+
+
+RAOAlgorithmFactory::~RAOAlgorithmFactory() {
+}
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactory.hpp b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e86a09379a9d840cecf987b6b22149973d5b3645
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactory.hpp
@@ -0,0 +1,42 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "RAOAlgorithm.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+/**
+ * Abstract class that will be extended by subclasses in order
+ * to instanciate an RAOAlgorithm
+ */
+class RAOAlgorithmFactory {
+public:
+  /**
+   * Creates an RAO algorithm that will be used to do a Recommended Access Order
+   * in order to give an optimized order to Retrieve files efficiently from a tape
+   * @return the RAO algorithm instance
+   */
+  virtual std::unique_ptr<RAOAlgorithm> createRAOAlgorithm() = 0;
+  virtual ~RAOAlgorithmFactory();
+private:
+
+};
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactoryFactory.cpp b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactoryFactory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b37e663f366d15a9534cad659c608c8f6b435625
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactoryFactory.cpp
@@ -0,0 +1,71 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "RAOAlgorithmFactoryFactory.hpp"
+#include "common/log/LogContext.hpp"
+#include "EnterpriseRAOAlgorithmFactory.hpp"
+#include "NoParamRAOAlgorithmFactory.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+RAOAlgorithmFactoryFactory::RAOAlgorithmFactoryFactory(RAOManager & raoManager, cta::log::LogContext & lc):m_raoManager(raoManager), m_lc(lc) {
+}
+
+std::unique_ptr<RAOAlgorithmFactory> RAOAlgorithmFactoryFactory::createAlgorithmFactory() {
+  std::unique_ptr<RAOAlgorithmFactory> ret;
+  auto maxFilesSupported = m_raoManager.getMaxFilesSupported();
+  if(m_raoManager.isDriveEnterpriseEnabled()) {
+    if(m_raoManager.hasUDS() && maxFilesSupported){
+      //We successfully queried the max limits UDS of the drive, we then return an Enterprise RAO factory
+      ret.reset(new EnterpriseRAOAlgorithmFactory(m_raoManager.getDrive(),maxFilesSupported.value()));
+    } 
+  } else {
+    //We will instanciate a CTA RAO algorithm
+    RAOConfig::RAOAlgorithmType raoAlgoType;
+    try {
+      raoAlgoType = m_raoManager.getConfig().getAlgorithmType();
+    } catch (const cta::exception::Exception & ex) {
+      //We failed to determine the RAOAlgorithmType, we use the linear algorithm by default
+      //log a warning
+      std::string msg = "In RAOAlgorithmFactoryFactory::createAlgorithmFactory(), unable to determine the RAO algorithm to use, the algorithm name provided"
+        " in the tapeserver configuration is " + m_raoManager.getConfig().getRAOAlgorithmName() + " the available algorithm names are " + m_raoManager.getConfig().getCTARAOAlgorithmNameAvailable()
+        + ". We will apply a linear algorithm instead.";
+      m_lc.log(cta::log::WARNING, msg);
+      raoAlgoType = RAOConfig::RAOAlgorithmType::linear;
+    }
+    switch(raoAlgoType){
+      case RAOConfig::RAOAlgorithmType::linear:
+      case RAOConfig::RAOAlgorithmType::random:
+      {
+        ret.reset(new NoParamRAOAlgorithmFactory(raoAlgoType));
+        break;
+      }
+      case RAOConfig::RAOAlgorithmType::sltf:
+      default:
+        break;
+    }
+  }
+  return ret;
+}
+
+
+RAOAlgorithmFactoryFactory::~RAOAlgorithmFactoryFactory() {
+}
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactoryFactory.hpp b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactoryFactory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c02404319ee1f8a05409c9455ea2e829890b77c
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOAlgorithmFactoryFactory.hpp
@@ -0,0 +1,53 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "common/log/LogContext.hpp"
+#include "RAOManager.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+/**
+ * Factory of RAOAlgorithmFactory
+ * This is where the logic of instanciating the correct RAOAlgorithmFactory is implemented.
+ * 
+ * This logic is the following:
+ * If we were able to query the drive User Data Segments (UDS) limits, then it means
+ * that the type of algorithm to instanciate will be an enterprise drive one (instanciate a EnteprpriseRAOAlgorithmFactory)
+ * 
+ * If we were not able to query the drive UDS limits, then it means that we will use a CTA RAO algorithm : linear, random or sltf
+ * 
+ * For implementation, see the implementation of the createAlgorithmFactory() method
+ */
+class RAOAlgorithmFactoryFactory {
+public:
+  RAOAlgorithmFactoryFactory(RAOManager & raoManager, cta::log::LogContext & lc);
+  /**
+   * Returns the correct RAOAlgorithmFactory according to the informations
+   * stored in the RAO manager
+   * @return 
+   */
+  std::unique_ptr<RAOAlgorithmFactory> createAlgorithmFactory();
+  virtual ~RAOAlgorithmFactoryFactory();
+private:
+  RAOManager & m_raoManager;
+  cta::log::LogContext & m_lc;
+};
+
+}}}}
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOConfig.cpp b/tapeserver/castor/tape/tapeserver/RAO/RAOConfig.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e324b2af9ff6c1ee8d77b6bfbbffe45046de4380
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOConfig.cpp
@@ -0,0 +1,75 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "RAOConfig.hpp"
+#include "common/exception/Exception.hpp"
+#include "common/utils/utils.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+  
+const std::map<std::string,RAOConfig::RAOAlgorithmType> RAOConfig::c_raoAlgoStringTypeMap = {
+  {"linear",RAOConfig::RAOAlgorithmType::linear},
+  {"random",RAOConfig::RAOAlgorithmType::random},
+  {"sltf",RAOConfig::RAOAlgorithmType::sltf}
+};  
+
+RAOConfig::RAOConfig(){}
+
+RAOConfig::RAOConfig(const bool useRAO, const std::string& raoAlgorithmName, const std::string raoAlgorithmOptions):m_useRAO(useRAO), m_raoAlgorithmName(raoAlgorithmName), m_raoAlgorithmOptions(raoAlgorithmOptions) {
+
+}
+
+bool RAOConfig::useRAO() const {
+  return m_useRAO;
+}
+
+std::string RAOConfig::getRAOAlgorithmName() const {
+  return m_raoAlgorithmName;
+}
+
+std::string RAOConfig::getRAOAlgorithmOptions() const {
+  return m_raoAlgorithmOptions;
+}
+
+void RAOConfig::disableRAO(){
+  m_useRAO = false;
+}
+
+RAOConfig::RAOAlgorithmType RAOConfig::getAlgorithmType() const {
+  try {
+    return c_raoAlgoStringTypeMap.at(m_raoAlgorithmName);  
+  } catch (const std::out_of_range &){
+    throw cta::exception::Exception("The algorithm name provided by the RAO configuration does not match any RAO algorithm type.");
+  }
+}
+
+std::string RAOConfig::getCTARAOAlgorithmNameAvailable() const {
+  std::string ret;
+  for(auto & kv: c_raoAlgoStringTypeMap){
+    ret += kv.first + " ";
+  }
+  if(ret.size()){
+    //remove last space
+    ret.resize(ret.size()-1);
+  }
+  return ret;
+}
+
+
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOConfig.hpp b/tapeserver/castor/tape/tapeserver/RAO/RAOConfig.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c1f3987afb219a467b2bca3518ca11736889330
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOConfig.hpp
@@ -0,0 +1,101 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <string>
+#include "tapeserver/castor/tape/tapeserver/daemon/DataTransferConfig.hpp"
+#include "tapeserver/castor/tape/tapeserver/SCSI/Structures.hpp"
+#include <memory>
+#include <common/log/LogContext.hpp>
+#include <vector>
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+  
+  /**
+   * This class contains the configuration of the CTA RAO Algorithm
+   * It is initialized by the DataTransferSession and is filled from the configuration file of the tapeserver.
+   */
+  class RAOConfig{
+  public:
+    /**
+     * This enum represent the RAO algorithm type implemented
+     * by CTA
+     */
+    enum RAOAlgorithmType {
+      linear,
+      random,
+      //Short Locate Time First
+      sltf
+    };
+
+    RAOConfig();
+    
+    /**
+     * Construct an RAO config
+     * @param useRAO if set to true, the RAO will be enabled. If false, not enabled.
+     * @param raoAlgorithmName the RAO algorithm that will be executed when called
+     * @param raoAlgorithmOptions the options that could be passed to the RAO algorithm
+     */
+    RAOConfig(const bool useRAO, const std::string & raoAlgorithmName, const std::string raoAlgorithmOptions);
+    
+    /**
+     * Returns true if RAO has to be used, false otherwise
+     */
+    bool useRAO() const;
+    
+    /**
+     * Returns the RAO algorithm name of this configuration
+     */
+    std::string getRAOAlgorithmName() const;
+    
+    /**
+     * Returns the RAO algorithm options of this configuration
+     * @return 
+     */
+    std::string getRAOAlgorithmOptions() const;
+    
+    /**
+     * Disable RAO from this configuration
+     */
+    void disableRAO();
+    
+    /**
+     * Returns RAOAlgorithmType object corresopnding to this configration's raoAlgorithmName
+     * @return the RAOAlgorithmType object corresopnding to this configration raoAlgorithmName
+     * @throws cta::exception::Exception in case the algorithm name does not match any RAOAlgorithmType
+     */
+    RAOAlgorithmType getAlgorithmType() const;
+    
+    /**
+     * Returns the CTA RAO algorithm name that can be used
+     */
+    std::string getCTARAOAlgorithmNameAvailable() const;
+    
+  private:
+    bool m_useRAO = false;
+    std::string m_raoAlgorithmName;
+    std::string m_raoAlgorithmOptions;
+    
+    /**
+     * Static map in order to match the string representing an algorithm name and its enum type
+     */
+    static const std::map<std::string, RAOAlgorithmType> c_raoAlgoStringTypeMap;
+  };
+  
+  
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOManager.cpp b/tapeserver/castor/tape/tapeserver/RAO/RAOManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..479c081a2e312097ea016ce6b13f94f24166c169
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOManager.cpp
@@ -0,0 +1,105 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "RAOManager.hpp"
+#include "EnterpriseRAOAlgorithm.hpp"
+#include "EnterpriseRAOAlgorithmFactory.hpp"
+#include "NoParamRAOAlgorithmFactory.hpp"
+#include "RAOAlgorithmFactoryFactory.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+  
+RAOManager::RAOManager(){
+  m_config.disableRAO();
+}  
+  
+RAOManager::RAOManager(const castor::tape::tapeserver::rao::RAOConfig & config, castor::tape::tapeserver::drive::DriveInterface * drive):m_config(config),m_drive(drive) {
+}
+
+RAOManager::RAOManager(const RAOManager& manager){
+  if(this != &manager){
+    m_config = manager.m_config;
+    m_drive = manager.m_drive;
+    m_enterpriseRaoLimits = manager.m_enterpriseRaoLimits;
+    m_hasUDS = manager.m_hasUDS;
+    m_maxFilesSupported = manager.m_maxFilesSupported;
+  }
+}
+
+RAOManager& RAOManager::operator=(const RAOManager& manager) {
+  if(this != &manager){
+    m_config = manager.m_config;
+    m_drive = manager.m_drive;
+    m_enterpriseRaoLimits = manager.m_enterpriseRaoLimits;
+    m_hasUDS = manager.m_hasUDS;
+    m_maxFilesSupported = manager.m_maxFilesSupported;
+  }
+  return *this;
+}
+
+
+RAOManager::~RAOManager() {
+}
+
+bool RAOManager::useRAO() const{
+  return m_config.useRAO();
+}
+
+bool RAOManager::hasUDS() const {
+  return m_hasUDS;
+}
+
+bool RAOManager::isDriveEnterpriseEnabled() const {
+  return m_isDriveEnterpriseEnabled;
+}
+
+
+castor::tape::tapeserver::drive::DriveInterface* RAOManager::getDrive() const {
+  return m_drive;
+}
+
+
+void RAOManager::disableRAO(){
+  m_config.disableRAO();
+}
+
+void RAOManager::setEnterpriseRAOUdsLimits(const SCSI::Structures::RAO::udsLimits& raoLimits) {
+  m_enterpriseRaoLimits = raoLimits;
+  m_maxFilesSupported = raoLimits.maxSupported;
+  m_hasUDS = true;
+  m_isDriveEnterpriseEnabled = true;
+}
+
+cta::optional<uint64_t> RAOManager::getMaxFilesSupported() const{
+  return m_maxFilesSupported;
+}
+
+RAOConfig RAOManager::getConfig() const {
+  return m_config;
+}
+
+
+std::vector<uint64_t> RAOManager::queryRAO(const std::vector<std::unique_ptr<cta::RetrieveJob>> & jobs, cta::log::LogContext & lc){
+  RAOAlgorithmFactoryFactory raoAlgoFactoryFactory(*this,lc);
+  std::unique_ptr<RAOAlgorithm> raoAlgo = raoAlgoFactoryFactory.createAlgorithmFactory()->createRAOAlgorithm();
+  return raoAlgo->performRAO(jobs);
+}
+
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RAOManager.hpp b/tapeserver/castor/tape/tapeserver/RAO/RAOManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0e8796a0122ef888903b8036fe351e58b44dd669
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RAOManager.hpp
@@ -0,0 +1,131 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include <vector>
+#include <memory>
+#include "castor/tape/tapeserver/RAO/RAOConfig.hpp"
+#include "castor/tape/tapeserver/drive/DriveInterface.hpp"
+#include "castor/tape/tapeserver/SCSI/Structures.hpp"
+#include "scheduler/RetrieveJob.hpp"
+#include "common/optional.hpp"
+#include "RAOAlgorithmFactory.hpp"
+#include "common/log/LogContext.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+  
+/**
+ * This class will be used to manage everything that is linked to RAO.
+ * It centralizes all the RAO-related task that are executed during the
+ * RecallTaskInjector lifecycle.
+ */
+class RAOManager {
+public:
+  /**
+   * Default constructor, disable the RAO
+   */
+  RAOManager();
+  
+  /**
+   * Copy constructor
+   * @param manager the RAOManager to copy
+   */
+  RAOManager(const RAOManager & manager);
+  
+  /**
+   * Constructor of a RAO manager
+   * @param config the configuration of the RAO to manage
+   * @param drive the DriveInterface of the drive that is mounting
+   */
+  RAOManager(const RAOConfig & config, castor::tape::tapeserver::drive::DriveInterface * drive);
+  
+  /**
+   * Assignment operator
+   * */
+  RAOManager & operator=(const RAOManager & manager);
+  
+  /**
+   * Returns true if RAO will be used, false otherwise
+   */
+  bool useRAO() const;
+  
+  /**
+   * Returns true if the manager has informations about the drive's User Data Segments limits to
+   * perform RAO for enteprise drive
+   */
+  bool hasUDS() const;
+  
+  /**
+   * Returns true if the manager can ask for an Enterprise RAO Algorithm
+   * false otherwise
+   */
+  bool isDriveEnterpriseEnabled() const;
+  
+  /**
+   * Returns the pointer to the interface of that is mounting
+   */
+  castor::tape::tapeserver::drive::DriveInterface * getDrive() const;
+  
+  /**
+   * Returns the number of files that will be supported by the RAO algorithm
+   */
+  cta::optional<uint64_t> getMaxFilesSupported() const;
+  
+  /**
+   * Returns the RAOConfig that is used by this RAOManager
+   */
+  RAOConfig getConfig() const;
+  
+  /**
+   * Disable the RAO algorithm
+   */
+  void disableRAO();
+  
+  /**
+   * Set the enterprise RAO User Data Segments limits
+   * that will be used by this manager to perform the Enterprise RAO query on the drive
+   * @param raoLimits the enterprise RAO user data segments limits
+   */
+  void setEnterpriseRAOUdsLimits(const SCSI::Structures::RAO::udsLimits & raoLimits);
+  
+  /**
+   * Query the RAO of the files passed in parameter
+   * @param jobs the vector of jobs to query the RAO
+   * @param lc the log context
+   * @return the vector with re-arranged indexes of the jobs passed in parameter
+   * It does not returns the fseqs, but a vector of indexes that will be used by the recall task injector to pick
+   * the correct job after RAO has been done
+   */
+  std::vector<uint64_t> queryRAO(const std::vector<std::unique_ptr<cta::RetrieveJob>> & jobs, cta::log::LogContext & lc);
+  
+  virtual ~RAOManager();
+  
+private:
+  RAOConfig m_config;
+  /** Enterprise Drive-specific RAO parameters */
+  SCSI::Structures::RAO::udsLimits m_enterpriseRaoLimits;
+  //Is true if the drive have been able to get the RAO UDS limits numbers
+  bool m_hasUDS = false;
+  //The maximum number of files that will be considered for RAO
+  cta::optional<uint64_t> m_maxFilesSupported;
+  //Pointer to the drive interface of the drive currently used by the tapeserver
+  castor::tape::tapeserver::drive::DriveInterface * m_drive;
+  bool m_isDriveEnterpriseEnabled = false;
+};
+
+}}}}
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RandomRAOAlgorithm.cpp b/tapeserver/castor/tape/tapeserver/RAO/RandomRAOAlgorithm.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..44f3a5b5656a2debb7b85633bd281a53b0bb90dc
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RandomRAOAlgorithm.cpp
@@ -0,0 +1,36 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "RandomRAOAlgorithm.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+RandomRAOAlgorithm::RandomRAOAlgorithm() {
+}
+
+std::vector<uint64_t> RandomRAOAlgorithm::performRAO(const std::vector<std::unique_ptr<cta::RetrieveJob> >& jobs) {
+  std::vector<uint64_t> raoIndices(jobs.size());
+  std::iota(raoIndices.begin(),raoIndices.end(),0);
+  std::random_shuffle(raoIndices.begin(), raoIndices.end());
+  return raoIndices;
+}
+
+RandomRAOAlgorithm::~RandomRAOAlgorithm() {
+}
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/RAO/RandomRAOAlgorithm.hpp b/tapeserver/castor/tape/tapeserver/RAO/RandomRAOAlgorithm.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b93be12ee3be06585890bdd086530181582db03
--- /dev/null
+++ b/tapeserver/castor/tape/tapeserver/RAO/RandomRAOAlgorithm.hpp
@@ -0,0 +1,46 @@
+/*
+ * The CERN Tape Archive (CTA) project
+ * Copyright (C) 2019  CERN
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "RAOAlgorithm.hpp"
+#include "NoParamRAOAlgorithmFactory.hpp"
+
+namespace castor { namespace tape { namespace tapeserver { namespace rao {
+
+class NoParamRAOAlgorithmFactory;
+
+/**
+ * This RAO Algorithm is a random one. The indexes of the jobs passed in parameter
+ * will be organized randomly 
+ */
+class RandomRAOAlgorithm : public RAOAlgorithm{
+public:
+  friend NoParamRAOAlgorithmFactory;
+  /**
+   * Returns a randomly organized vector of the indexes of the jobs passed in parameter
+   * @param jobs the jobs to perform the random RAO on
+   */
+  std::vector<uint64_t> performRAO(const std::vector<std::unique_ptr<cta::RetrieveJob> >& jobs) override;
+  
+  virtual ~RandomRAOAlgorithm();
+private:
+  RandomRAOAlgorithm();
+};
+
+}}}}
\ No newline at end of file
diff --git a/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt b/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt
index aff6ac79437fe4309bf007c43e2af7772ffd4f9b..09fe5024d4177e1e4ff73c90d8f1102ca58f307f 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt
+++ b/tapeserver/castor/tape/tapeserver/daemon/CMakeLists.txt
@@ -81,7 +81,7 @@ endif(CMAKE_COMPILER_IS_GNUCC)
 add_library(ctaTapeServerDaemon 
   ${CTATAPESERVERDAEMON_LIBRARY_SRCS})
 
-target_link_libraries(ctaTapeServerDaemon ctamessages ctacommon ${PROTOBUF3_LIBRARIES} ctascheduler ctalegacymsg ctacatalogue TapeDrive)
+target_link_libraries(ctaTapeServerDaemon ctamessages ctacommon ${PROTOBUF3_LIBRARIES} ctascheduler ctalegacymsg ctacatalogue TapeDrive ctarao)
 add_dependencies(ctaTapeServerDaemon ctamessagesprotobuf)
 
 include_directories(${PROTOBUF3_INCLUDE_DIRS})
diff --git a/tapeserver/castor/tape/tapeserver/daemon/DataTransferConfig.hpp b/tapeserver/castor/tape/tapeserver/daemon/DataTransferConfig.hpp
index d153d909d2beaa6c91c8cf97663f5c8712838612..db460aced0a294d7cff3757320ba08dd78f0668a 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/DataTransferConfig.hpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/DataTransferConfig.hpp
@@ -119,9 +119,14 @@ struct DataTransferConfig {
   bool useRAO;
   
   /**
-   * The name of the raoAlgorithm to use
+   * The name of the RAO LTO algorithm to use
    */
-  std::string raoAlgorithm;
+  std::string raoLtoAlgorithm;
+  
+  /**
+   * The options that can be used by the raoAlgorithm
+   */
+  std::string raoLtoAlgorithmOptions;
 
   /**
    * The path to the operator provided encyption control script (or empty string)
diff --git a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSession.cpp b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSession.cpp
index 74fe8be20fd7338ec10728003a19f3ff6e98ad17..dc97c238615b62ad667f47fd777c42ac09e672cd 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSession.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSession.cpp
@@ -39,6 +39,7 @@
 #include "castor/tape/tapeserver/SCSI/Device.hpp"
 #include "common/exception/Exception.hpp"
 #include "scheduler/RetrieveMount.hpp"
+#include "castor/tape/tapeserver/RAO/RAOConfig.hpp"
 
 #include <google/protobuf/stubs/common.h>
 #include <memory>
@@ -272,7 +273,9 @@ castor::tape::tapeserver::daemon::Session::EndOfSessionAction
 
     // The RecallTaskInjector and the TapeReadSingleThread share the promise
     if (m_castorConf.useRAO) {
-      rti.initRAO(m_castorConf.raoAlgorithm);
+      //castor::tape::tapeserver::rao::RAOConfig config;
+      castor::tape::tapeserver::rao::RAOConfig config(true, m_castorConf.raoLtoAlgorithm, m_castorConf.raoLtoAlgorithmOptions);
+      rti.initRAO(config);
     }
     bool noFilesToRecall = false;
     if (rti.synchronousFetch(noFilesToRecall)) {  //adapt the recall task injector (starting from synchronousFetch)
diff --git a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp
index 99b8e5e428b108480f9771b47a475956c6aaf21c..7d00ace863a5da0d5b2215632f4101ea73124af3 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/DataTransferSessionTest.cpp
@@ -319,6 +319,30 @@ public:
     mediaType.comment = "comment";
     catalogue.createMediaType(s_adminOnAdminHost,mediaType);
   }
+  
+  /**
+   * Returns the map of Fseqs given by RAO from a string containing CTA logs
+   * @param log the string containing the CTA logs
+   * @return the map that gives for each RAO call, the associated ordered Fseqs according to the RAO algorithm result
+   */
+  std::map<size_t, std::vector<std::string>> getRAOFseqs(const std::string & log){
+    std::map<size_t,std::vector<std::string>> ret;
+    std::string logCopy = log;
+    std::string recallRAOOrderMsg = "Recall order of FSEQs using RAO: ";
+    size_t recallRAOOrderMsgSize = recallRAOOrderMsg.size();
+    size_t posRAOMsg = logCopy.find(recallRAOOrderMsg);
+    size_t i = 0;
+    while(posRAOMsg != std::string::npos){
+      size_t posFirstFseq = posRAOMsg + recallRAOOrderMsgSize;
+      size_t posLastFseq = logCopy.find("\"",posFirstFseq);
+      std::string stringFseq = logCopy.substr(posFirstFseq,posLastFseq - posFirstFseq);
+      cta::utils::splitString(stringFseq,' ',ret[i]);
+      logCopy = logCopy.substr(posLastFseq);
+      posRAOMsg = logCopy.find(recallRAOOrderMsg);
+      i++;
+    }
+    return ret;
+  }
 
 private:
 
@@ -820,9 +844,8 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
 
   int MAX_RECALLS = 50;
   int MAX_BULK_RECALLS = 31;
-  std::vector<int> expectedOrder;
-  std::vector<std::string> expectedFseqOrderLog;
-
+  std::map<size_t,std::vector<std::string>> expectedRAOFseqOrder;
+  //RAO for the fake drive is a std::reverse
   // 6) Prepare files for reading by writing them to the mock system
   {
     // Label the tape
@@ -842,6 +865,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
     castor::tape::SCSI::Structures::zeroStruct(&data);
     int fseq;
     for (fseq=1; fseq <= MAX_RECALLS ; fseq ++) {
+      expectedRAOFseqOrder[fseq / MAX_BULK_RECALLS].push_back(std::to_string(fseq));
       // Create a path to a remote destination file
       std::ostringstream remoteFilePath;
       remoteFilePath << "file://" << m_tmpDir << "/test" << fseq;
@@ -891,38 +915,205 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
       std::list<std::string> archiveFilePaths;
       scheduler.queueRetrieve(diskInstance, rReq, logContext);
 
-      expectedOrder.push_back(fseq);
+    }
+  }
+  //As RAO with the fakedrive is a std::reverse of the vector of the files given in parameter,
+  //we reverse all expected fseqs vectors
+  std::reverse(expectedRAOFseqOrder[0].begin(),expectedRAOFseqOrder[0].end());
+  std::reverse(expectedRAOFseqOrder[1].begin(),expectedRAOFseqOrder[1].end());
+  scheduler.waitSchedulerDbSubthreadsComplete();
+    
+  // 6) Report the drive's existence and put it up in the drive register.
+  cta::tape::daemon::TpconfigLine driveConfig("T10D6116", "TestLogicalLibrary", "/dev/tape_T10D6116", "manual");
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName=driveConfig.unitName;
+  driveInfo.logicalLibrary=driveConfig.rawLibrarySlot;
+  // We need to create the drive in the registry before being able to put it up.
+  scheduler.reportDriveStatus(driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, logContext);
+  cta::common::dataStructures::DesiredDriveState driveState;
+  driveState.up = true;
+  driveState.forceDown = false;
+  scheduler.setDesiredDriveState(s_adminOnAdminHost, driveConfig.unitName, driveState, logContext);
 
-      bool apply_rao = false;
-      bool add_expected = false;
-      if (MAX_BULK_RECALLS < 2) {
-        if (expectedOrder.size() % MAX_BULK_RECALLS == 0 ||
-            fseq % MAX_RECALLS == 0) {
-          add_expected = true;
-        }
-      }
-      else if (MAX_BULK_RECALLS >= 30) {
-        if ((expectedOrder.size() % 30 == 0) ||
-            (fseq % MAX_RECALLS == 0) || (fseq % MAX_BULK_RECALLS == 0)) {
-          apply_rao = true;
-          add_expected = true;
-        }
-      }
-      else if ((fseq % MAX_BULK_RECALLS == 0) || (fseq % MAX_RECALLS == 0)) {
-        apply_rao = true;
-        add_expected = true;
-      }
-      if (apply_rao) {
-        std::reverse(expectedOrder.begin(), expectedOrder.end());
-      }
-      if (add_expected) {
-        std::stringstream expectedLogLine;
-        for(const auto &fseq: expectedOrder) {
-          expectedLogLine << " " << fseq;
-        }
-        expectedFseqOrderLog.push_back(expectedLogLine.str());
-        expectedOrder.clear();
-      }
+  // 7) Create the data transfer session
+  DataTransferConfig castorConf;
+  castorConf.bufsz = 1024*1024; // 1 MB memory buffers
+  castorConf.nbBufs = 10;
+  castorConf.bulkRequestRecallMaxBytes = UINT64_C(100)*1000*1000*1000;
+  castorConf.bulkRequestRecallMaxFiles = MAX_BULK_RECALLS - 1;
+  castorConf.nbDiskThreads = 1;
+  castorConf.useRAO = true;
+  cta::log::DummyLogger dummyLog("dummy", "dummy");
+  cta::mediachanger::MediaChangerFacade mc(dummyLog);
+  cta::server::ProcessCap capUtils;
+  castor::messages::TapeserverProxyDummy initialProcess;
+  castor::tape::tapeserver::daemon::DataTransferSession sess("tapeHost", logger, mockSys,
+    driveConfig, mc, initialProcess, capUtils, castorConf, scheduler);
+
+  // 8) Run the data transfer session
+  sess.execute();
+
+  // 9) Check the session git the correct VID
+  ASSERT_EQ(s_vid, sess.getVid());
+
+  // 10) Check the remote files exist and have the correct size
+  for(auto & path: remoteFilePaths) {
+    struct stat statBuf;
+    bzero(&statBuf, sizeof(statBuf));
+    const int statRc = stat(path.substr(7).c_str(), &statBuf); //remove the "file://" for stat-ing
+    ASSERT_EQ(0, statRc);
+    ASSERT_EQ(1000, statBuf.st_size); //same size of data
+  }
+
+  // 10) Check logs
+  std::string logToCheck = logger.getLog();
+  logToCheck += "";
+  ASSERT_NE(std::string::npos, logToCheck.find("firmwareVersion=\"123A\" serialNumber=\"123456\" "
+                                               "mountTotalCorrectedReadErrors=\"5\" mountTotalReadBytesProcessed=\"4096\" "
+                                               "mountTotalUncorrectedReadErrors=\"1\" mountTotalNonMediumErrorCounts=\"2\""));
+  ASSERT_NE(std::string::npos, logToCheck.find("firmwareVersion=\"123A\" serialNumber=\"123456\" lifetimeMediumEfficiencyPrct=\"100\" "
+                                               "mountReadEfficiencyPrct=\"100\" mountWriteEfficiencyPrct=\"100\" "
+                                               "mountReadTransients=\"10\" "
+                                               "mountServoTemps=\"10\" mountServoTransients=\"5\" mountTemps=\"100\" "
+                                               "mountTotalReadRetries=\"25\" mountTotalWriteRetries=\"25\" mountWriteTransients=\"10\""));
+  
+  ASSERT_EQ(expectedRAOFseqOrder,getRAOFseqs(logToCheck));
+}
+
+TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallLinearAlgorithm) {
+  // 0) Prepare the logger for everyone
+  cta::log::StringLogger logger("dummy","tapeServerUnitTest",cta::log::DEBUG);
+  cta::log::LogContext logContext(logger);
+  
+  setupDefaultCatalogue();
+  // 1) prepare the fake scheduler
+  std::string vid = s_vid;
+  // cta::MountType::Enum mountType = cta::MountType::RETRIEVE;
+
+  // 3) Prepare the necessary environment (logger, plus system wrapper), 
+  castor::tape::System::mockWrapper mockSys;
+  mockSys.delegateToFake();
+  mockSys.disableGMockCallsCounting();
+  mockSys.fake.setupForVirtualDriveSLC6();
+  //delete is unnecessary
+  //pointer with ownership will be passed to the application,
+  //which will do the delete 
+  mockSys.fake.m_pathToDrive["/dev/nst0"] = new castor::tape::tapeserver::drive::FakeNonRAODrive;
+
+  // 4) Create the scheduler
+  auto & catalogue = getCatalogue();
+  auto & scheduler = getScheduler();
+  
+  // Always use the same requester
+  const cta::common::dataStructures::SecurityIdentity requester;
+
+  // List to remember the path of each remote file so that the existance of the
+  // files can be tested for at the end of the test
+  std::list<std::string> remoteFilePaths;
+  
+  // 5) Create the environment for the migration to happen (library + tape) 
+    const std::string libraryComment = "Library comment";
+  const bool libraryIsDisabled = false;
+  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+    libraryIsDisabled, libraryComment);
+  {
+    auto libraries = catalogue.getLogicalLibraries();
+    ASSERT_EQ(1, libraries.size());
+    ASSERT_EQ(s_libraryName, libraries.front().name);
+    ASSERT_EQ(libraryComment, libraries.front().comment);
+  }
+  const std::string tapeComment = "Tape comment";
+  bool notDisabled = false;
+  bool notFull = false;
+  bool notReadOnly = false;
+
+  {
+    cta::catalogue::CreateTapeAttributes tape;
+    tape.vid = s_vid;
+    tape.mediaType = s_mediaType;
+    tape.vendor = s_vendor;
+    tape.logicalLibraryName = s_libraryName;
+    tape.tapePoolName = s_tapePoolName;
+    tape.full = notFull;
+    tape.disabled = notDisabled;
+    tape.readOnly = notReadOnly;
+    tape.comment = tapeComment;
+    catalogue.createTape(s_adminOnAdminHost, tape);
+  }
+
+  int MAX_RECALLS = 50;
+  int MAX_BULK_RECALLS = 31;
+  std::map<size_t,std::vector<std::string>> expectedRAOOrder;
+  // 6) Prepare files for reading by writing them to the mock system
+  {
+    // Label the tape
+    castor::tape::tapeFile::LabelSession ls(*mockSys.fake.m_pathToDrive["/dev/nst0"], 
+        s_vid, false);
+    mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
+    // And write to it
+    castor::tape::tapeserver::daemon::VolumeInfo volInfo;
+    volInfo.vid=s_vid;
+    castor::tape::tapeFile::WriteSession ws(*mockSys.fake.m_pathToDrive["/dev/nst0"],
+       volInfo , 0, true, false);
+
+    // Write a few files on the virtual tape and modify the archive name space
+    // so that it is in sync
+    uint8_t data[1000];
+    size_t archiveFileSize=sizeof(data);
+    castor::tape::SCSI::Structures::zeroStruct(&data);
+    int fseq;
+    //For the RAO orders we will have two rao calls : first with 30 files,
+    //the second with 20 files
+    for (fseq=1; fseq <= MAX_RECALLS ; fseq ++) {
+      expectedRAOOrder[fseq / MAX_BULK_RECALLS].push_back(std::to_string(fseq));
+      // Create a path to a remote destination file
+      std::ostringstream remoteFilePath;
+      remoteFilePath << "file://" << m_tmpDir << "/test" << fseq;
+      remoteFilePaths.push_back(remoteFilePath.str());
+
+      // Create an archive file entry in the archive namespace
+      auto tapeFileWrittenUP = cta::make_unique<cta::catalogue::TapeFileWritten>();
+      auto &tapeFileWritten=*tapeFileWrittenUP;
+      std::set<cta::catalogue::TapeItemWrittenPointer> tapeFileWrittenSet;
+      tapeFileWrittenSet.insert(tapeFileWrittenUP.release());
+      
+      // Write the file to tape
+      cta::MockArchiveMount mam(catalogue);
+      std::unique_ptr<cta::ArchiveJob> aj(new cta::MockArchiveJob(&mam, catalogue));
+      aj->tapeFile.fSeq = fseq;
+      aj->archiveFile.archiveFileID = fseq;
+      castor::tape::tapeFile::WriteFile wf(&ws, *aj, archiveFileSize);
+      tapeFileWritten.blockId = wf.getBlockId();
+      // Write the data (one block)
+      wf.write(data, archiveFileSize);
+      // Close the file
+      wf.close();
+
+      // Create file entry in the archive namespace
+      tapeFileWritten.archiveFileId=fseq;
+      tapeFileWritten.checksumBlob.insert(cta::checksum::ADLER32, cta::utils::getAdler32(data, archiveFileSize));
+      tapeFileWritten.vid=volInfo.vid;
+      tapeFileWritten.size=archiveFileSize;
+      tapeFileWritten.fSeq=fseq;
+      tapeFileWritten.copyNb=1;
+      tapeFileWritten.diskInstance = s_diskInstance;
+      tapeFileWritten.diskFileId = fseq;
+      
+      tapeFileWritten.diskFileOwnerUid = DISK_FILE_SOME_USER;
+      tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
+      tapeFileWritten.storageClassName = s_storageClassName;
+      tapeFileWritten.tapeDrive = "drive0";
+      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+
+      // Schedule the retrieval of the file
+      std::string diskInstance="disk_instance";
+      cta::common::dataStructures::RetrieveRequest rReq;
+      rReq.archiveFileID=fseq;
+      rReq.requester.name = s_userName;
+      rReq.requester.group = "someGroup";
+      rReq.dstURL = remoteFilePaths.back();
+      std::list<std::string> archiveFilePaths;
+      scheduler.queueRetrieve(diskInstance, rReq, logContext);
     }
   }
   scheduler.waitSchedulerDbSubthreadsComplete();
@@ -947,6 +1138,7 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
   castorConf.bulkRequestRecallMaxFiles = MAX_BULK_RECALLS - 1;
   castorConf.nbDiskThreads = 1;
   castorConf.useRAO = true;
+  castorConf.raoLtoAlgorithm = "linear";
   cta::log::DummyLogger dummyLog("dummy", "dummy");
   cta::mediachanger::MediaChangerFacade mc(dummyLog);
   cta::server::ProcessCap capUtils;
@@ -980,9 +1172,206 @@ TEST_P(DataTransferSessionTest, DataTransferSessionRAORecall) {
                                                "mountReadTransients=\"10\" "
                                                "mountServoTemps=\"10\" mountServoTransients=\"5\" mountTemps=\"100\" "
                                                "mountTotalReadRetries=\"25\" mountTotalWriteRetries=\"25\" mountWriteTransients=\"10\""));
-  for (std::string s : expectedFseqOrderLog) {
-    ASSERT_NE(std::string::npos, logToCheck.find(s));
+  
+  ASSERT_EQ(expectedRAOOrder,getRAOFseqs(logToCheck));
+}
+
+TEST_P(DataTransferSessionTest, DataTransferSessionRAORecallRAOAlgoDoesNotExistShouldApplyLinear) {
+  // 0) Prepare the logger for everyone
+  cta::log::StringLogger logger("dummy","tapeServerUnitTest",cta::log::DEBUG);
+  cta::log::LogContext logContext(logger);
+  
+  setupDefaultCatalogue();
+  // 1) prepare the fake scheduler
+  std::string vid = s_vid;
+  // cta::MountType::Enum mountType = cta::MountType::RETRIEVE;
+
+  // 3) Prepare the necessary environment (logger, plus system wrapper), 
+  castor::tape::System::mockWrapper mockSys;
+  mockSys.delegateToFake();
+  mockSys.disableGMockCallsCounting();
+  mockSys.fake.setupForVirtualDriveSLC6();
+  //delete is unnecessary
+  //pointer with ownership will be passed to the application,
+  //which will do the delete 
+  mockSys.fake.m_pathToDrive["/dev/nst0"] = new castor::tape::tapeserver::drive::FakeNonRAODrive;
+
+  // 4) Create the scheduler
+  auto & catalogue = getCatalogue();
+  auto & scheduler = getScheduler();
+  
+  // Always use the same requester
+  const cta::common::dataStructures::SecurityIdentity requester;
+
+  // List to remember the path of each remote file so that the existance of the
+  // files can be tested for at the end of the test
+  std::list<std::string> remoteFilePaths;
+  
+  // 5) Create the environment for the migration to happen (library + tape) 
+    const std::string libraryComment = "Library comment";
+  const bool libraryIsDisabled = false;
+  catalogue.createLogicalLibrary(s_adminOnAdminHost, s_libraryName,
+    libraryIsDisabled, libraryComment);
+  {
+    auto libraries = catalogue.getLogicalLibraries();
+    ASSERT_EQ(1, libraries.size());
+    ASSERT_EQ(s_libraryName, libraries.front().name);
+    ASSERT_EQ(libraryComment, libraries.front().comment);
+  }
+  const std::string tapeComment = "Tape comment";
+  bool notDisabled = false;
+  bool notFull = false;
+  bool notReadOnly = false;
+
+  {
+    cta::catalogue::CreateTapeAttributes tape;
+    tape.vid = s_vid;
+    tape.mediaType = s_mediaType;
+    tape.vendor = s_vendor;
+    tape.logicalLibraryName = s_libraryName;
+    tape.tapePoolName = s_tapePoolName;
+    tape.full = notFull;
+    tape.disabled = notDisabled;
+    tape.readOnly = notReadOnly;
+    tape.comment = tapeComment;
+    catalogue.createTape(s_adminOnAdminHost, tape);
   }
+
+  int MAX_RECALLS = 50;
+  int MAX_BULK_RECALLS = 31;
+  std::map<size_t,std::vector<std::string>> expectedRAOOrder;
+  // 6) Prepare files for reading by writing them to the mock system
+  {
+    // Label the tape
+    castor::tape::tapeFile::LabelSession ls(*mockSys.fake.m_pathToDrive["/dev/nst0"], 
+        s_vid, false);
+    mockSys.fake.m_pathToDrive["/dev/nst0"]->rewind();
+    // And write to it
+    castor::tape::tapeserver::daemon::VolumeInfo volInfo;
+    volInfo.vid=s_vid;
+    castor::tape::tapeFile::WriteSession ws(*mockSys.fake.m_pathToDrive["/dev/nst0"],
+       volInfo , 0, true, false);
+
+    // Write a few files on the virtual tape and modify the archive name space
+    // so that it is in sync
+    uint8_t data[1000];
+    size_t archiveFileSize=sizeof(data);
+    castor::tape::SCSI::Structures::zeroStruct(&data);
+    int fseq;
+    //For the RAO orders we will have two rao calls : first with 30 files,
+    //the second with 20 files
+    for (fseq=1; fseq <= MAX_RECALLS ; fseq ++) {
+      expectedRAOOrder[fseq / MAX_BULK_RECALLS].push_back(std::to_string(fseq));
+      // Create a path to a remote destination file
+      std::ostringstream remoteFilePath;
+      remoteFilePath << "file://" << m_tmpDir << "/test" << fseq;
+      remoteFilePaths.push_back(remoteFilePath.str());
+
+      // Create an archive file entry in the archive namespace
+      auto tapeFileWrittenUP = cta::make_unique<cta::catalogue::TapeFileWritten>();
+      auto &tapeFileWritten=*tapeFileWrittenUP;
+      std::set<cta::catalogue::TapeItemWrittenPointer> tapeFileWrittenSet;
+      tapeFileWrittenSet.insert(tapeFileWrittenUP.release());
+      
+      // Write the file to tape
+      cta::MockArchiveMount mam(catalogue);
+      std::unique_ptr<cta::ArchiveJob> aj(new cta::MockArchiveJob(&mam, catalogue));
+      aj->tapeFile.fSeq = fseq;
+      aj->archiveFile.archiveFileID = fseq;
+      castor::tape::tapeFile::WriteFile wf(&ws, *aj, archiveFileSize);
+      tapeFileWritten.blockId = wf.getBlockId();
+      // Write the data (one block)
+      wf.write(data, archiveFileSize);
+      // Close the file
+      wf.close();
+
+      // Create file entry in the archive namespace
+      tapeFileWritten.archiveFileId=fseq;
+      tapeFileWritten.checksumBlob.insert(cta::checksum::ADLER32, cta::utils::getAdler32(data, archiveFileSize));
+      tapeFileWritten.vid=volInfo.vid;
+      tapeFileWritten.size=archiveFileSize;
+      tapeFileWritten.fSeq=fseq;
+      tapeFileWritten.copyNb=1;
+      tapeFileWritten.diskInstance = s_diskInstance;
+      tapeFileWritten.diskFileId = fseq;
+      
+      tapeFileWritten.diskFileOwnerUid = DISK_FILE_SOME_USER;
+      tapeFileWritten.diskFileGid = DISK_FILE_SOME_GROUP;
+      tapeFileWritten.storageClassName = s_storageClassName;
+      tapeFileWritten.tapeDrive = "drive0";
+      catalogue.filesWrittenToTape(tapeFileWrittenSet);
+
+      // Schedule the retrieval of the file
+      std::string diskInstance="disk_instance";
+      cta::common::dataStructures::RetrieveRequest rReq;
+      rReq.archiveFileID=fseq;
+      rReq.requester.name = s_userName;
+      rReq.requester.group = "someGroup";
+      rReq.dstURL = remoteFilePaths.back();
+      std::list<std::string> archiveFilePaths;
+      scheduler.queueRetrieve(diskInstance, rReq, logContext);
+    }
+  }
+  scheduler.waitSchedulerDbSubthreadsComplete();
+    
+  // 6) Report the drive's existence and put it up in the drive register.
+  cta::tape::daemon::TpconfigLine driveConfig("T10D6116", "TestLogicalLibrary", "/dev/tape_T10D6116", "manual");
+  cta::common::dataStructures::DriveInfo driveInfo;
+  driveInfo.driveName=driveConfig.unitName;
+  driveInfo.logicalLibrary=driveConfig.rawLibrarySlot;
+  // We need to create the drive in the registry before being able to put it up.
+  scheduler.reportDriveStatus(driveInfo, cta::common::dataStructures::MountType::NoMount, cta::common::dataStructures::DriveStatus::Down, logContext);
+  cta::common::dataStructures::DesiredDriveState driveState;
+  driveState.up = true;
+  driveState.forceDown = false;
+  scheduler.setDesiredDriveState(s_adminOnAdminHost, driveConfig.unitName, driveState, logContext);
+
+  // 7) Create the data transfer session
+  DataTransferConfig castorConf;
+  castorConf.bufsz = 1024*1024; // 1 MB memory buffers
+  castorConf.nbBufs = 10;
+  castorConf.bulkRequestRecallMaxBytes = UINT64_C(100)*1000*1000*1000;
+  castorConf.bulkRequestRecallMaxFiles = MAX_BULK_RECALLS - 1;
+  castorConf.nbDiskThreads = 1;
+  castorConf.useRAO = true;
+  castorConf.raoLtoAlgorithm = "DOES_NOT_EXIST";
+  cta::log::DummyLogger dummyLog("dummy", "dummy");
+  cta::mediachanger::MediaChangerFacade mc(dummyLog);
+  cta::server::ProcessCap capUtils;
+  castor::messages::TapeserverProxyDummy initialProcess;
+  castor::tape::tapeserver::daemon::DataTransferSession sess("tapeHost", logger, mockSys,
+    driveConfig, mc, initialProcess, capUtils, castorConf, scheduler);
+
+  // 8) Run the data transfer session
+  sess.execute();
+
+  // 9) Check the session git the correct VID
+  ASSERT_EQ(s_vid, sess.getVid());
+
+  // 10) Check the remote files exist and have the correct size
+  for(auto & path: remoteFilePaths) {
+    struct stat statBuf;
+    bzero(&statBuf, sizeof(statBuf));
+    const int statRc = stat(path.substr(7).c_str(), &statBuf); //remove the "file://" for stat-ing
+    ASSERT_EQ(0, statRc);
+    ASSERT_EQ(1000, statBuf.st_size); //same size of data
+  }
+
+  // 10) Check logs
+  std::string logToCheck = logger.getLog();
+  logToCheck += "";
+  ASSERT_NE(std::string::npos, logToCheck.find("firmwareVersion=\"123A\" serialNumber=\"123456\" "
+                                               "mountTotalCorrectedReadErrors=\"5\" mountTotalReadBytesProcessed=\"4096\" "
+                                               "mountTotalUncorrectedReadErrors=\"1\" mountTotalNonMediumErrorCounts=\"2\""));
+  ASSERT_NE(std::string::npos, logToCheck.find("firmwareVersion=\"123A\" serialNumber=\"123456\" lifetimeMediumEfficiencyPrct=\"100\" "
+                                               "mountReadEfficiencyPrct=\"100\" mountWriteEfficiencyPrct=\"100\" "
+                                               "mountReadTransients=\"10\" "
+                                               "mountServoTemps=\"10\" mountServoTransients=\"5\" mountTemps=\"100\" "
+                                               "mountTotalReadRetries=\"25\" mountTotalWriteRetries=\"25\" mountWriteTransients=\"10\""));
+  
+  ASSERT_NE(std::string::npos, logToCheck.find("In RAOAlgorithmFactoryFactory::createAlgorithmFactory(), unable to determine the RAO algorithm to use"));
+  
+  ASSERT_EQ(expectedRAOOrder,getRAOFseqs(logToCheck));
 }
 
 TEST_P(DataTransferSessionTest, DataTransferSessionNoSuchDrive) {
diff --git a/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.cpp b/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.cpp
index 76b05d54ffeca3064b4ef6a7e0a26b67ef1c75ec..4da74d9ff00a90aa26a41885f6bb4ad31da45052 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.cpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.cpp
@@ -30,6 +30,7 @@
 #include "castor/tape/tapeserver/SCSI/Structures.hpp"
 #include "castor/tape/tapeserver/drive/DriveInterface.hpp"
 #include "scheduler/RetrieveJob.hpp"
+#include "castor/tape/tapeserver/drive/DriveGeneric.hpp"
 
 #include <stdint.h>
 
@@ -49,7 +50,6 @@ RecallTaskInjector::RecallTaskInjector(RecallMemoryManager & mm,
         m_thread(*this),m_memManager(mm),
         m_tapeReader(tapeReader),m_diskWriter(diskWriter),
         m_retrieveMount(retrieveMount),m_lc(lc),m_maxFiles(maxFiles),m_maxBytes(byteSizeThreshold),
-        m_useRAO(false),
         m_firstTasksInjectedFuture(m_firstTasksInjectedPromise.get_future()){}
 //------------------------------------------------------------------------------
 //destructor
@@ -93,9 +93,8 @@ void RecallTaskInjector::setDriveInterface(castor::tape::tapeserver::drive::Driv
 //------------------------------------------------------------------------------
 //initRAO
 //------------------------------------------------------------------------------
-void RecallTaskInjector::initRAO(const std::string & raoAlgorithm) {
-  m_useRAO = true;
-  m_raoAlgorithm = raoAlgorithm;
+void RecallTaskInjector::initRAO(const castor::tape::tapeserver::rao::RAOConfig & config) {
+  this->m_raoManager = castor::tape::tapeserver::rao::RAOManager(config,m_drive);
   m_raoFuture = m_raoPromise.get_future();
 }
 //------------------------------------------------------------------------------
@@ -141,63 +140,26 @@ void RecallTaskInjector::waitForFirstTasksInjectedPromise(){
 //injectBulkRecalls
 //------------------------------------------------------------------------------
 void RecallTaskInjector::injectBulkRecalls() {
-
-  uint32_t block_size = 262144;
   uint32_t njobs = m_jobs.size();
-  std::vector<uint32_t> raoOrder;
+  std::vector<uint64_t> raoOrder;
 
-  if (m_useRAO) {
+  bool useRAO = m_raoManager.useRAO();
+  if (useRAO) {
     std::list<castor::tape::SCSI::Structures::RAO::blockLims> files;
     m_lc.log(cta::log::INFO, "Performing RAO reordering");
-
-    for (uint32_t i = 0; i < njobs; i++) {
-      cta::RetrieveJob *job = m_jobs.at(i).get();
-
-      castor::tape::SCSI::Structures::RAO::blockLims lims;
-      strncpy((char*)lims.fseq, std::to_string(i).c_str(), sizeof(i));
-      lims.begin = job->selectedTapeFile().blockId;
-      lims.end = job->selectedTapeFile().blockId + 8 +
-                 /* ceiling the number of blocks */
-                 ((job->archiveFile.fileSize + block_size - 1) / block_size);
-
-      files.push_back(lims);
-
-      if (files.size() == m_raoLimits.maxSupported ||
-              ((i == njobs - 1) && (files.size() > 1))) {
-        /* We do a RAO query if:
-         *  1. the maximum number of files supported by the drive
-         *     for RAO query has been reached
-         *  2. the end of the jobs list has been reached and there are at least
-         *     2 unordered files
-         */
-        m_drive->queryRAO(files, m_raoLimits.maxSupported,m_raoAlgorithm);
-
-        /* Add the RAO sorted files to the new list*/
-        for (auto fit = files.begin(); fit != files.end(); fit++) {
-          uint64_t id = atoi((char*)fit->fseq);
-          raoOrder.push_back(id);
-        }
-        files.clear();
-      }
-    }
     
-    /* Copy the rest of the files in the new ordered list */
-    for (auto fit = files.begin(); fit != files.end(); fit++) {
-      uint64_t id = atoi((char*)fit->fseq);
-      raoOrder.push_back(id);
-    }
-    files.clear();
+    raoOrder = m_raoManager.queryRAO(m_jobs,m_lc);
   }
 
   std::ostringstream recallOrderLog;
-  if(m_useRAO) {
+  if(useRAO) {
     recallOrderLog << "Recall order of FSEQs using RAO:";
   } else {
     recallOrderLog << "Recall order of FSEQs:";
   }
 
   for (uint32_t i = 0; i < njobs; i++) {
-    uint32_t index = m_useRAO ? raoOrder.at(i) : i;
+    uint64_t index = useRAO ? raoOrder.at(i) : i;
 
     cta::RetrieveJob *job = m_jobs.at(index).release();
     recallOrderLog << " " << job->selectedTapeFile().fSeq;
@@ -237,12 +199,12 @@ void RecallTaskInjector::injectBulkRecalls() {
 bool RecallTaskInjector::synchronousFetch(bool & noFilesToRecall)
 {
   noFilesToRecall = false;
-  uint64_t reqFiles = (m_useRAO && m_hasUDS) ? m_raoLimits.maxSupported - m_fetched : m_maxFiles;
+  uint64_t reqFiles = (m_raoManager.useRAO() && m_raoManager.getMaxFilesSupported()) ? m_raoManager.getMaxFilesSupported().value() - m_fetched : m_maxFiles;
   /* If RAO is enabled, we must ask for files up to 1PB.
    * We are limiting to 1PB because the size will be passed as
    * oracle::occi::Number which is limited to ~56 bits precision
    */
-  uint64_t reqSize = (m_useRAO && m_hasUDS) ? 1024L * 1024 * 1024 * 1024 * 1024 : m_maxBytes;
+  uint64_t reqSize = (m_raoManager.useRAO() && m_raoManager.hasUDS()) ? 1024L * 1024 * 1024 * 1024 * 1024 : m_maxBytes;
   try {
     auto jobsList = m_retrieveMount.getNextJobBatch(reqFiles, reqSize, m_lc);
     for (auto & j: jobsList)
@@ -266,7 +228,7 @@ bool RecallTaskInjector::synchronousFetch(bool & noFilesToRecall)
     return false;
   }
   else {
-    if (! m_useRAO)
+    if (! m_raoManager.useRAO())
       injectBulkRecalls();
     else {
       cta::log::ScopedParamContainer scoped(m_lc);
@@ -302,34 +264,37 @@ void RecallTaskInjector::WorkerThread::run()
   using cta::log::LogContext;
   m_parent.m_lc.pushOrReplace(Param("thread", "RecallTaskInjector"));
   m_parent.m_lc.log(cta::log::DEBUG, "Starting RecallTaskInjector thread");
-  if (m_parent.m_useRAO) {
+  if (m_parent.m_raoManager.useRAO()) {
     /* RecallTaskInjector is waiting to have access to the drive in order
      * to perform the RAO query;
      * This waitForPromise() call means that the drive is mounted 
      */
     m_parent.waitForPromise();
     try {
-      m_parent.m_raoLimits = m_parent.m_drive->getLimitUDS();
-      m_parent.m_hasUDS = true;
-      LogContext::ScopedParam sp(m_parent.m_lc, Param("maxSupportedUDS", m_parent.m_raoLimits.maxSupported));
-      m_parent.m_lc.log(cta::log::INFO,"Query getLimitUDS for RAO completed");
-      if (m_parent.m_fetched < m_parent.m_raoLimits.maxSupported) {
-        /* Fetching until we reach maxSupported for the tape drive RAO */
-        bool noFilesToRecall;
-        m_parent.synchronousFetch(noFilesToRecall);
-      }
-      m_parent.injectBulkRecalls();
-      /**
-       * Commenting this line as we want the RAO to be executed on
-       * all the batchs and not only one.
-       */
-      //m_parent.m_useRAO = false;
+      m_parent.m_raoManager.setEnterpriseRAOUdsLimits(m_parent.m_drive->getLimitUDS());
+      LogContext::ScopedParam sp(m_parent.m_lc, Param("maxSupportedUDS", m_parent.m_raoManager.getMaxFilesSupported().value()));
+      m_parent.m_lc.log(cta::log::INFO,"Query getLimitUDS for RAO Enterprise completed");
+    } catch (castor::tape::SCSI::Exception& e) {
+      m_parent.m_raoManager.disableRAO();
+      cta::log::ScopedParamContainer spc(m_parent.m_lc);
+      spc.add("exceptionMessage",e.getMessageValue());
+      m_parent.m_lc.log(cta::log::ERR, "Error while fetching the limitUDS for RAO enterprise drive.");
+    } catch(const castor::tape::tapeserver::drive::DriveDoesNotSupportRAOException &ex){
+      m_parent.m_lc.log(cta::log::INFO, "The drive does not support RAO Enterprise, will run a CTA RAO.");
     }
-    catch (castor::tape::SCSI::Exception& e) {
-      m_parent.m_lc.log(cta::log::WARNING, "The drive does not support RAO: disabled");
-      m_parent.m_useRAO = false;
-      m_parent.injectBulkRecalls();
+    cta::optional<uint64_t> maxFilesSupportedByRAO = m_parent.m_raoManager.getMaxFilesSupported();
+    if (maxFilesSupportedByRAO && m_parent.m_fetched < maxFilesSupportedByRAO.value()) {
+      /* Fetching until we reach maxSupported for the tape drive RAO */
+      //unused boolean here but need to be kept to respect the synchronousFetch signature
+      bool noFilesToRecall;
+      m_parent.synchronousFetch(noFilesToRecall);
     }
+    m_parent.injectBulkRecalls();
+    /**
+     * Commenting this line as we want the RAO to be executed on
+     * all the batchs and not only one.
+     */
+    //m_parent.m_raoManager.disableRAO();
   }
   try{
     while (1) {
diff --git a/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.hpp b/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.hpp
index 0157c9abae51d7e4096830927a2ab1a2160014a3..ade4ae5561c795b53bce16a63de5a17d8408273c 100644
--- a/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.hpp
+++ b/tapeserver/castor/tape/tapeserver/daemon/RecallTaskInjector.hpp
@@ -30,6 +30,8 @@
 #include "scheduler/RetrieveJob.hpp"
 #include "scheduler/RetrieveMount.hpp"
 #include "castor/tape/tapeserver/drive/DriveInterface.hpp"
+#include "castor/tape/tapeserver/RAO/RAOConfig.hpp"
+#include "castor/tape/tapeserver/RAO/RAOManager.hpp"
 
 #include <future>
 
@@ -122,7 +124,7 @@ public:
   /**
    * Initialize Recommended Access Order parameters
    */
-  void initRAO(const std::string & raoAlgorithm);
+  void initRAO(const castor::tape::tapeserver::rao::RAOConfig & config);
 
   void waitForPromise();
 
@@ -229,23 +231,11 @@ private:
   
   //maximal number of cumulated byte requested. at once
   const uint64_t m_maxBytes;
-
-  /** Flag indicating if the file recalls are performed using
-   * the Recommended Access Order (RAO)
-   */
-  bool m_useRAO;
   
   /**
-   * The RAO algorithm to use, it is given by the tapeserver parameter
-   * RAOAlgorithm
+   * The RAO manager to perofrm RAO operations
    */
-  std::string m_raoAlgorithm;
-
-  /** Drive-specific RAO parameters */
-  SCSI::Structures::RAO::udsLimits m_raoLimits;
-
-  /** Indicator that UDS limits gave been obtained */
-  bool m_hasUDS = false;
+  castor::tape::tapeserver::rao::RAOManager m_raoManager;
 
   /** Number of jobs to be fetched before the tape is mounted.
    *  The desired number is m_raoLimits.maxSupported
diff --git a/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.cpp b/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.cpp
index 7f9c81bd116a0962df451f836b3aec16e7ffded0..23ba2a409aaaaa500f50ed7b545a4e40ea8e489f 100644
--- a/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.cpp
+++ b/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.cpp
@@ -156,7 +156,12 @@ std::vector<castor::tape::tapeserver::drive::endOfWrapPosition> drive::DriveLTO:
   return ret;
 }
 
-void drive::DriveLTO::queryRAO(std::list<SCSI::Structures::RAO::blockLims>& files, int maxSupported, const std::string & raoAlgorithm) {
+SCSI::Structures::RAO::udsLimits drive::DriveLTO::getLimitUDS() {
+  throw castor::tape::tapeserver::drive::DriveDoesNotSupportRAOException("Drive does not support RAO enterprise.");
+}
+
+
+void drive::DriveLTO::queryRAO(std::list<SCSI::Structures::RAO::blockLims>& files, int maxSupported) {
   //TODO : Create an interface RAOAlgorithm with a method called getRAO() taking the files list as input/output parameter
   //Implement this method in three different classes : LinearRAOAlgorithm, RandomRAOAlgorithm, CERNRAOAlgorithm (or another name)
   //Create a factory to return the correct implementation subclass regarding the raoAlgorithm string parameter.
@@ -253,6 +258,7 @@ drive::deviceInfo drive::DriveMHVTL::getDeviceInfo()  {
 }
 
 SCSI::Structures::RAO::udsLimits drive::DriveMHVTL::getLimitUDS(){
+  throw drive::DriveDoesNotSupportRAOException("MHVTL does not support getLimitUDS");
   SCSI::Structures::RAO::udsLimits lims;
   //For MHVTL and for tests, assume that
   //the max number of files for RAO supported by an MHVTL drive 
@@ -262,7 +268,7 @@ SCSI::Structures::RAO::udsLimits drive::DriveMHVTL::getLimitUDS(){
   return lims;
 }
 
-void drive::DriveMHVTL::queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported, const std::string & raoAlgorithm){
+void drive::DriveMHVTL::queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported){
   //The query RAO method of MHVTL drive returns nothing
   //something could be implemented for testing...
 }
@@ -972,7 +978,7 @@ void drive::DriveGeneric::receiveRAO(std::list<SCSI::Structures::RAO::blockLims>
 }
 
 void drive::DriveGeneric::queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files,
-                                   int maxSupported, const std::string & raoAlgorithm) {
+                                   int maxSupported) {
     generateRAO(files, maxSupported);
     receiveRAO(files);
 }
diff --git a/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.hpp b/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.hpp
index e6789636961bd2f983136e72c29f75899671adda..ef3c4a40d8805200d3affcaf6f14d42fcbea5389 100644
--- a/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.hpp
+++ b/tapeserver/castor/tape/tapeserver/drive/DriveGeneric.hpp
@@ -28,6 +28,8 @@ namespace castor {
 namespace tape {
 namespace tapeserver {
 namespace drive {
+  
+  CTA_GENERATE_EXCEPTION_CLASS(DriveDoesNotSupportRAOException);
 /**
    * Class abstracting the tape drives. This class is templated to allow the use
    * of unrelated test harness and real system. The test harness is made up of 
@@ -479,11 +481,9 @@ namespace drive {
      * @param filename The name of the file containing the sequential order of
      * a list of files [line format: ID:BLOCK_START:BLOCK_END]
      * @param maxSupported, the max number of files the drive is able to perform an RAO on
-     * @param raoAlgorithm, the name of the RAO algorithm to use, if the specified name
-     * does not match any raoAlgorithm, the linear algorithm will be used.
      */
-    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported, const std::string & raoAlgorithm);
-
+    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported);
+    
   protected:
     SCSI::DeviceInfo m_SCSIInfo;
     int m_tapeFD; 
@@ -583,7 +583,7 @@ namespace drive {
     virtual std::map<std::string,uint32_t> getVolumeStats();
     virtual drive::deviceInfo getDeviceInfo();
     virtual SCSI::Structures::RAO::udsLimits getLimitUDS();
-    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported, const std::string & raoAlgorithm);
+    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported);
   };
 
   class DriveLTO : public DriveGeneric {
@@ -595,8 +595,8 @@ namespace drive {
     virtual compressionStats getCompression();
     virtual void clearCompressionStats();
     virtual std::vector<castor::tape::tapeserver::drive::endOfWrapPosition> getEndOfWrapPositions();
-    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims>& files, int maxSupported, const std::string& raoAlgorithm);
-
+    virtual SCSI::Structures::RAO::udsLimits getLimitUDS();
+    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims>& files, int maxSupported);
   };
 
   class DriveIBM3592 : public DriveGeneric {
diff --git a/tapeserver/castor/tape/tapeserver/drive/DriveInterface.hpp b/tapeserver/castor/tape/tapeserver/drive/DriveInterface.hpp
index d231d106314d237b076864b27658acd112705169..a4fb31f7cd1f7ae3102f61a06f655c71af41539f 100644
--- a/tapeserver/castor/tape/tapeserver/drive/DriveInterface.hpp
+++ b/tapeserver/castor/tape/tapeserver/drive/DriveInterface.hpp
@@ -273,8 +273,7 @@ namespace drive {
     virtual bool hasTapeInPlace() = 0;
     
     virtual SCSI::Structures::RAO::udsLimits getLimitUDS() = 0;
-    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported, const std::string & raoAlgorithm) = 0;
-
+    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported) = 0;
     /**
      * The configuration of the tape drive as parsed from the TPCONFIG file.
      */
diff --git a/tapeserver/castor/tape/tapeserver/drive/FakeDrive.cpp b/tapeserver/castor/tape/tapeserver/drive/FakeDrive.cpp
index 58e244ace5e7acec060a9e4c1c81329cf1cd3a85..306fa5ea0372adf8809add96640260a0bdc305b4 100644
--- a/tapeserver/castor/tape/tapeserver/drive/FakeDrive.cpp
+++ b/tapeserver/castor/tape/tapeserver/drive/FakeDrive.cpp
@@ -23,6 +23,7 @@
 
 #include "castor/tape/tapeserver/drive/FakeDrive.hpp"
 #include "castor/tape/tapeserver/SCSI/Structures.hpp"
+#include "DriveGeneric.hpp"
 #include <iostream>
 
 namespace {
@@ -325,7 +326,7 @@ castor::tape::SCSI::Structures::RAO::udsLimits
 }
 
 void castor::tape::tapeserver::drive::FakeDrive::queryRAO(
-            std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported, const std::string & queryRAO)  {
+            std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported)  {
   files.reverse();
 }
 
@@ -387,3 +388,14 @@ std::map<std::string,uint32_t> castor::tape::tapeserver::drive::FakeDrive::getVo
   return std::map<std::string,uint32_t>();
 }
 
+
+castor::tape::tapeserver::drive::FakeNonRAODrive::FakeNonRAODrive():FakeDrive() {
+  
+}
+
+castor::tape::SCSI::Structures::RAO::udsLimits castor::tape::tapeserver::drive::FakeNonRAODrive::getLimitUDS() {
+  throw castor::tape::tapeserver::drive::DriveDoesNotSupportRAOException("UnitTests");
+}
+
+
+
diff --git a/tapeserver/castor/tape/tapeserver/drive/FakeDrive.hpp b/tapeserver/castor/tape/tapeserver/drive/FakeDrive.hpp
index 89ea6ca20c121db841c3e79d6636404702b3e0b2..586c46dddf659c4f5aacb7cc6d97caee19a79dae 100644
--- a/tapeserver/castor/tape/tapeserver/drive/FakeDrive.hpp
+++ b/tapeserver/castor/tape/tapeserver/drive/FakeDrive.hpp
@@ -112,7 +112,13 @@ namespace drive {
     virtual lbpToUse getLbpToUse();
     virtual bool hasTapeInPlace();
     virtual castor::tape::SCSI::Structures::RAO::udsLimits getLimitUDS();
-    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported,const std::string & raoAlgorithm);
+    virtual void queryRAO(std::list<SCSI::Structures::RAO::blockLims> &files, int maxSupported);
+  };
+  
+  class FakeNonRAODrive : public FakeDrive{
+  public:
+    FakeNonRAODrive();
+    virtual castor::tape::SCSI::Structures::RAO::udsLimits getLimitUDS(); 
   };
   
 }}}}
diff --git a/tapeserver/castor/tape/tapeserver/file/BasicReadWriteTest.cpp b/tapeserver/castor/tape/tapeserver/file/BasicReadWriteTest.cpp
index 83c310967a669980e1eddcfdd7958656b34b5e0b..12b63097edabc5889509ae00c5fa282858d2a446 100644
--- a/tapeserver/castor/tape/tapeserver/file/BasicReadWriteTest.cpp
+++ b/tapeserver/castor/tape/tapeserver/file/BasicReadWriteTest.cpp
@@ -260,7 +260,7 @@ int main (int argc, char *argv[])
                       throw -1;
                     }
                     castor::tape::SCSI::Structures::RAO::udsLimits limits = drive->getLimitUDS();
-                    drive->queryRAO(files, limits.maxSupported,"");
+                    drive->queryRAO(files, limits.maxSupported);
                 }
             }
 
diff --git a/tapeserver/daemon/DriveHandler.cpp b/tapeserver/daemon/DriveHandler.cpp
index 5e6cbd8b451628861de6fd797c053acf5b0e9495..f40cb3de17c0b5ea881fed7420e6ec16c7471315 100644
--- a/tapeserver/daemon/DriveHandler.cpp
+++ b/tapeserver/daemon/DriveHandler.cpp
@@ -1076,7 +1076,8 @@ int DriveHandler::runChild() {
     dataTransferConfig.nbDiskThreads = m_tapedConfig.nbDiskThreads.value();
     dataTransferConfig.useLbp = true;
     dataTransferConfig.useRAO = m_tapedConfig.useRAO.value() == "yes" ? true : false;
-    dataTransferConfig.raoAlgorithm = m_tapedConfig.raoAlgorithm.value();
+    dataTransferConfig.raoLtoAlgorithm = m_tapedConfig.raoLtoAlgorithm.value();
+    dataTransferConfig.raoLtoAlgorithmOptions = m_tapedConfig.raoLtoOptions.value();
     dataTransferConfig.xrootPrivateKey = "";
     
     // Before launching, and if this is the first session since daemon start, we will
@@ -1143,7 +1144,7 @@ int DriveHandler::runChild() {
       capUtils,
       dataTransferConfig,
       scheduler);
-    
+
     auto ret = dataTransferSession.execute();
     agentHeartbeat.stopAndWaitThread();
     return ret;
diff --git a/tapeserver/daemon/TapedConfiguration.cpp b/tapeserver/daemon/TapedConfiguration.cpp
index 1eaf5f9cf3348023a3db3a95a494168465b98286..c23207ce107530d07c98d4caebfd77ace9a1848c 100644
--- a/tapeserver/daemon/TapedConfiguration.cpp
+++ b/tapeserver/daemon/TapedConfiguration.cpp
@@ -109,7 +109,8 @@ TapedConfiguration TapedConfiguration::createFromCtaConf(
   ret.nbDiskThreads.setFromConfigurationFile(cf, generalConfigPath);
   //RAO
   ret.useRAO.setFromConfigurationFile(cf, generalConfigPath);
-  ret.raoAlgorithm.setFromConfigurationFile(cf,generalConfigPath);
+  ret.raoLtoAlgorithm.setFromConfigurationFile(cf,generalConfigPath);
+  ret.raoLtoOptions.setFromConfigurationFile(cf,generalConfigPath);
   // Watchdog: parameters for timeouts in various situations.
   ret.wdIdleSessionTimer.setFromConfigurationFile(cf, generalConfigPath);
   ret.wdMountMaxSecs.setFromConfigurationFile(cf, generalConfigPath);
diff --git a/tapeserver/daemon/TapedConfiguration.hpp b/tapeserver/daemon/TapedConfiguration.hpp
index 13ab316063d3f09c8bf96fb69f268af68f106637..225a1a6015721c24ab76ac1385ab0ff471783ae1 100644
--- a/tapeserver/daemon/TapedConfiguration.hpp
+++ b/tapeserver/daemon/TapedConfiguration.hpp
@@ -97,8 +97,11 @@ struct TapedConfiguration {
   cta::SourcedParameter<std::string> useRAO{
     "taped", "UseRAO", "no", "Compile time default"};
   /// RAO type of algorithm
-  cta::SourcedParameter<std::string> raoAlgorithm{
-    "taped", "RAOAlgorithm","linear","Compile time default"};
+  cta::SourcedParameter<std::string> raoLtoAlgorithm{
+    "taped", "RAOLTOAlgorithm","linear","Compile time default"};
+  cta::SourcedParameter<std::string> raoLtoOptions {
+    "taped", "RAOLTOAlgorithmOptions","","Compile time default"
+  };
   //----------------------------------------------------------------------------
   // Watchdog: parameters for timeouts in various situations.
   //----------------------------------------------------------------------------