diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h
index 3b97b246a9b1431de56ddf4b3483b6befad1d1e8..e534fabc63361dac54accd9ea8337fedb5325326 100644
--- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h
+++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperApplication.h
@@ -124,9 +124,9 @@ public:
   ParameterGroup* GetParameterList();
 
   /* Get the internal application parameter specified
-   *
+   * if the follow flag is on, the function returns the target of proxy parameters
    * WARNING: this method may disappear from the API */
-  Parameter* GetParameterByKey(std::string parameter);
+  Parameter* GetParameterByKey(std::string parameter, bool follow=true);
 
   /* Get the internal application parameter specified
    *
diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperCompositeApplication.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperCompositeApplication.h
new file mode 100644
index 0000000000000000000000000000000000000000..5af08d98241f6795147832f9f566642ee325d448
--- /dev/null
+++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperCompositeApplication.h
@@ -0,0 +1,151 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the above copyright notices for more information.
+
+  =========================================================================*/
+#ifndef otbWrapperCompositeApplication_h
+#define otbWrapperCompositeApplication_h
+
+#include "otbWrapperApplication.h"
+#include "itkStdStreamLogOutput.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+
+/** \class CompositeApplication
+ *  \brief This class is a base class for composite applications
+ *
+ * This class allows to create & store internal applications with the same logic
+ * as parameters. You choose the application type to create, you choose an
+ * identifier (alphanumeric string), and you can give a short description.
+ * Later, you will refer to this application using the identifier. In the
+ * functions of this class, you can refer to parameters from internal
+ * applications by using their identifier as prefix. For instance, "app1.in"
+ * will refer to parameter "in" from internal application named "app1"
+ * (if such application exists, if not it will refer to a parameter of this
+ * composite application).
+ *
+ * \ingroup OTBApplicationEngine
+ */
+class OTBApplicationEngine_EXPORT CompositeApplication: public Application
+{
+public:
+  /** Standard class typedefs. */
+  typedef CompositeApplication          Self;
+  typedef Application                   Superclass;
+  typedef itk::SmartPointer<Self>       Pointer;
+  typedef itk::SmartPointer<const Self> ConstPointer;
+
+  /** RTTI support */
+  itkTypeMacro(CompositeApplication, Application);
+
+  /** Filters typedef */
+  typedef itk::MemberCommand< Self >        AddProcessCommandType;
+
+  typedef struct
+    {
+    Application::Pointer App;
+    std::string Desc;
+    } InternalApplication;
+
+  typedef std::map<std::string, InternalApplication> InternalAppContainer;
+
+protected:
+  /** Constructor */
+  CompositeApplication();
+
+  /** Destructor */
+  ~CompositeApplication() ITK_OVERRIDE;
+
+  /**
+   * Callback function to retrieve the process watchers on internal filters
+   */
+  void LinkWatchers(itk::Object * itkNotUsed(caller), const itk::EventObject & event);
+
+  /**
+   * Method to instanciate and register a new internal application
+   * \param appType Type of the application to instanciate
+   * \param key Identifier associated to the created application
+   * \param desc Description of the internal application
+   */
+  bool AddApplication(std::string appType, std::string key, std::string desc);
+
+  /**
+   * Connect two existing parameters together. The first parameter will point to
+   * the second parameter.
+   */
+  bool Connect(std::string fromKey, std::string toKey);
+
+  /**
+   * Share a parameter between the composite application and an internal application
+   * The local parameter is created as a proxy to the internal parameter.
+   * \param localKey New parameter key in the composite application
+   * \param internalKey Key to the internal parameter to expose
+   * \param name Name for the local parameter, if empty the target's name is used
+   * \param desc Description for the local parameter, if empty the target's description is used
+   */
+  bool ShareParameter(std::string localKey,
+                      std::string internalKey,
+                      std::string name = std::string(),
+                      std::string desc = std::string());
+
+  /**
+   * Decode a key to extract potential prefix for internal applications
+   * If a valid prefix (corresponding to an internal app) is found:
+   *   - prefix is removed from the input key which is altered.
+   *   - the function returns a pointer to the internal application
+   * If no valid prefix is found, the input key is not modified, and the
+   * function returns 'this'.
+   */
+  Application* DecodeKey(std::string &key);
+
+  /**
+   * Get the internal application with the given identifier
+   */
+  Application* GetInternalApplication(std::string id);
+
+  /**
+   * Get the description of an internal application
+   */
+  std::string GetInternalAppDescription(std::string id);
+
+  /**
+   * Utility function to call Execute() on an internal app and get its output logs
+   */
+  void ExecuteInternal(std::string key);
+
+  /**
+   * Utility function to call UpdateParameters() on an internal app
+   */
+  void UpdateInternalParameters(std::string key);
+
+private:
+  CompositeApplication(const CompositeApplication &); //purposely not implemented
+  void operator =(const CompositeApplication&); //purposely not implemented
+
+  InternalAppContainer m_AppContainer;
+
+  itk::StdStreamLogOutput::Pointer  m_LogOutput;
+
+  std::ostringstream m_Oss;
+
+  AddProcessCommandType::Pointer    m_AddProcessCommand;
+};
+
+}
+}
+#endif
diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperParameterGroup.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperParameterGroup.h
index fb395b439c68f70701676b16665fde8443032dd1..5aea541604912424fcec62ed5319ed33976d0162 100644
--- a/Modules/Wrappers/ApplicationEngine/include/otbWrapperParameterGroup.h
+++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperParameterGroup.h
@@ -47,6 +47,10 @@ public:
 
   void AddParameter(Parameter::Pointer p);
 
+  /** Method to substitute a parameter in a group. 
+   *  The function returns true on success, false on failure */
+  bool ReplaceParameter(std::string &key, Parameter::Pointer p);
+
   /** Add a new choice value to an existing choice parameter */
   void AddChoice(std::string paramKey, std::string paramName);
 
@@ -61,9 +65,9 @@ public:
    * or the path to a choice value */
   void AddParameter(ParameterType type, std::string paramKey, std::string paramName);
 
-  Parameter::Pointer GetParameterByIndex(unsigned int i);
+  Parameter::Pointer GetParameterByIndex(unsigned int i, bool follow=true);
 
-  Parameter::Pointer GetParameterByKey(std::string name);
+  Parameter::Pointer GetParameterByKey(std::string name, bool follow=true);
 
   /** rashad: Add xml parameters eg: -inxml -outxml */
   void AddInXMLParameter();
@@ -93,6 +97,10 @@ public:
     return true;
   }
 
+  /** Resolve potential proxy parameters by following their targets until
+   *  a non-proxy parameter. It will detect cycles and report an error */
+  static Parameter* ResolveParameter(Parameter *param);
+
 protected:
   ParameterGroup();
   ~ParameterGroup() ITK_OVERRIDE;
diff --git a/Modules/Wrappers/ApplicationEngine/include/otbWrapperProxyParameter.h b/Modules/Wrappers/ApplicationEngine/include/otbWrapperProxyParameter.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea16db09d0e8ab7c7587cecdeca187a27870a697
--- /dev/null
+++ b/Modules/Wrappers/ApplicationEngine/include/otbWrapperProxyParameter.h
@@ -0,0 +1,71 @@
+#ifndef otbWrapperProxyParameter_h
+#define otbWrapperProxyParameter_h
+
+#include "itkObject.h"
+#include "otbWrapperParameter.h"
+#include <utility>
+
+namespace otb
+{
+namespace Wrapper
+{
+/**
+ * \class ProxyParameter
+ *
+ * \brief Parameter class acting as a proxy to a different parameter
+ *
+ * The target parameter of this proxy is defined as a pair of a group parameter
+ * containing the target and the targets key. It allows to define proxies on
+ * parameters that may be themselves replaced by a proxy
+ *
+ * \ingroup OTBApplicationEngine
+ */
+class ProxyParameter : public Parameter
+{
+public:
+  typedef ProxyParameter                       Self;
+  typedef Parameter                            Superclass;
+  typedef itk::SmartPointer<Self>              Pointer;
+  typedef itk::SmartPointer<const Self>        ConstPointer;
+
+  itkNewMacro(Self);
+
+  itkTypeMacro(ProxyParameter, Parameter);
+
+  typedef std::pair<Parameter::Pointer, std::string> ProxyTargetType;
+
+  /** Set the target parameter of the proxy
+   *  \param target pair of a group parameter containing the target and its key
+   */
+  void SetTarget(const ProxyTargetType& target)
+    {
+    m_Target = target;
+    }
+
+  /** Get the target parameter of the proxy
+   *  First part is the group parameter containing the target
+   *  Second part is the key of the target */
+  const ProxyTargetType & GetTarget(void)
+    {
+    return m_Target;
+    }
+
+protected:
+  ProxyParameter() {}
+  ~ProxyParameter() ITK_OVERRIDE {}
+
+private:
+  ProxyParameter(const Self &); //purposely not implemented
+  void operator =(const Self&); //purposely not implemented
+
+  ProxyTargetType m_Target;
+};
+
+}
+}
+
+//#ifndef OTB_MANUAL_INSTANTIATION
+//#include "otbWrapperProxyParameter.txx"
+//#endif
+
+#endif
diff --git a/Modules/Wrappers/ApplicationEngine/src/CMakeLists.txt b/Modules/Wrappers/ApplicationEngine/src/CMakeLists.txt
index 6d11c565ee8af5638682141dc0a8f911369203b5..c33fdbcdc04cf938d179a35c29271ffb39ce9e1b 100644
--- a/Modules/Wrappers/ApplicationEngine/src/CMakeLists.txt
+++ b/Modules/Wrappers/ApplicationEngine/src/CMakeLists.txt
@@ -27,6 +27,7 @@ set(OTBApplicationEngine_SRC
   otbWrapperChoiceParameter.cxx
   otbWrapperApplicationRegistry.cxx
   otbWrapperApplicationFactoryBase.cxx
+  otbWrapperCompositeApplication.cxx
   )
 
 add_library(OTBApplicationEngine ${OTBApplicationEngine_SRC})
diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx
index 0ab8e27f8ca12f727a1b18f11262e90c7d5afc9b..f24308291ed7a3f1804baf8a070fe143dcb0f202 100644
--- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx
+++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperApplication.cxx
@@ -32,6 +32,7 @@
 #include "otbWrapperInputImageListParameter.h"
 #include "otbWrapperInputProcessXMLParameter.h"
 #include "otbWrapperRAMParameter.h"
+#include "otbWrapperProxyParameter.h"
 
 
 #include "otbWrapperAddProcessToWatchEvent.h"
@@ -97,9 +98,9 @@ ParameterGroup* Application::GetParameterList()
   return m_ParameterList;
 }
 
-Parameter* Application::GetParameterByKey(std::string name)
+Parameter* Application::GetParameterByKey(std::string name, bool follow)
 {
-  return GetParameterList()->GetParameterByKey(name);
+  return GetParameterList()->GetParameterByKey(name, follow);
 }
 
 void Application::SetParameterInt(std::string parameter, int value, bool hasUserValueFlag)
@@ -927,19 +928,22 @@ void Application::SetParameterOutputVectorData(std::string parameter, VectorData
 
 std::string Application::GetParameterName(std::string parameter)
 {
-  Parameter* param = GetParameterByKey(parameter);
+  // get the actual parameter, even if it is a proxy
+  Parameter* param = GetParameterByKey(parameter,false);
   return param->GetName();
 }
 
 std::string Application::GetParameterDescription(std::string parameter)
 {
-  Parameter* param = GetParameterByKey(parameter);
+  // get the actual parameter, even if it is a proxy
+  Parameter* param = GetParameterByKey(parameter,false);
   return param->GetDescription();
 }
 
 void Application::SetParameterDescription(std::string parameter, std::string desc)
 {
-  Parameter* param = GetParameterByKey(parameter);
+  // get the actual parameter, even if it is a proxy
+  Parameter* param = GetParameterByKey(parameter,false);
   param->SetDescription(desc);
 }
 
@@ -1623,6 +1627,5 @@ std::string Application::GetProgressDescription() const
   return m_ProgressSourceDescription;
 }
 
-
 }
 }
diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperCompositeApplication.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperCompositeApplication.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a3a264a02dcbbe290f2c59b6c8981b2fcf0c06c8
--- /dev/null
+++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperCompositeApplication.cxx
@@ -0,0 +1,185 @@
+/*=========================================================================
+
+  Program:   ORFEO Toolbox
+  Language:  C++
+  Date:      $Date$
+  Version:   $Revision$
+
+
+  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
+  See OTBCopyright.txt for details.
+
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the above copyright notices for more information.
+
+  =========================================================================*/
+
+#include "otbWrapperCompositeApplication.h"
+#include "otbWrapperProxyParameter.h"
+#include "otbWrapperApplicationRegistry.h"
+#include "otbWrapperAddProcessToWatchEvent.h"
+#include "otbWrapperParameterKey.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+
+CompositeApplication::CompositeApplication()
+{
+  m_LogOutput = itk::StdStreamLogOutput::New();
+  m_LogOutput->SetStream(m_Oss);
+  m_AddProcessCommand = AddProcessCommandType::New();
+  m_AddProcessCommand->SetCallbackFunction(this, &CompositeApplication::LinkWatchers);
+}
+
+CompositeApplication::~CompositeApplication()
+{
+}
+
+void
+CompositeApplication
+::LinkWatchers(itk::Object * itkNotUsed(caller), const itk::EventObject & event)
+{
+  if (typeid(AddProcessToWatchEvent) == typeid( event ))
+    {
+    this->InvokeEvent(event);
+    }
+}
+
+bool
+CompositeApplication
+::AddApplication(std::string appType, std::string key, std::string desc)
+{
+  if (m_AppContainer.count(key))
+    {
+    otbAppLogWARNING("The requested identifier for internal application is already used ("<<key<<")");
+    return false;
+    }
+  InternalApplication container;
+  container.App = ApplicationRegistry::CreateApplication(appType);
+  container.Desc = desc;
+  // Setup logger
+  container.App->GetLogger()->AddLogOutput(m_LogOutput);
+  container.App->GetLogger()->SetTimeStampFormat(itk::LoggerBase::HUMANREADABLE);
+  container.App->AddObserver(AddProcessToWatchEvent(), m_AddProcessCommand.GetPointer());
+  m_AppContainer[key] = container;
+  return true;
+}
+
+bool
+CompositeApplication
+::Connect(std::string fromKey, std::string toKey)
+{
+  std::string key1(fromKey);
+  std::string key2(toKey);
+  Application *app1 = DecodeKey(key1);
+  Application *app2 = DecodeKey(key2);
+
+  Parameter* rawParam1 = app1->GetParameterByKey(key1, false);
+  if (dynamic_cast<ProxyParameter*>(rawParam1))
+    {
+    otbAppLogWARNING("Parameter is already connected ! Override current connection");
+    }
+  ProxyParameter::Pointer proxyParam = ProxyParameter::New();
+  ProxyParameter::ProxyTargetType target;
+  target.first = app2->GetParameterList();
+  target.second = key2;
+  proxyParam->SetTarget(target);
+  proxyParam->SetName(rawParam1->GetName());
+  proxyParam->SetDescription(rawParam1->GetDescription());
+  return app1->GetParameterList()->ReplaceParameter(key1, proxyParam.GetPointer());
+}
+
+bool
+CompositeApplication
+::ShareParameter(std::string localKey,
+                 std::string internalKey,
+                 std::string name,
+                 std::string desc)
+{
+  std::string internalKeyCheck(internalKey);
+  Application *app = DecodeKey(internalKeyCheck);
+  Parameter* rawTarget = app->GetParameterByKey(internalKeyCheck, false);
+
+  // Check source
+  ParameterKey pKey(localKey);
+  std::string proxyKey(pKey.GetLastElement());
+
+  // Create and setup proxy parameter
+  ProxyParameter::Pointer proxyParam = ProxyParameter::New();
+  ProxyParameter::ProxyTargetType target;
+  target.first = app->GetParameterList();
+  target.second = internalKeyCheck;
+  proxyParam->SetTarget(target);
+  proxyParam->SetName( name.empty() ? rawTarget->GetName() : name);
+  proxyParam->SetDescription(desc.empty() ? rawTarget->GetDescription() : desc);
+  proxyParam->SetKey(proxyKey);
+
+  // Get group parameter where the proxy should be added
+  Parameter::Pointer baseParam(proxyParam.GetPointer());
+  ParameterGroup *parentGroup = this->GetParameterList();
+  if (localKey.find('.') != std::string::npos)
+    {
+    Parameter::Pointer parentParam = this->GetParameterList()->GetParameterByKey(pKey.GetRoot());
+    parentGroup = dynamic_cast<ParameterGroup*>(parentParam.GetPointer());
+    baseParam->SetRoot(parentGroup);
+    parentGroup->AddChild(baseParam);
+    }
+  parentGroup->AddParameter(baseParam);
+  return true;
+}
+
+Application*
+CompositeApplication
+::DecodeKey(std::string &key)
+{
+  Application *ret = this;
+  size_t pos = key.find('.');
+  if (pos != std::string::npos && m_AppContainer.count(key.substr(0,pos)))
+    {
+    ret = m_AppContainer[key.substr(0,pos)].App;
+    key = key.substr(pos+1);
+    }
+  return ret;
+}
+
+Application*
+CompositeApplication
+::GetInternalApplication(std::string id)
+{
+  if (!m_AppContainer.count(id))
+    otbAppLogFATAL("Unknown internal application : "<<id);
+  return m_AppContainer[id].App;
+}
+
+std::string
+CompositeApplication
+::GetInternalAppDescription(std::string id)
+{
+  if (!m_AppContainer.count(id))
+    otbAppLogFATAL("Unknown internal application : "<<id);
+  return m_AppContainer[id].Desc;
+}
+
+void
+CompositeApplication
+::ExecuteInternal(std::string key)
+{
+  otbAppLogINFO(<< GetInternalAppDescription(key) <<"...");
+  GetInternalApplication(key)->Execute();
+  otbAppLogINFO(<< "\n" << m_Oss.str());
+  m_Oss.str(std::string(""));
+}
+
+void
+CompositeApplication
+::UpdateInternalParameters(std::string key)
+{
+  GetInternalApplication(key)->UpdateParameters();
+}
+
+} // end namespace Wrapper
+} // end namespace otb
diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperParameterGroup.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperParameterGroup.cxx
index a242cf60916566ff7ae9ca93e293d48716485637..199bb65f494267765a17f37ae064175d42e69c44 100644
--- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperParameterGroup.cxx
+++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperParameterGroup.cxx
@@ -36,6 +36,7 @@
 #include "otbWrapperInputProcessXMLParameter.h"
 #include "otbWrapperParameterKey.h"
 #include "otbWrapperRAMParameter.h"
+#include "otbWrapperProxyParameter.h"
 
 #include "otb_boost_string_header.h"
 
@@ -61,7 +62,11 @@ ParameterGroup::GetParametersKeys(bool recursive)
   for (pit = m_ParameterList.begin(); pit != m_ParameterList.end(); ++pit)
     {
     Parameter* param = *pit;
-    parameters.push_back( param->GetKey() );
+    std::string currentKey(param->GetKey());
+    parameters.push_back( currentKey );
+
+    // follow proxy parameters
+    param = this->ResolveParameter(param);
 
     if (recursive && dynamic_cast<ParameterGroup*>(param))
       {
@@ -70,7 +75,7 @@ ParameterGroup::GetParametersKeys(bool recursive)
       for (std::vector<std::string>::const_iterator it = subparams.begin();
            it != subparams.end(); ++it)
         {
-        parameters.push_back( std::string(paramAsGroup->GetKey()) + "."  + *it );
+        parameters.push_back( currentKey + "."  + *it );
         }
       }
     else if (recursive && dynamic_cast<ChoiceParameter*>(param))
@@ -81,7 +86,7 @@ ParameterGroup::GetParametersKeys(bool recursive)
       for (std::vector<std::string>::const_iterator it = subparams.begin();
            it != subparams.end(); ++it)
         {
-        parameters.push_back( std::string(paramAsChoice->GetKey()) + "."  + *it );
+        parameters.push_back( currentKey + "."  + *it );
         }
       }
     }
@@ -670,14 +675,79 @@ ParameterGroup::AddParameter(Parameter::Pointer p)
   m_ParameterList.push_back(p);
 }
 
+bool
+ParameterGroup::ReplaceParameter(std::string &key, Parameter::Pointer p)
+{
+  bool ret = true;
+  ParameterKey pName(key);
+  std::vector<std::string> splitName = pName.Split();
+  std::string lastkey = pName.GetLastElement();
+  std::string parentkey = pName.GetRoot();
+  ParameterGroup* parentGroup = this;
+  if( splitName.size() > 1 )
+    {
+    Parameter* parentParam = GetParameterByKey(parentkey);
+    parentGroup = dynamic_cast<ParameterGroup*>(parentParam);
+    if (parentGroup)
+      {
+      ret = parentGroup->ReplaceParameter(lastkey, p);
+      }
+    else
+      {
+      ret = false;
+      }
+    }
+  else
+    {
+    // find current parameter in the current group
+    Parameter::Pointer oldParam;
+    ParameterListType::iterator vit;
+    for (vit = m_ParameterList.begin(); vit != m_ParameterList.end(); ++vit)
+      {
+      if (lastkey.compare((*vit)->GetKey()) == 0)
+        {
+        oldParam = *vit;
+        break;
+        }
+      }
+    if (oldParam.IsNull())
+      {
+      // parameter to replace not found : return false
+      ret = false;
+      }
+    else
+      {
+      // parameter already exists : replace it
+      *vit = p;
+      p->SetKey(lastkey);
+      }
+    }
+  if (ret)
+    {
+    if( splitName.size() > 1 )
+      {
+      p->SetRoot(parentGroup);
+      parentGroup->AddChild(p);
+      }
+    }
+  // don't check type compatibility here, we may want to handle special cases
+  // at higher level
+  return ret;
+}
+
 Parameter::Pointer
-ParameterGroup::GetParameterByIndex(unsigned int i)
+ParameterGroup::GetParameterByIndex(unsigned int i, bool follow)
 {
-  return m_ParameterList[i];
+  Parameter *param = m_ParameterList[i];
+  if (follow)
+    {
+    param = this->ResolveParameter(param);
+    }
+  return Parameter::Pointer(param);
 }
 
 Parameter::Pointer
-ParameterGroup::GetParameterByKey(std::string name)
+ParameterGroup::GetParameterByKey(std::string name, bool follow)
 {
   ParameterKey pName(name);
 
@@ -705,6 +775,13 @@ ParameterGroup::GetParameterByKey(std::string name)
     itkExceptionMacro(<< "Could not find parameter " << name)
     }
 
+  // follow proxy parameters
+  if (follow)
+    {
+    Parameter *rawParam = this->ResolveParameter(parentParam.GetPointer());
+    parentParam = rawParam;
+    }
+
   // If the name contains a child, make a recursive call
   if (splitName.size() > 1)
     {
@@ -778,5 +855,33 @@ ParameterGroup::GetNumberOfParameters()
   return m_ParameterList.size();
 }
 
+Parameter* ParameterGroup::ResolveParameter(Parameter *param)
+{
+  Parameter* ret = param;
+  if (ret == ITK_NULLPTR)
+    {
+    itkGenericExceptionMacro("Can't resolve NULL parameter!");
+    }
+  while (dynamic_cast<ProxyParameter*>(ret))
+    {
+    ProxyParameter* castParam = dynamic_cast<ProxyParameter*>(ret);
+    ProxyParameter::ProxyTargetType target = castParam->GetTarget();
+    ParameterGroup* targetGroup = dynamic_cast<ParameterGroup*>(target.first.GetPointer());
+    if (targetGroup)
+      {
+      ret = targetGroup->GetParameterByKey(target.second);
+      }
+    else
+      {
+      itkGenericExceptionMacro("Target group of a proxy parameter is not of type ParameterGroup");
+      }
+    if (ret == param)
+      {
+      itkGenericExceptionMacro("Cycle detected with proxy parameters!");
+      }
+    }
+  return ret;
+}
+
 }
 }
diff --git a/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt b/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt
index 753d0016ad7cb2d0075de2a9055961ad5f97378b..7b6dee37b275ed7ec8a2c99e6842e13ec5814002 100644
--- a/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt
+++ b/Modules/Wrappers/ApplicationEngine/test/CMakeLists.txt
@@ -177,3 +177,7 @@ otb_add_test(NAME owTvApplicationMemoryConnectTest COMMAND otbApplicationEngineT
   $<TARGET_FILE_DIR:otbapp_Smoothing>
   ${INPUTDATA}/poupees.tif
   ${TEMP}/owTvApplicationMemoryConnectTestOutput.tif)
+
+otb_add_test(NAME owTvParameterGroup COMMAND otbApplicationEngineTestDriver
+  otbWrapperParameterList
+  )
diff --git a/Modules/Wrappers/ApplicationEngine/test/otbApplicationEngineTestDriver.cxx b/Modules/Wrappers/ApplicationEngine/test/otbApplicationEngineTestDriver.cxx
index 3cb4aaddcdfb84ccd62bf6e44b4fe5171bcf1619..2d6145cedbb23434b53fb12a212e3b1e85f28920 100644
--- a/Modules/Wrappers/ApplicationEngine/test/otbApplicationEngineTestDriver.cxx
+++ b/Modules/Wrappers/ApplicationEngine/test/otbApplicationEngineTestDriver.cxx
@@ -20,6 +20,7 @@ void RegisterTests()
   REGISTER_TEST(otbWrapperDocExampleStructureTest);
   REGISTER_TEST(otbWrapperParameterKey);
   REGISTER_TEST(otbWrapperParameterListNew);
+  REGISTER_TEST(otbWrapperParameterList);
   REGISTER_TEST(otbWrapperEmptyParameterNew);
   REGISTER_TEST(otbWrapperInputImageListParameterNew);
   REGISTER_TEST(otbWrapperInputImageListParameterTest1);
diff --git a/Modules/Wrappers/ApplicationEngine/test/otbWrapperParameterListTest.cxx b/Modules/Wrappers/ApplicationEngine/test/otbWrapperParameterListTest.cxx
index 5d14917b40d3389d20bf30ef226231f1b7664eb8..966db2ceac9724911b6249b2d66e702d6ea024d3 100644
--- a/Modules/Wrappers/ApplicationEngine/test/otbWrapperParameterListTest.cxx
+++ b/Modules/Wrappers/ApplicationEngine/test/otbWrapperParameterListTest.cxx
@@ -20,6 +20,9 @@
 #endif
 
 #include "otbWrapperParameterGroup.h"
+#include "otbWrapperStringParameter.h"
+#include "otbWrapperNumericalParameter.h"
+#include "otbWrapperProxyParameter.h"
 
 int otbWrapperParameterListNew(int itkNotUsed(argc), char * itkNotUsed(argv)[])
 {
@@ -30,3 +33,66 @@ int otbWrapperParameterListNew(int itkNotUsed(argc), char * itkNotUsed(argv)[])
 
   return EXIT_SUCCESS;
 }
+
+int otbWrapperParameterList(int itkNotUsed(argc), char * itkNotUsed(argv)[])
+{
+  typedef otb::Wrapper::ParameterGroup  GroupPrm;
+  typedef otb::Wrapper::StringParameter StringPrm;
+  typedef otb::Wrapper::IntParameter    IntPrm;
+  typedef otb::Wrapper::ProxyParameter  ProxyPrm;
+
+  // setup first group of parameters
+  GroupPrm::Pointer parameters = GroupPrm::New();
+
+  StringPrm::Pointer strParam = StringPrm::New();
+  strParam->SetKey("str");
+
+  IntPrm::Pointer numParam = IntPrm::New();
+  numParam->SetKey("num");
+  numParam->SetValue(1);
+
+  parameters->AddParameter(strParam.GetPointer());
+  parameters->AddParameter(numParam.GetPointer());
+
+  // setup second group of parameters
+  GroupPrm::Pointer otherParameters = GroupPrm::New();
+
+  IntPrm::Pointer hiddenParam = IntPrm::New();
+  hiddenParam->SetKey("hidden");
+  hiddenParam->SetValue(2);
+
+  otherParameters->AddParameter(hiddenParam.GetPointer());
+
+  ProxyPrm::Pointer proxyParam = ProxyPrm::New();
+  ProxyPrm::ProxyTargetType target;
+  target.first = otherParameters;
+  target.second = "hidden";
+  proxyParam->SetTarget(target);
+
+  // try to set a proxy to "hidden" in the first group
+  std::string proxyKey("num");
+  if (! parameters->ReplaceParameter(proxyKey, proxyParam.GetPointer()))
+    {
+    std::cout << "Failed to replace with proxy parameter" << std::endl;
+    return EXIT_FAILURE;
+    }
+
+  // check that we get the right value in "num"
+  otb::Wrapper::Parameter* resultParam = GroupPrm::ResolveParameter(parameters->GetParameterByKey("num"));
+  IntPrm* castInt = dynamic_cast<IntPrm*>(resultParam);
+  if (castInt)
+    {
+    if (castInt->GetValue() != 2)
+      {
+      std::cout << "Failed to setup proxy on int parameter, got "<< castInt->GetValue()<< ", expected 2."<< std::endl;
+      return EXIT_FAILURE;
+      }
+    }
+  else
+    {
+    std::cout << "Can't cast parameter to Int, probably wrong type."<< std::endl;
+    return EXIT_FAILURE;
+    }
+
+  return EXIT_SUCCESS;
+}