diff --git a/app/code/Magento/Backend/App/Config.php b/app/code/Magento/Backend/App/Config.php
index d8eaa7af14254bd16dd3c8f7ae3cc9a549e75cde..0ac5211b41d9f58308d4f57977e0c8054c15e4c7 100644
--- a/app/code/Magento/Backend/App/Config.php
+++ b/app/code/Magento/Backend/App/Config.php
@@ -10,6 +10,8 @@
 
 namespace Magento\Backend\App;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 /**
  * Backend config accessor
  */
@@ -36,7 +38,7 @@ class Config implements ConfigInterface
      */
     public function getValue($path)
     {
-        return $this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, null)->getValue($path);
+        return $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->getValue($path);
     }
 
     /**
@@ -48,7 +50,7 @@ class Config implements ConfigInterface
      */
     public function setValue($path, $value)
     {
-        $this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, null)->setValue($path, $value);
+        $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->setValue($path, $value);
     }
 
     /**
@@ -59,6 +61,6 @@ class Config implements ConfigInterface
      */
     public function isSetFlag($path)
     {
-        return !!$this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, null)->getValue($path);
+        return !!$this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, null)->getValue($path);
     }
 }
diff --git a/app/code/Magento/Backend/Block/Menu.php b/app/code/Magento/Backend/Block/Menu.php
index 55ad3f2cbca00cec0f08004c0a0db5867727d769..087fd1fa351709778cc536f519c4b84206d97134 100644
--- a/app/code/Magento/Backend/Block/Menu.php
+++ b/app/code/Magento/Backend/Block/Menu.php
@@ -436,6 +436,7 @@ class Menu extends \Magento\Backend\Block\Template
      * @param array $colBrakes
      * @return string HTML
      * @SuppressWarnings(PHPMD.NPathComplexity)
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     public function renderNavigation($menu, $level = 0, $limit = 0, $colBrakes = [])
     {
@@ -454,21 +455,30 @@ class Menu extends \Magento\Backend\Block\Template
             }
 
             $id = $this->getJsId($menuItem->getId());
-            $output .= '<li ' . $this->getUiId(
-                $menuItem->getId()
-            ) . ' class="item-' . $itemClass . ' ' . $this->_renderItemCssClass(
-                $menuItem,
-                $level
-            ) . ($level == 0 ? '" id="' . $id . '" aria-haspopup="true' : '')
-                . '" role="menu-item">' . $this->_renderAnchor(
-                $menuItem,
-                $level
-            ) . $this->_addSubMenu(
-                $menuItem,
-                $level,
-                $limit,
-                $id
-            ) . '</li>';
+            if (count($menu) > 1 || $level != 1) {
+                $output .= '<li ' . $this->getUiId(
+                        $menuItem->getId()
+                    ) . ' class="item-' . $itemClass . ' ' . $this->_renderItemCssClass(
+                        $menuItem,
+                        $level
+                    ) . ($level == 0 ? '" id="' . $id . '" aria-haspopup="true' : '')
+                    . '" role="menu-item">' . $this->_renderAnchor(
+                        $menuItem,
+                        $level
+                    ) . $this->_addSubMenu(
+                        $menuItem,
+                        $level,
+                        $limit,
+                        $id
+                    ) . '</li>';
+            } else {
+                $output .= $this->_addSubMenu(
+                    $menuItem,
+                    $level,
+                    $limit,
+                    $id);
+            }
+
             $itemPosition++;
         }
 
diff --git a/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php b/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php
index d32e99a00d814b5a134e9e3060526e7586f9043e..827ae18b0b8e1f908653597bc1816a6c73d9b148 100644
--- a/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php
+++ b/app/code/Magento/Backend/Block/Page/System/Config/Robots/Reset.php
@@ -8,6 +8,8 @@
 
 namespace Magento\Backend\Block\Page\System\Config\Robots;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 /**
  * "Reset to Defaults" button renderer
  *
@@ -50,7 +52,7 @@ class Reset extends \Magento\Config\Block\System\Config\Form\Field
     public function getRobotsDefaultCustomInstructions()
     {
         return trim((string)$this->_scopeConfig->getValue(
-            self::XML_PATH_ROBOTS_DEFAULT_CUSTOM_INSTRUCTIONS, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+            self::XML_PATH_ROBOTS_DEFAULT_CUSTOM_INSTRUCTIONS, ScopeConfigInterface::SCOPE_TYPE_DEFAULT
         ));
     }
 
diff --git a/app/code/Magento/Backend/Model/Auth/Session.php b/app/code/Magento/Backend/Model/Auth/Session.php
index fec438f70c744d7b7cc9999d8574f3e4deef862c..d5cf7e86c4b8ae8c301a2993893a9d31d615e7e5 100644
--- a/app/code/Magento/Backend/Model/Auth/Session.php
+++ b/app/code/Magento/Backend/Model/Auth/Session.php
@@ -61,9 +61,11 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage
      * @param \Magento\Framework\Session\StorageInterface $storage
      * @param CookieManagerInterface $cookieManager
      * @param CookieMetadataFactory $cookieMetadataFactory
+     * @param \Magento\Framework\App\State $appState
      * @param \Magento\Framework\Acl\Builder $aclBuilder
      * @param \Magento\Backend\Model\UrlInterface $backendUrl
      * @param \Magento\Backend\App\ConfigInterface $config
+     * @throws \Magento\Framework\Exception\SessionException
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -75,6 +77,7 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage
         \Magento\Framework\Session\StorageInterface $storage,
         CookieManagerInterface $cookieManager,
         CookieMetadataFactory $cookieMetadataFactory,
+        \Magento\Framework\App\State $appState,
         \Magento\Framework\Acl\Builder $aclBuilder,
         \Magento\Backend\Model\UrlInterface $backendUrl,
         \Magento\Backend\App\ConfigInterface $config
@@ -90,9 +93,9 @@ class Session extends \Magento\Framework\Session\SessionManager implements \Mage
             $validator,
             $storage,
             $cookieManager,
-            $cookieMetadataFactory
+            $cookieMetadataFactory,
+            $appState
         );
-        $this->start();
     }
 
     /**
diff --git a/app/code/Magento/Backend/Model/Locale/Manager.php b/app/code/Magento/Backend/Model/Locale/Manager.php
index 76a327f1364d1b42a05f3ea000474049d5b87068..4f9584c96a81706b0eaefafd3137a27d75291fc3 100644
--- a/app/code/Magento/Backend/Model/Locale/Manager.php
+++ b/app/code/Magento/Backend/Model/Locale/Manager.php
@@ -68,7 +68,7 @@ class Manager
      */
     public function getUserInterfaceLocale()
     {
-        $interfaceLocale = \Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE;
+        $interfaceLocale = \Magento\Framework\Locale\Resolver::DEFAULT_LOCALE;
 
         $userData = $this->_authSession->getUser();
         if ($userData && $userData->getInterfaceLocale()) {
diff --git a/app/code/Magento/Backend/Model/Session.php b/app/code/Magento/Backend/Model/Session.php
index c0719bb2ecf9ef98084a0ecba7e11630732eddbf..6dd5af4a3c54830029f8372e214009a4e3b04bef 100644
--- a/app/code/Magento/Backend/Model/Session.php
+++ b/app/code/Magento/Backend/Model/Session.php
@@ -9,39 +9,6 @@ namespace Magento\Backend\Model;
 
 class Session extends \Magento\Framework\Session\SessionManager
 {
-    /**
-     * @param \Magento\Framework\App\Request\Http $request
-     * @param \Magento\Framework\Session\SidResolverInterface $sidResolver
-     * @param \Magento\Framework\Session\Config\ConfigInterface $sessionConfig
-     * @param \Magento\Framework\Session\SaveHandlerInterface $saveHandler
-     * @param \Magento\Framework\Session\ValidatorInterface $validator
-     * @param \Magento\Framework\Session\StorageInterface $storage
-     * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
-     * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
-     */
-    public function __construct(
-        \Magento\Framework\App\Request\Http $request,
-        \Magento\Framework\Session\SidResolverInterface $sidResolver,
-        \Magento\Framework\Session\Config\ConfigInterface $sessionConfig,
-        \Magento\Framework\Session\SaveHandlerInterface $saveHandler,
-        \Magento\Framework\Session\ValidatorInterface $validator,
-        \Magento\Framework\Session\StorageInterface $storage,
-        \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
-        \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
-    ) {
-        parent::__construct(
-            $request,
-            $sidResolver,
-            $sessionConfig,
-            $saveHandler,
-            $validator,
-            $storage,
-            $cookieManager,
-            $cookieMetadataFactory
-        );
-        $this->start();
-    }
-
     /**
      * Skip path validation in backend area
      *
diff --git a/app/code/Magento/Backend/Model/Session/Quote.php b/app/code/Magento/Backend/Model/Session/Quote.php
index 2f1f75ab07e9201d03ec0706fc3a0fb20f42ddc1..1427e1ad5ad570d5ea93c0f5cef3c5cdfd71c4cb 100644
--- a/app/code/Magento/Backend/Model/Session/Quote.php
+++ b/app/code/Magento/Backend/Model/Session/Quote.php
@@ -83,11 +83,13 @@ class Quote extends \Magento\Framework\Session\SessionManager
      * @param \Magento\Framework\Session\StorageInterface $storage
      * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
      * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
+     * @param \Magento\Framework\App\State $appState
      * @param CustomerRepositoryInterface $customerRepository
      * @param \Magento\Quote\Model\QuoteRepository $quoteRepository
      * @param \Magento\Sales\Model\OrderFactory $orderFactory
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param GroupManagementInterface $groupManagement
+     * @throws \Magento\Framework\Exception\SessionException
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -99,6 +101,7 @@ class Quote extends \Magento\Framework\Session\SessionManager
         \Magento\Framework\Session\StorageInterface $storage,
         \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
         \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory,
+        \Magento\Framework\App\State $appState,
         CustomerRepositoryInterface $customerRepository,
         \Magento\Quote\Model\QuoteRepository $quoteRepository,
         \Magento\Sales\Model\OrderFactory $orderFactory,
@@ -118,9 +121,9 @@ class Quote extends \Magento\Framework\Session\SessionManager
             $validator,
             $storage,
             $cookieManager,
-            $cookieMetadataFactory
+            $cookieMetadataFactory,
+            $appState
         );
-        $this->start();
         if ($this->_storeManager->hasSingleStore()) {
             $this->setStoreId($this->_storeManager->getStore(true)->getId());
         }
@@ -147,7 +150,7 @@ class Quote extends \Magento\Framework\Session\SessionManager
                     $this->_quote->setStoreId($this->getStoreId());
                 }
 
-                if ($this->getCustomerId()) {
+                if ($this->getCustomerId() && $this->getCustomerId() != $this->_quote->getCustomerId()) {
                     $customer = $this->customerRepository->getById($this->getCustomerId());
                     $this->_quote->assignCustomer($customer);
                 }
diff --git a/app/code/Magento/Backend/Model/Url.php b/app/code/Magento/Backend/Model/Url.php
index 265a7237ed37e1713fb17d97a3674c633d406341..36fbfafe66974d236c0bc90c2d7133fc3bcf128b 100644
--- a/app/code/Magento/Backend/Model/Url.php
+++ b/app/code/Magento/Backend/Model/Url.php
@@ -83,7 +83,7 @@ class Url extends \Magento\Framework\Url implements \Magento\Backend\Model\UrlIn
      * @param \Magento\Framework\Url\ScopeResolverInterface $scopeResolver
      * @param \Magento\Framework\Session\Generic $session
      * @param \Magento\Framework\Session\SidResolverInterface $sidResolver
-     * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver
+     * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory
      * @param \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
      * @param string $scopeType
@@ -105,7 +105,7 @@ class Url extends \Magento\Framework\Url implements \Magento\Backend\Model\UrlIn
         \Magento\Framework\Url\ScopeResolverInterface $scopeResolver,
         \Magento\Framework\Session\Generic $session,
         \Magento\Framework\Session\SidResolverInterface $sidResolver,
-        \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver,
+        \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory,
         \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver,
         \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
         $scopeType,
@@ -126,7 +126,7 @@ class Url extends \Magento\Framework\Url implements \Magento\Backend\Model\UrlIn
             $scopeResolver,
             $session,
             $sidResolver,
-            $routeParamsResolver,
+            $routeParamsResolverFactory,
             $queryParamsResolver,
             $scopeConfig,
             $scopeType,
diff --git a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php
index 000c861f11d8eba4c098f8427df867287de61c2d..a624a526d5dbffd7dcd06460a11872fb1f4328c2 100644
--- a/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/Locale/ManagerTest.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Backend\Test\Unit\Model\Locale;
 
+use Magento\Framework\Locale\Resolver;
+
 class ManagerTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -87,7 +89,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
     {
         $locale = $this->_model->getUserInterfaceLocale();
 
-        $this->assertEquals($locale, \Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE);
+        $this->assertEquals($locale, Resolver::DEFAULT_LOCALE);
     }
 
     /**
diff --git a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php
index 13f906bee346b45c02399d3f2cdc59602f9dec3f..fd3af3380023396c31f53ac97a41a1a631154993 100644
--- a/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/Session/QuoteTest.php
@@ -182,6 +182,13 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
+        $appStateMock = $this->getMock(
+            'Magento\Framework\App\State',
+            [],
+            [],
+            '',
+            false
+        );
         $this->storeManagerMock = $this->getMockForAbstractClass(
             'Magento\Store\Model\StoreManagerInterface',
             [],
@@ -201,11 +208,12 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
                 'storage' => $this->storageMock,
                 'cookieManager' => $this->cookieManagerMock,
                 'cookieMetadataFactory' => $this->cookieMetadataFactoryMock,
+                'appState' => $appStateMock,
                 'customerRepository' => $this->customerRepositoryMock,
                 'quoteRepository' => $this->quoteRepositoryMock,
                 'orderFactory' => $this->orderFactoryMock,
                 'storeManager' => $this->storeManagerMock,
-                'groupManagement' => $this->groupManagementMock
+                'groupManagement' => $this->groupManagementMock,
             ],
             '',
             true
@@ -217,12 +225,42 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
      *
      * @return void
      */
-    public function testGetQuote()
+    public function testGetQuoteWithoutQuoteId()
     {
-        $storeId = 10;
         $quoteId = 22;
-        $customerGroupId = 77;
+        $storeId = 10;
         $customerId = 66;
+        $customerGroupId = 77;
+
+        $this->quote->expects($this->any())
+            ->method('getQuoteId')
+            ->will($this->returnValue(null));
+        $this->quote->expects($this->any())
+            ->method('setQuoteId')
+            ->with($quoteId);
+        $this->quote->expects($this->any())
+            ->method('getStoreId')
+            ->will($this->returnValue($storeId));
+        $this->quote->expects($this->any())
+            ->method('getCustomerId')
+            ->will($this->returnValue($customerId));
+
+        $defaultGroup = $this->getMockBuilder('Magento\Customer\Api\Data\GroupInterface')
+            ->getMock();
+        $defaultGroup->expects($this->any())
+            ->method('getId')
+            ->will($this->returnValue($customerGroupId));
+        $this->groupManagementMock->expects($this->any())
+            ->method('getDefaultGroup')
+            ->will($this->returnValue($defaultGroup));
+
+        $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->customerRepositoryMock->expects($this->once())
+            ->method('getById')
+            ->with($customerId)
+            ->willReturn($dataCustomerMock);
 
         $quoteMock = $this->getMock(
             'Magento\Quote\Model\Quote',
@@ -240,28 +278,9 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-
-        $defaultGroup = $this->getMockBuilder('Magento\Customer\Api\Data\GroupInterface')
-            ->getMock();
-        $defaultGroup->expects($this->any())
-            ->method('getId')
-            ->will($this->returnValue($customerGroupId));
-        $this->groupManagementMock->expects($this->any())
-            ->method('getDefaultGroup')
-            ->will($this->returnValue($defaultGroup));
-
-        $this->quoteRepositoryMock->expects($this->once())
-            ->method('create')
-            ->will($this->returnValue($quoteMock));
-        $this->quote->expects($this->any())
-            ->method('getStoreId')
-            ->will($this->returnValue($storeId));
         $quoteMock->expects($this->once())
             ->method('setStoreId')
             ->with($storeId);
-        $this->quote->expects($this->any())
-            ->method('getQuoteId')
-            ->will($this->returnValue(null));
         $quoteMock->expects($this->once())
             ->method('setCustomerGroupId')
             ->with($customerGroupId)
@@ -270,25 +289,9 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
             ->method('setIsActive')
             ->with(false)
             ->will($this->returnSelf());
-        $this->quoteRepositoryMock->expects($this->once())
-            ->method('save')
-            ->with($quoteMock);
         $quoteMock->expects($this->once())
             ->method('getId')
             ->will($this->returnValue($quoteId));
-        $this->quote->expects($this->any())
-            ->method('setQuoteId')
-            ->with($quoteId);
-        $this->quote->expects($this->any())
-            ->method('getCustomerId')
-            ->will($this->returnValue($customerId));
-        $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $this->customerRepositoryMock->expects($this->once())
-            ->method('getById')
-            ->with($customerId)
-            ->willReturn($dataCustomerMock);
         $quoteMock->expects($this->once())
             ->method('assignCustomer')
             ->with($dataCustomerMock);
@@ -299,6 +302,13 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
             ->method('setIsSuperMode')
             ->with(true);
 
+        $this->quoteRepositoryMock->expects($this->once())
+            ->method('create')
+            ->will($this->returnValue($quoteMock));
+        $this->quoteRepositoryMock->expects($this->once())
+            ->method('save')
+            ->with($quoteMock);
+
         $this->assertEquals($quoteMock, $this->quote->getQuote());
     }
 
@@ -306,12 +316,33 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
      * Run test getQuote method
      *
      * @return void
+     * @dataProvider getQuoteDataProvider
      */
-    public function testGetQuoteGet()
+    public function testGetQuoteWithQuoteId($customerId, $quoteCustomerId, $expectedNumberOfInvokes)
     {
-        $storeId = 10;
         $quoteId = 22;
-        $customerId = 66;
+        $storeId = 10;
+
+        $this->quote->expects($this->any())
+            ->method('getQuoteId')
+            ->will($this->returnValue($quoteId));
+        $this->quote->expects($this->any())
+            ->method('setQuoteId')
+            ->with($quoteId);
+        $this->quote->expects($this->any())
+            ->method('getStoreId')
+            ->will($this->returnValue($storeId));
+        $this->quote->expects($this->any())
+            ->method('getCustomerId')
+            ->will($this->returnValue($customerId));
+
+        $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->customerRepositoryMock->expects($this->$expectedNumberOfInvokes())
+            ->method('getById')
+            ->with($customerId)
+            ->willReturn($dataCustomerMock);
 
         $quoteMock = $this->getMock(
             'Magento\Quote\Model\Quote',
@@ -323,43 +354,17 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
                 'assignCustomer',
                 'setIgnoreOldQty',
                 'setIsSuperMode',
+                'getCustomerId',
                 '__wakeup'
             ],
             [],
             '',
             false
         );
-
-        $this->quoteRepositoryMock->expects($this->once())
-            ->method('create')
-            ->will($this->returnValue($quoteMock));
-        $this->quote->expects($this->any())
-            ->method('getStoreId')
-            ->will($this->returnValue($storeId));
         $quoteMock->expects($this->once())
             ->method('setStoreId')
             ->with($storeId);
-        $this->quote->expects($this->any())
-            ->method('getQuoteId')
-            ->will($this->returnValue($quoteId));
-        $this->quoteRepositoryMock->expects($this->once())
-            ->method('get')
-            ->with($quoteId)
-            ->willReturn($quoteMock);
-        $this->quote->expects($this->any())
-            ->method('setQuoteId')
-            ->with($quoteId);
-        $this->quote->expects($this->any())
-            ->method('getCustomerId')
-            ->will($this->returnValue($customerId));
-        $dataCustomerMock = $this->getMockBuilder('Magento\Customer\Api\Data\CustomerInterface')
-            ->disableOriginalConstructor()
-            ->getMock();
-        $this->customerRepositoryMock->expects($this->once())
-            ->method('getById')
-            ->with($customerId)
-            ->willReturn($dataCustomerMock);
-        $quoteMock->expects($this->once())
+        $quoteMock->expects($this->$expectedNumberOfInvokes())
             ->method('assignCustomer')
             ->with($dataCustomerMock);
         $quoteMock->expects($this->once())
@@ -368,7 +373,29 @@ class QuoteTest extends \PHPUnit_Framework_TestCase
         $quoteMock->expects($this->once())
             ->method('setIsSuperMode')
             ->with(true);
+        $quoteMock->expects($this->once())
+            ->method('getCustomerId')
+            ->will($this->returnValue($quoteCustomerId));
+
+        $this->quoteRepositoryMock->expects($this->once())
+            ->method('create')
+            ->will($this->returnValue($quoteMock));
+        $this->quoteRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($quoteId)
+            ->willReturn($quoteMock);
 
         $this->assertEquals($quoteMock, $this->quote->getQuote());
     }
+
+    /**
+     * @return array
+     */
+    public function getQuoteDataProvider()
+    {
+        return [
+            'customer ids different' => [66, null, 'once'],
+            'customer ids same' => [66, 66, 'never'],
+        ];
+    }
 }
diff --git a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php
index a53b891a17be5e100f0916cf04e6b2a4f3469c71..b833fe25752a09ecd9af87a7147f4debf493affe 100644
--- a/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php
+++ b/app/code/Magento/Backend/Test/Unit/Model/UrlTest.php
@@ -165,7 +165,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
                 'menuConfig' => $this->_menuConfigMock,
                 'authSession' => $this->_authSessionMock,
                 'encryptor' => $this->_encryptor,
-                'routeParamsResolver' => $this->_paramsResolverMock
+                'routeParamsResolverFactory' => $this->_paramsResolverMock
             ]
         );
         $this->_paramsResolverMock->expects(
@@ -186,7 +186,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
                 'menuConfig' => $this->_menuConfigMock,
                 'authSession' => $this->_authSessionMock,
                 'encryptor' => $this->_encryptor,
-                'routeParamsResolver' => $this->_paramsResolverMock
+                'routeParamsResolverFactory' => $this->_paramsResolverMock
             ]
         );
 
@@ -259,7 +259,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
             [
                 'backendHelper' => $helperMock,
                 'authSession' => $this->_authSessionMock,
-                'routeParamsResolver' => $this->_paramsResolverMock
+                'routeParamsResolverFactory' => $this->_paramsResolverMock
             ]
         );
         $urlModel->getAreaFrontName();
diff --git a/app/code/Magento/Backup/README.md b/app/code/Magento/Backup/README.md
index 661f8a5d6ddd86f23772c9f595526420ae51dd9e..59688ea3e716e162e8efa9421de0ffe004ec3cee 100644
--- a/app/code/Magento/Backup/README.md
+++ b/app/code/Magento/Backup/README.md
@@ -1,3 +1,3 @@
 The Backup module allows administrators to perform backups and rollbacks. Types of backups include system, database and media backups. This module relies on the Cron module to schedule backups.
 
-This module does not effect the storefront.
\ No newline at end of file
+This module does not affect the storefront.
diff --git a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml
index 861bc2050e44a61e3cf1daf950dcad8fc05d0997..1553a783f6356dca8510044813cd919312ff5fdb 100644
--- a/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml
+++ b/app/code/Magento/Backup/view/adminhtml/layout/backup_index_block.xml
@@ -46,7 +46,6 @@
                         <arguments>
                             <argument name="header" xsi:type="string" translate="true">Name</argument>
                             <argument name="index" xsi:type="string">display_name</argument>
-                            <argument name="filter" xsi:type="string">0</argument>
                             <argument name="sortable" xsi:type="string">1</argument>
                             <argument name="column_css_class" xsi:type="string">col-name</argument>
                             <argument name="header_css_class" xsi:type="string">col-name</argument>
diff --git a/app/code/Magento/Bundle/etc/data_object.xml b/app/code/Magento/Bundle/etc/service_data_attributes.xml
similarity index 70%
rename from app/code/Magento/Bundle/etc/data_object.xml
rename to app/code/Magento/Bundle/etc/service_data_attributes.xml
index 2b3da013978f971bb20baca3a5e0ca95f2b501fc..8150a2dceeddd7d70d4e16ffa3d1b6996f316d5d 100644
--- a/app/code/Magento/Bundle/etc/data_object.xml
+++ b/app/code/Magento/Bundle/etc/service_data_attributes.xml
@@ -5,8 +5,8 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
         <attribute code="bundle_product_options" type="Magento\Bundle\Api\Data\OptionInterface[]" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
index a79466c6007bfb13a619d59a922c29d99d947968..29be0868558ee450d152d492091f5cb5b9bbaef8 100644
--- a/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
+++ b/app/code/Magento/Catalog/Block/Product/ProductList/Toolbar.php
@@ -29,7 +29,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
      *
      * @var array
      */
-    protected $_availableOrder = [];
+    protected $_availableOrder = null;
 
     /**
      * List of available view types
@@ -146,19 +146,6 @@ class Toolbar extends \Magento\Framework\View\Element\Template
         parent::__construct($context, $data);
     }
 
-    /**
-     * Init Toolbar
-     *
-     * @return null
-     */
-    protected function _construct()
-    {
-        parent::_construct();
-        $this->_orderField = $this->_productListHelper->getDefaultSortField();
-        $this->_availableOrder = $this->_catalogConfig->getAttributeUsedForSortByArray();
-        $this->_availableMode = $this->_productListHelper->getAvailableViewMode();
-    }
-
     /**
      * Disable list state params memorizing
      *
@@ -241,7 +228,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
         }
 
         $orders = $this->getAvailableOrders();
-        $defaultOrder = $this->_orderField;
+        $defaultOrder = $this->getOrderField();
 
         if (!isset($orders[$defaultOrder])) {
             $keys = array_keys($orders);
@@ -295,6 +282,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
      */
     public function setDefaultOrder($field)
     {
+        $this->loadAvailableOrders();
         if (isset($this->_availableOrder[$field])) {
             $this->_orderField = $field;
         }
@@ -322,6 +310,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
      */
     public function getAvailableOrders()
     {
+        $this->loadAvailableOrders();
         return $this->_availableOrder;
     }
 
@@ -346,6 +335,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
      */
     public function addOrderToAvailableOrders($order, $value)
     {
+        $this->loadAvailableOrders();
         $this->_availableOrder[$order] = $value;
         return $this;
     }
@@ -358,6 +348,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
      */
     public function removeOrderFromAvailableOrders($order)
     {
+        $this->loadAvailableOrders();
         if (isset($this->_availableOrder[$order])) {
             unset($this->_availableOrder[$order]);
         }
@@ -411,7 +402,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
         if ($mode) {
             return $mode;
         }
-        $defaultMode = $this->_productListHelper->getDefaultViewMode($this->_availableMode);
+        $defaultMode = $this->_productListHelper->getDefaultViewMode($this->getModes());
         $mode = $this->_toolbarModel->getMode();
         if (!$mode || !isset($this->_availableMode[$mode])) {
             $mode = $defaultMode;
@@ -439,6 +430,9 @@ class Toolbar extends \Magento\Framework\View\Element\Template
      */
     public function getModes()
     {
+        if ($this->_availableMode === []) {
+            $this->_availableMode = $this->_productListHelper->getAvailableViewMode();
+        }
         return $this->_availableMode;
     }
 
@@ -450,6 +444,7 @@ class Toolbar extends \Magento\Framework\View\Element\Template
      */
     public function setModes($modes)
     {
+        $this->getModes();
         if (!isset($this->_availableMode)) {
             $this->_availableMode = $modes;
         }
@@ -691,4 +686,30 @@ class Toolbar extends \Magento\Framework\View\Element\Template
         $options = array_replace_recursive($options, $customOptions);
         return json_encode(['productListToolbarForm' => $options]);
     }
+
+    /**
+     * Get order field
+     *
+     * @return null|string
+     */
+    protected function getOrderField()
+    {
+        if ($this->_orderField === null) {
+            $this->_orderField = $this->_productListHelper->getDefaultSortField();
+        }
+        return $this->_orderField;
+    }
+
+    /**
+     * Load Available Orders
+     *
+     * @return $this
+     */
+    private function loadAvailableOrders()
+    {
+        if ($this->_availableOrder === null) {
+            $this->_availableOrder = $this->_catalogConfig->getAttributeUsedForSortByArray();
+        }
+        return $this;
+    }
 }
diff --git a/app/code/Magento/Catalog/Block/Product/View/Options.php b/app/code/Magento/Catalog/Block/Product/View/Options.php
index f80bdede71af34f4440d8cce9ff14da2ebfdf9f0..db3d2a911038295fbcb8942d7c61f0041a2637a5 100644
--- a/app/code/Magento/Catalog/Block/Product/View/Options.php
+++ b/app/code/Magento/Catalog/Block/Product/View/Options.php
@@ -206,19 +206,30 @@ class Options extends \Magento\Framework\View\Element\Template
             /* @var $option \Magento\Catalog\Model\Product\Option */
             $priceValue = 0;
             if ($option->getGroupByType() == \Magento\Catalog\Model\Product\Option::OPTION_GROUP_SELECT) {
-                $_tmpPriceValues = [];
+                $tmpPriceValues = [];
                 foreach ($option->getValues() as $value) {
                     /* @var $value \Magento\Catalog\Model\Product\Option\Value */
                     $id = $value->getId();
-                    $_tmpPriceValues[$id] = $this->_getPriceConfiguration($value);
+                    $tmpPriceValues[$id] = $this->_getPriceConfiguration($value);
                 }
-                $priceValue = $_tmpPriceValues;
+                $priceValue = $tmpPriceValues;
             } else {
                 $priceValue = $this->_getPriceConfiguration($option);
             }
             $config[$option->getId()] = $priceValue;
         }
 
+        $configObj = new \Magento\Framework\Object(
+            [
+                'config' => $config,
+            ]
+        );
+
+        //pass the return array encapsulated in an object for the other modules to be able to alter it eg: weee
+        $this->_eventManager->dispatch('catalog_product_option_price_configuration_after', ['configObj' => $configObj]);
+
+        $config=$configObj->getConfig();
+
         return $this->_jsonEncoder->encode($config);
     }
 
diff --git a/app/code/Magento/Catalog/Model/Observer.php b/app/code/Magento/Catalog/Model/Observer.php
index 39e72883f68add9d3672497199eb7c744480a7db..417a4f61811c99663538fd72e3c790ddc0753f3c 100644
--- a/app/code/Magento/Catalog/Model/Observer.php
+++ b/app/code/Magento/Catalog/Model/Observer.php
@@ -34,7 +34,14 @@ class Observer
      *
      * @var \Magento\Catalog\Model\Layer
      */
-    protected $_catalogLayer;
+    private $_catalogLayer = null;
+
+    /**
+     * Catalog layer resolver
+     *
+     * @var \Magento\Catalog\Model\Layer\Resolver
+     */
+    protected $layerResolver;
 
     /**
      * Store manager
@@ -95,7 +102,7 @@ class Observer
         $this->_categoryResource = $categoryResource;
         $this->_catalogProduct = $catalogProduct;
         $this->_storeManager = $storeManager;
-        $this->_catalogLayer = $layerResolver->get();
+        $this->layerResolver = $layerResolver;
         $this->_catalogCategory = $catalogCategory;
         $this->_catalogData = $catalogData;
         $this->categoryFlatConfig = $categoryFlatState;
@@ -184,11 +191,12 @@ class Observer
      */
     protected function hasActive($category)
     {
-        if (!$this->_catalogLayer) {
+        $catalogLayer = $this->getCatalogLayer();
+        if (!$catalogLayer) {
             return false;
         }
 
-        $currentCategory = $this->_catalogLayer->getCurrentCategory();
+        $currentCategory = $catalogLayer->getCurrentCategory();
         if (!$currentCategory) {
             return false;
         }
@@ -196,4 +204,16 @@ class Observer
         $categoryPathIds = explode(',', $currentCategory->getPathInStore());
         return in_array($category->getId(), $categoryPathIds);
     }
+
+    /**
+     * Get catalog layer
+     * @return \Magento\Catalog\Model\Layer
+     */
+    private function getCatalogLayer()
+    {
+        if ($this->_catalogLayer === null) {
+            $this->_catalogLayer = $this->layerResolver->get();
+        }
+        return $this->_catalogLayer;
+    }
 }
diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php
index ebd9494fe1f842f7e42eba21cdb0c3da4cbd1325..d012f23de3df22b5e130abe2454fe2dc8f030c25 100644
--- a/app/code/Magento/Catalog/Model/Product.php
+++ b/app/code/Magento/Catalog/Model/Product.php
@@ -250,15 +250,25 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
     protected $metadataService;
 
     /*
-     * @param \Magento\Catalog\Model\ProductLink\ProductLinkManagementInterface
+     * @param \Magento\Catalog\Model\ProductLink\CollectionProvider
      */
-    protected $linkManagement;
+    protected $entityCollectionProvider;
 
     /*
-     * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory
+     * @param \Magento\Catalog\Model\Product\LinkTypeProvider
+     */
+    protected $linkProvider;
+
+    /*
+     * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory
      */
     protected $productLinkFactory;
 
+    /*
+     * @param \Magento\Catalog\Api\Data\ProductLinkExtensionFactory
+     */
+    protected $productLinkExtensionFactory;
+
     /**
      * @var \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory
      */
@@ -318,8 +328,10 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
      * @param Indexer\Product\Eav\Processor $productEavIndexerProcessor
      * @param CategoryRepositoryInterface $categoryRepository
      * @param Product\Image\CacheFactory $imageCacheFactory
-     * @param \Magento\Catalog\Model\ProductLink\Management $linkManagement
-     * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory,
+     * @param \Magento\Catalog\Model\ProductLink\CollectionProvider $entityCollectionProvider
+     * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider
+     * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory
+     * @param \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory
      * @param \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory
      * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
      * @param array $data
@@ -354,8 +366,10 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
         \Magento\Catalog\Model\Indexer\Product\Eav\Processor $productEavIndexerProcessor,
         CategoryRepositoryInterface $categoryRepository,
         Product\Image\CacheFactory $imageCacheFactory,
-        \Magento\Catalog\Model\ProductLink\Management $linkManagement,
+        \Magento\Catalog\Model\ProductLink\CollectionProvider $entityCollectionProvider,
+        \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider,
         \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory,
+        \Magento\Catalog\Api\Data\ProductLinkExtensionFactory $productLinkExtensionFactory,
         \Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory $mediaGalleryEntryFactory,
         \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
         array $data = []
@@ -380,8 +394,10 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
         $this->_productEavIndexerProcessor = $productEavIndexerProcessor;
         $this->categoryRepository = $categoryRepository;
         $this->imageCacheFactory = $imageCacheFactory;
-        $this->linkManagement = $linkManagement;
+        $this->entityCollectionProvider = $entityCollectionProvider;
+        $this->linkTypeProvider = $linkTypeProvider;
         $this->productLinkFactory = $productLinkFactory;
+        $this->productLinkExtensionFactory = $productLinkExtensionFactory;
         $this->mediaGalleryEntryFactory = $mediaGalleryEntryFactory;
         $this->dataObjectHelper = $dataObjectHelper;
         parent::__construct(
@@ -1352,23 +1368,34 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
     public function getProductLinks()
     {
         if (empty($this->_links)) {
-            $productLinks = [];
-
-            $productLinks['related'] = $this->getRelatedProducts();
-            $productLinks['upsell'] = $this->getUpSellProducts();
-            $productLinks['crosssell'] = $this->getCrossSellProducts();
-
             $output = [];
-            foreach ($productLinks as $type => $linkTypeArray) {
-                foreach ($linkTypeArray as $link) {
+            $linkTypes = $this->linkTypeProvider->getLinkTypes();
+            foreach (array_keys($linkTypes) as $linkTypeName) {
+                $collection = $this->entityCollectionProvider->getCollection($this, $linkTypeName);
+                foreach ($collection as $item) {
                     /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */
                     $productLink = $this->productLinkFactory->create();
                     $productLink->setProductSku($this->getSku())
-                        ->setLinkType($type)
-                        ->setLinkedProductSku($link['sku'])
-                        ->setLinkedProductType($link['type_id'])
-                        ->setPosition($link['position']);
-
+                        ->setLinkType($linkTypeName)
+                        ->setLinkedProductSku($item['sku'])
+                        ->setLinkedProductType($item['type'])
+                        ->setPosition($item['position']);
+                    if (isset($item['custom_attributes'])) {
+                        $productLinkExtension = $productLink->getExtensionAttributes();
+                        if ($productLinkExtension === null) {
+                            $productLinkExtension = $this->productLinkExtensionFactory->create();
+                        }
+                        foreach ($item['custom_attributes'] as $option) {
+                            $name = $option['attribute_code'];
+                            $value = $option['value'];
+                            $setterName = 'set' . ucfirst($name);
+                            // Check if setter exists
+                            if (method_exists($productLinkExtension, $setterName)) {
+                                call_user_func([$productLinkExtension, $setterName], $value);
+                            }
+                        }
+                        $productLink->setExtensionAttributes($productLinkExtension);
+                    }
                     $output[] = $productLink;
                 }
             }
diff --git a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php
index 94612dbf200a5f95fc003342e0c114d273366be1..b40b91d5b08b8efa59ff0534e04600c1bfcc33d7 100644
--- a/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php
+++ b/app/code/Magento/Catalog/Model/Product/Type/AbstractType.php
@@ -100,6 +100,13 @@ abstract class AbstractType
      */
     protected $_fileStorageDb;
 
+    /**
+     * Cache key for Product Attributes
+     *
+     * @var string
+     */
+    protected $_cacheProductSetAttributes = '_cache_instance_product_set_attributes';
+
     /**
      * Delete data specific for this product type
      *
@@ -249,9 +256,13 @@ abstract class AbstractType
      */
     public function getSetAttributes($product)
     {
-        return $product->getResource()
-            ->loadAllAttributes($product)
-            ->getSortedAttributes($product->getAttributeSetId());
+        if (!$product->hasData($this->_cacheProductSetAttributes)) {
+            $setAttributes = $product->getResource()
+                ->loadAllAttributes($product)
+                ->getSortedAttributes($product->getAttributeSetId());
+            $product->setData($this->_cacheProductSetAttributes, $setAttributes);
+        }
+        return $product->getData($this->_cacheProductSetAttributes);
     }
 
     /**
diff --git a/app/code/Magento/Catalog/Model/ProductLink/Management.php b/app/code/Magento/Catalog/Model/ProductLink/Management.php
index 558561d22de50af6f402f9201a6138aaba441496..e702a6cde0e41f1a8e3316d5a30be821c620fa0b 100644
--- a/app/code/Magento/Catalog/Model/ProductLink/Management.php
+++ b/app/code/Magento/Catalog/Model/ProductLink/Management.php
@@ -7,7 +7,6 @@
 namespace Magento\Catalog\Model\ProductLink;
 
 use Magento\Catalog\Api\Data;
-use Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks as LinksInitializer;
 use Magento\Framework\Exception\CouldNotSaveException;
 use Magento\Framework\Exception\NoSuchEntityException;
 
@@ -18,26 +17,6 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface
      */
     protected $productRepository;
 
-    /**
-     * @var CollectionProvider
-     */
-    protected $entityCollectionProvider;
-
-    /**
-     * @var LinksInitializer
-     */
-    protected $linkInitializer;
-
-    /**
-     * @var \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory
-     */
-    protected $productLinkFactory;
-
-    /**
-     * @var \Magento\Catalog\Model\Resource\Product
-     */
-    protected $productResource;
-
     /**
      * @var \Magento\Catalog\Model\Product\LinkTypeProvider
      */
@@ -45,25 +24,13 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface
 
     /**
      * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
-     * @param CollectionProvider $collectionProvider
-     * @param \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory
-     * @param LinksInitializer $linkInitializer
-     * @param \Magento\Catalog\Model\Resource\Product $productResource
      * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider
      */
     public function __construct(
         \Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
-        CollectionProvider $collectionProvider,
-        \Magento\Catalog\Api\Data\ProductLinkInterfaceFactory $productLinkFactory,
-        LinksInitializer $linkInitializer,
-        \Magento\Catalog\Model\Resource\Product $productResource,
         \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider
     ) {
         $this->productRepository = $productRepository;
-        $this->entityCollectionProvider = $collectionProvider;
-        $this->productLinkFactory = $productLinkFactory;
-        $this->productResource = $productResource;
-        $this->linkInitializer = $linkInitializer;
         $this->linkTypeProvider = $linkTypeProvider;
     }
 
@@ -73,27 +40,22 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface
     public function getLinkedItemsByType($sku, $type)
     {
         $output = [];
-        $product = $this->productRepository->get($sku);
-        try {
-            $collection = $this->entityCollectionProvider->getCollection($product, $type);
-        } catch (NoSuchEntityException $e) {
+
+        $linkTypes = $this->linkTypeProvider->getLinkTypes();
+
+        if (!isset($linkTypes[$type])) {
             throw new NoSuchEntityException(__('Unknown link type: %1', (string)$type));
         }
-        foreach ($collection as $item) {
-            /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $productLink */
-            $productLink = $this->productLinkFactory->create();
-            $productLink->setProductSku($product->getSku())
-                ->setLinkType($type)
-                ->setLinkedProductSku($item['sku'])
-                ->setLinkedProductType($item['type'])
-                ->setPosition($item['position']);
-            if (isset($item['custom_attributes'])) {
-                foreach ($item['custom_attributes'] as $option) {
-                    $productLink->getExtensionAttributes()->setQty($option['value']);
-                }
+        $product = $this->productRepository->get($sku);
+        $links = $product->getProductLinks();
+
+        // Only return the links of type specified
+        foreach ($links as $link) {
+            if ($link->getLinkType() == $type) {
+                $output[] = $link;
             }
-            $output[] = $productLink;
         }
+
         return $output;
     }
 
@@ -111,32 +73,27 @@ class Management implements \Magento\Catalog\Api\ProductLinkManagementInterface
         }
 
         $product = $this->productRepository->get($sku);
-        $assignedSkuList = [];
-        /** @var \Magento\Catalog\Api\Data\ProductLinkInterface $link */
-        foreach ($items as $link) {
-            $assignedSkuList[] = $link->getLinkedProductSku();
-        }
-        $linkedProductIds = $this->productResource->getProductsIdsBySkus($assignedSkuList);
 
-        $links = [];
-        /** @var \Magento\Catalog\Api\Data\ProductLinkInterface[] $items*/
-        foreach ($items as $link) {
-            $data = $link->__toArray();
-            $linkedSku = $link->getLinkedProductSku();
-            if (!isset($linkedProductIds[$linkedSku])) {
-                throw new NoSuchEntityException(
-                    __('Product with SKU "%1" does not exist', $linkedSku)
-                );
+        // Replace only links of the specified type
+        $existingLinks = $product->getProductLinks();
+        $newLinks = [];
+        if (!empty($existingLinks)) {
+            foreach ($existingLinks as $link) {
+                if ($link->getLinkType() != $type) {
+                    $newLinks[] = $link;
+                }
             }
-            $data['product_id'] = $linkedProductIds[$linkedSku];
-            $links[$linkedProductIds[$linkedSku]] = $data;
+            $newLinks = array_merge($newLinks, $items);
+        } else {
+            $newLinks = $items;
         }
-        $this->linkInitializer->initializeLinks($product, [$type => $links]);
+        $product->setProductLinks($newLinks);
         try {
-            $product->save();
+            $this->productRepository->save($product);
         } catch (\Exception $exception) {
             throw new CouldNotSaveException(__('Invalid data provided for linked products'));
         }
+
         return true;
     }
 }
diff --git a/app/code/Magento/Catalog/Model/ProductRepository.php b/app/code/Magento/Catalog/Model/ProductRepository.php
index 40ecc5d2cb3419101374a9d563b52ada19cd9967..8f2e7664c4e3df2c3ebf963cd4122e22a0e730cb 100644
--- a/app/code/Magento/Catalog/Model/ProductRepository.php
+++ b/app/code/Magento/Catalog/Model/ProductRepository.php
@@ -75,6 +75,16 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
      */
     protected $linkInitializer;
 
+    /*
+     * @param \Magento\Catalog\Model\Product\LinkTypeProvider
+     */
+    protected $linkTypeProvider;
+
+    /*
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     */
+    protected $storeManager;
+
     /**
      * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface
      */
@@ -129,6 +139,8 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
      * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository
      * @param Resource\Product $resourceModel
      * @param \Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks $linkInitializer
+     * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager,
      * @param \Magento\Framework\Api\FilterBuilder $filterBuilder
      * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $metadataServiceInterface
      * @param \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter
@@ -149,6 +161,8 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository,
         \Magento\Catalog\Model\Resource\Product $resourceModel,
         \Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks $linkInitializer,
+        \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider,
+        \Magento\Store\Model\StoreManagerInterface $storeManager,
         \Magento\Framework\Api\FilterBuilder $filterBuilder,
         \Magento\Catalog\Api\ProductAttributeRepositoryInterface $metadataServiceInterface,
         \Magento\Framework\Api\ExtensibleDataObjectConverter $extensibleDataObjectConverter,
@@ -166,6 +180,8 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         $this->searchCriteriaBuilder = $searchCriteriaBuilder;
         $this->resourceModel = $resourceModel;
         $this->linkInitializer = $linkInitializer;
+        $this->linkTypeProvider = $linkTypeProvider;
+        $this->storeManager = $storeManager;
         $this->attributeRepository = $attributeRepository;
         $this->filterBuilder = $filterBuilder;
         $this->metadataService = $metadataServiceInterface;
@@ -260,6 +276,9 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
     {
         if ($createNew) {
             $product = $this->productFactory->create();
+            if ($this->storeManager->hasSingleStore()) {
+                $product->setWebsiteIds([$this->storeManager->getStore(true)->getWebsite()->getId()]);
+            }
         } else {
             unset($this->instances[$productData['sku']]);
             $product = $this->get($productData['sku']);
@@ -353,11 +372,12 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
         }
 
         // Clear all existing product links and then set the ones we want
-        $this->linkInitializer->initializeLinks($product, ['related' => []]);
-        $this->linkInitializer->initializeLinks($product, ['upsell' => []]);
-        $this->linkInitializer->initializeLinks($product, ['crosssell' => []]);
+        $linkTypes = $this->linkTypeProvider->getLinkTypes();
+        foreach (array_keys($linkTypes) as $typeName) {
+            $this->linkInitializer->initializeLinks($product, [$typeName => []]);
+        }
 
-        // Gather each linktype info
+        // Set each linktype info
         if (!empty($newLinks)) {
             $productLinks = [];
             foreach ($newLinks as $link) {
diff --git a/app/code/Magento/Catalog/Model/Resource/Category.php b/app/code/Magento/Catalog/Model/Resource/Category.php
index f99d1b9ee9d2c15cdca6c883cc7ded953973afc7..27d7f8b801df56c735259f20150e53aa1a5d7af4 100644
--- a/app/code/Magento/Catalog/Model/Resource/Category.php
+++ b/app/code/Magento/Catalog/Model/Resource/Category.php
@@ -92,13 +92,36 @@ class Category extends AbstractResource
         $this->_categoryTreeFactory = $categoryTreeFactory;
         $this->_categoryCollectionFactory = $categoryCollectionFactory;
         $this->_eventManager = $eventManager;
-        $this->setType(
-            \Magento\Catalog\Model\Category::ENTITY
-        )->setConnection(
-            $this->_resource->getConnection('catalog_read'),
-            $this->_resource->getConnection('catalog_write')
-        );
-        $this->_categoryProductTable = $this->getTable('catalog_category_product');
+
+        $this->_read  = 'catalog_read';
+        $this->_write = 'catalog_write';
+    }
+
+    /**
+     * Entity type getter and lazy loader
+     *
+     * @return \Magento\Eav\Model\Entity\Type
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    public function getEntityType()
+    {
+        if (empty($this->_type)) {
+            $this->setType(\Magento\Catalog\Model\Category::ENTITY);
+        }
+        return parent::getEntityType();
+    }
+
+    /**
+     * Category product table name getter
+     *
+     * @return string
+     */
+    public function getCategoryProductTable()
+    {
+        if (!$this->_categoryProductTable) {
+            $this->_categoryProductTable = $this->getTable('catalog_category_product');
+        }
+        return $this->_categoryProductTable;
     }
 
     /**
@@ -359,7 +382,7 @@ class Category extends AbstractResource
          */
         if (!empty($delete)) {
             $cond = ['product_id IN(?)' => array_keys($delete), 'category_id=?' => $id];
-            $adapter->delete($this->_categoryProductTable, $cond);
+            $adapter->delete($this->getCategoryProductTable(), $cond);
         }
 
         /**
@@ -374,7 +397,7 @@ class Category extends AbstractResource
                     'position' => (int)$position,
                 ];
             }
-            $adapter->insertMultiple($this->_categoryProductTable, $data);
+            $adapter->insertMultiple($this->getCategoryProductTable(), $data);
         }
 
         /**
@@ -384,7 +407,7 @@ class Category extends AbstractResource
             foreach ($update as $productId => $position) {
                 $where = ['category_id = ?' => (int)$id, 'product_id = ?' => (int)$productId];
                 $bind = ['position' => (int)$position];
-                $adapter->update($this->_categoryProductTable, $bind, $where);
+                $adapter->update($this->getCategoryProductTable(), $bind, $where);
             }
         }
 
@@ -417,7 +440,7 @@ class Category extends AbstractResource
     public function getProductsPosition($category)
     {
         $select = $this->_getWriteAdapter()->select()->from(
-            $this->_categoryProductTable,
+            $this->getCategoryProductTable(),
             ['product_id', 'position']
         )->where(
             'category_id = :category_id'
diff --git a/app/code/Magento/Catalog/Model/Resource/Category/Collection.php b/app/code/Magento/Catalog/Model/Resource/Category/Collection.php
index 2abc203eccbcada147503ce7f5be4400deb898e8..f21ce9f363b51f01a7555d79e3ba39aa89598b53 100644
--- a/app/code/Magento/Catalog/Model/Resource/Category/Collection.php
+++ b/app/code/Magento/Catalog/Model/Resource/Category/Collection.php
@@ -33,7 +33,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
      *
      * @var string
      */
-    protected $_productTable;
+    private $_productTable;
 
     /**
      * Store id, that we should count products on
@@ -47,7 +47,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
      *
      * @var string
      */
-    protected $_productWebsiteTable;
+    private $_productWebsiteTable;
 
     /**
      * Load with product count flag
@@ -64,9 +64,6 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
     protected function _construct()
     {
         $this->_init('Magento\Catalog\Model\Category', 'Magento\Catalog\Model\Resource\Category');
-
-        $this->_productWebsiteTable = $this->getTable('catalog_product_website');
-        $this->_productTable = $this->getTable('catalog_category_product');
     }
 
     /**
@@ -224,7 +221,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
             if (!empty($regularIds)) {
                 $select = $this->_conn->select();
                 $select->from(
-                    ['main_table' => $this->_productTable],
+                    ['main_table' => $this->getProductTable()],
                     ['category_id', new \Zend_Db_Expr('COUNT(main_table.product_id)')]
                 )->where(
                     $this->_conn->quoteInto('main_table.category_id IN(?)', $regularIds)
@@ -233,7 +230,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
                 );
                 if ($websiteId) {
                     $select->join(
-                        ['w' => $this->_productWebsiteTable],
+                        ['w' => $this->getProductWebsiteTable()],
                         'main_table.product_id = w.product_id',
                         []
                     )->where(
@@ -259,7 +256,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
                     $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%'];
                     $select = $this->_conn->select();
                     $select->from(
-                        ['main_table' => $this->_productTable],
+                        ['main_table' => $this->getProductTable()],
                         new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)')
                     )->joinInner(
                         ['e' => $this->getTable('catalog_category_entity')],
@@ -272,7 +269,7 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
                     );
                     if ($websiteId) {
                         $select->join(
-                            ['w' => $this->_productWebsiteTable],
+                            ['w' => $this->getProductWebsiteTable()],
                             'main_table.product_id = w.product_id',
                             []
                         )->where(
@@ -416,4 +413,30 @@ class Collection extends \Magento\Catalog\Model\Resource\Collection\AbstractColl
         $this->setOrder($field, self::SORT_ORDER_ASC);
         return $this;
     }
+
+    /**
+     * Getter for _productWebsiteTable
+     *
+     * @return string
+     */
+    public function getProductWebsiteTable()
+    {
+        if (empty($this->_productWebsiteTable)) {
+            $this->_productWebsiteTable = $this->getTable('catalog_product_website');
+        }
+        return $this->_productWebsiteTable;
+    }
+
+    /**
+     * Getter for _productTable
+     *
+     * @return string
+     */
+    public function getProductTable()
+    {
+        if (empty($this->_productTable)) {
+            $this->_productTable = $this->getTable('catalog_category_product');
+        }
+        return $this->_productTable;
+    }
 }
diff --git a/app/code/Magento/Catalog/Model/Resource/Product.php b/app/code/Magento/Catalog/Model/Resource/Product.php
index 336d8f796577ecabad0284932cc213cabcfb5f28..6f02829d2bc2a7dff4e95b39b7cd20181d5d1903 100644
--- a/app/code/Magento/Catalog/Model/Resource/Product.php
+++ b/app/code/Magento/Catalog/Model/Resource/Product.php
@@ -91,9 +91,48 @@ class Product extends AbstractResource
             $modelFactory,
             $data
         );
-        $this->setType(\Magento\Catalog\Model\Product::ENTITY)->setConnection('catalog_read', 'catalog_write');
-        $this->_productWebsiteTable = $this->getTable('catalog_product_website');
-        $this->_productCategoryTable = $this->getTable('catalog_category_product');
+        $this->_read  = 'catalog_read';
+        $this->_write = 'catalog_write';
+    }
+
+    /**
+     * Entity type getter and lazy loader
+     *
+     * @return \Magento\Eav\Model\Entity\Type
+     * @throws \Magento\Framework\Exception\LocalizedException
+     */
+    public function getEntityType()
+    {
+        if (empty($this->_type)) {
+            $this->setType(\Magento\Catalog\Model\Product::ENTITY);
+        }
+        return parent::getEntityType();
+    }
+
+    /**
+     * Product Website table name getter
+     *
+     * @return string
+     */
+    public function getProductWebsiteTable()
+    {
+        if (!$this->_productWebsiteTable) {
+            $this->_productWebsiteTable = $this->getTable('catalog_product_website');
+        }
+        return $this->_productWebsiteTable;
+    }
+
+    /**
+     * Product Category table name getter
+     *
+     * @return string
+     */
+    public function getProductCategoryTable()
+    {
+        if (!$this->_productCategoryTable) {
+            $this->_productCategoryTable = $this->getTable('catalog_category_product');
+        }
+        return $this->_productCategoryTable;
     }
 
     /**
@@ -123,7 +162,7 @@ class Product extends AbstractResource
         }
 
         $select = $adapter->select()->from(
-            $this->_productWebsiteTable,
+            $this->getProductWebsiteTable(),
             'website_id'
         )->where(
             'product_id = ?',
@@ -142,7 +181,7 @@ class Product extends AbstractResource
     public function getWebsiteIdsByProductIds($productIds)
     {
         $select = $this->_getWriteAdapter()->select()->from(
-            $this->_productWebsiteTable,
+            $this->getProductWebsiteTable(),
             ['product_id', 'website_id']
         )->where(
             'product_id IN (?)',
@@ -171,7 +210,7 @@ class Product extends AbstractResource
         $adapter = $this->_getReadAdapter();
 
         $select = $adapter->select()->from(
-            $this->_productCategoryTable,
+            $this->getProductCategoryTable(),
             'category_id'
         )->where(
             'product_id = ?',
@@ -274,14 +313,14 @@ class Product extends AbstractResource
             foreach ($insert as $websiteId) {
                 $data[] = ['product_id' => (int)$product->getId(), 'website_id' => (int)$websiteId];
             }
-            $adapter->insertMultiple($this->_productWebsiteTable, $data);
+            $adapter->insertMultiple($this->getProductWebsiteTable(), $data);
         }
 
         if (!empty($delete)) {
             foreach ($delete as $websiteId) {
                 $condition = ['product_id = ?' => (int)$product->getId(), 'website_id = ?' => (int)$websiteId];
 
-                $adapter->delete($this->_productWebsiteTable, $condition);
+                $adapter->delete($this->getProductWebsiteTable(), $condition);
             }
         }
 
@@ -329,7 +368,7 @@ class Product extends AbstractResource
                 ];
             }
             if ($data) {
-                $write->insertMultiple($this->_productCategoryTable, $data);
+                $write->insertMultiple($this->getProductCategoryTable(), $data);
             }
         }
 
@@ -337,7 +376,7 @@ class Product extends AbstractResource
             foreach ($delete as $categoryId) {
                 $where = ['product_id = ?' => (int)$object->getId(), 'category_id = ?' => (int)$categoryId];
 
-                $write->delete($this->_productCategoryTable, $where);
+                $write->delete($this->getProductCategoryTable(), $where);
             }
         }
 
diff --git a/app/code/Magento/Catalog/Model/Session.php b/app/code/Magento/Catalog/Model/Session.php
index b61175b11e7da3ff879834074f303590b664b72b..d7a3748c2810bd82a6b856e4d7dfe814cd0401ab 100644
--- a/app/code/Magento/Catalog/Model/Session.php
+++ b/app/code/Magento/Catalog/Model/Session.php
@@ -8,6 +8,6 @@ namespace Magento\Catalog\Model;
 /**
  * Catalog session model
  */
-class Session extends \Magento\Framework\Session\Generic
+class Session extends \Magento\Framework\Session\SessionManager
 {
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
index df66448c160372d82452ebe45b9ac68b7bb08749..d4501bd7d04393062a693c3491482132a7565d7d 100644
--- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ProductList/ToolbarTest.php
@@ -106,9 +106,6 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->catalogConfig->expects($this->any())
-            ->method('getAttributeUsedForSortByArray')
-            ->will($this->returnValue(['name' => [], 'price' => []]));
 
         $context = $this->getMock(
             'Magento\Framework\View\Element\Template\Context',
@@ -133,9 +130,6 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $this->productListHelper->expects($this->any())
-            ->method('getAvailableViewMode')
-            ->will($this->returnValue(['list' => 'List']));
 
         $this->urlEncoder = $this->getMock('Magento\Framework\Url\EncoderInterface', ['encode'], [], '', false);
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
@@ -187,6 +181,9 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase
         $this->model->expects($this->once())
             ->method('getOrder')
             ->will($this->returnValue($order));
+        $this->catalogConfig->expects($this->once())
+            ->method('getAttributeUsedForSortByArray')
+            ->will($this->returnValue(['name' => [], 'price' => []]));
 
         $this->assertEquals($order, $this->block->getCurrentOrder());
     }
@@ -206,6 +203,9 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase
     {
         $mode = 'list';
 
+        $this->productListHelper->expects($this->once())
+            ->method('getAvailableViewMode')
+            ->will($this->returnValue(['list' => 'List']));
         $this->model->expects($this->once())
             ->method('getMode')
             ->will($this->returnValue($mode));
@@ -213,6 +213,40 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($mode, $this->block->getCurrentMode());
     }
 
+    public function testGetModes()
+    {
+        $mode = ['list' => 'List'];
+        $this->productListHelper->expects($this->once())
+            ->method('getAvailableViewMode')
+            ->will($this->returnValue($mode));
+
+        $this->assertEquals($mode, $this->block->getModes());
+        $this->assertEquals($mode, $this->block->getModes());
+    }
+
+    /**
+     * @param string[] $mode
+     * @param string[] $expected
+     * @dataProvider setModesDataProvider
+     */
+    public function testSetModes($mode, $expected)
+    {
+        $this->productListHelper->expects($this->once())
+            ->method('getAvailableViewMode')
+            ->will($this->returnValue($mode));
+
+        $block = $this->block->setModes(['mode' => 'mode']);
+        $this->assertEquals($expected, $block->getModes());
+    }
+
+    public function setModesDataProvider()
+    {
+        return [
+            [['list' => 'List'], ['list' => 'List']],
+            [null, ['mode' => 'mode']],
+        ];
+    }
+
     public function testGetLimit()
     {
         $mode = 'list';
@@ -232,6 +266,9 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase
             ->method('getDefaultLimitPerPageValue')
             ->with($this->equalTo('list'))
             ->will($this->returnValue(10));
+        $this->productListHelper->expects($this->any())
+            ->method('getAvailableViewMode')
+            ->will($this->returnValue(['list' => 'List']));
 
         $this->assertEquals($limit, $this->block->getLimit());
     }
@@ -280,4 +317,48 @@ class ToolbarTest extends \PHPUnit_Framework_TestCase
 
         $this->assertTrue($this->block->getPagerHtml());
     }
+
+    public function testSetDefaultOrder()
+    {
+        $this->catalogConfig->expects($this->atLeastOnce())
+            ->method('getAttributeUsedForSortByArray')
+            ->will($this->returnValue(['name' => [], 'price' => []]));
+
+        $this->block->setDefaultOrder('field');
+    }
+
+    public function testGetAvailableOrders()
+    {
+        $data = ['name' => [], 'price' => []];
+        $this->catalogConfig->expects($this->once())
+            ->method('getAttributeUsedForSortByArray')
+            ->will($this->returnValue($data));
+
+        $this->assertEquals($data, $this->block->getAvailableOrders());
+        $this->assertEquals($data, $this->block->getAvailableOrders());
+    }
+
+    public function testAddOrderToAvailableOrders()
+    {
+        $data = ['name' => [], 'price' => []];
+        $this->catalogConfig->expects($this->once())
+            ->method('getAttributeUsedForSortByArray')
+            ->will($this->returnValue($data));
+        $expected = $data;
+        $expected['order'] = 'value';
+        $toolbar = $this->block->addOrderToAvailableOrders('order', 'value');
+        $this->assertEquals($expected, $toolbar->getAvailableOrders());
+    }
+
+    public function testRemoveOrderFromAvailableOrders()
+    {
+        $data = ['name' => [], 'price' => []];
+        $this->catalogConfig->expects($this->once())
+            ->method('getAttributeUsedForSortByArray')
+            ->will($this->returnValue($data));
+        $toolbar = $this->block->removeOrderFromAvailableOrders('order', 'value');
+        $this->assertEquals($data, $toolbar->getAvailableOrders());
+        $toolbar2 = $this->block->removeOrderFromAvailableOrders('name');
+        $this->assertEquals(['price' => []], $toolbar2->getAvailableOrders());
+    }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php
index 75149c26c8d001cf51956b2ca893ecb326f801d4..0ce39e12fbdc93ee8f2e1d38eff837c5a9b25514 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/Product/Type/AbstractTypeTest.php
@@ -119,11 +119,12 @@ class AbstractTypeTest extends \PHPUnit_Framework_TestCase
 
     public function testGetSetAttributes()
     {
-        $this->productResource->expects($this->any())->method('loadAllAttributes')->will(
+        $this->productResource->expects($this->once())->method('loadAllAttributes')->will(
             $this->returnValue($this->productResource)
         );
-        $this->productResource->expects($this->any())->method('getSortedAttributes')->will($this->returnValue(5));
-        $this->model->getSetAttributes($this->product);
+        $this->productResource->expects($this->once())->method('getSortedAttributes')->will($this->returnValue(5));
+        $this->assertEquals(5, $this->model->getSetAttributes($this->product));
+        //Call the method for a second time, the cached copy should be used
         $this->assertEquals(5, $this->model->getSetAttributes($this->product));
     }
 
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php
index ed28dc6fda78135af0e50f4e57d7db7c656dac67..92c2026f8961c258516299ba309da2cb811c145c 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductLink/ManagementTest.php
@@ -26,58 +26,21 @@ class ManagementTest extends \PHPUnit_Framework_TestCase
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
-    protected $collectionProviderMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $linkInitializerMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $productLinkFactoryMock;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $productResourceMock;
+    protected $productMock;
 
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
-    protected $productMock;
+    protected $linkTypeProviderMock;
 
     /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
+     * @var \Magento\Framework\ObjectManager
      */
-    protected $linkTypeProviderMock;
+    protected $objectManager;
 
     protected function setUp()
     {
         $this->productRepositoryMock = $this->getMock('\Magento\Catalog\Model\ProductRepository', [], [], '', false);
-        $this->productResourceMock = $this->getMock('\Magento\Catalog\Model\Resource\Product', [], [], '', false);
-        $this->collectionProviderMock = $this->getMock(
-            '\Magento\Catalog\Model\ProductLink\CollectionProvider',
-            [],
-            [],
-            '',
-            false
-        );
-        $this->linkInitializerMock = $this->getMock(
-            '\Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks',
-            [],
-            [],
-            '',
-            false
-        );
-        $this->productLinkFactoryMock = $this->getMock(
-            '\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory',
-            ['create'],
-            [],
-            '',
-            false
-        );
         $this->productMock = $this->getMock('Magento\Catalog\Model\Product', [], [], '', false);
 
         $this->linkTypeProviderMock = $this->getMock(
@@ -88,15 +51,11 @@ class ManagementTest extends \PHPUnit_Framework_TestCase
             false
         );
 
-        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-        $this->model = $objectManager->getObject(
+        $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $this->objectManager->getObject(
             'Magento\Catalog\Model\ProductLink\Management',
             [
                 'productRepository' => $this->productRepositoryMock,
-                'collectionProvider' => $this->collectionProviderMock,
-                'productLinkFactory' => $this->productLinkFactoryMock,
-                'linkInitializer' => $this->linkInitializerMock,
-                'productResource' => $this->productResourceMock,
                 'linkTypeProvider' => $this->linkTypeProviderMock
             ]
         );
@@ -104,163 +63,135 @@ class ManagementTest extends \PHPUnit_Framework_TestCase
 
     public function testGetLinkedItemsByType()
     {
-        $productSku = 'product';
-        $linkType = 'link';
+        $productSku = 'Simple Product 1';
+        $linkType = 'related';
         $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku)
             ->willReturn($this->productMock);
-        $item = [
-            'sku' => 'product1',
-            'type' => 'type1',
-            'position' => 'pos1',
-        ];
-        $itemCollection = [$item];
-        $this->collectionProviderMock->expects($this->once())
-            ->method('getCollection')
-            ->with($this->productMock, $linkType)
-            ->willReturn($itemCollection);
-        $this->productMock->expects($this->once())->method('getSku')->willReturn($productSku);
-        $productLinkMock = $this->getMock('\Magento\Catalog\Api\Data\ProductLinkInterface');
-        $productLinkMock->expects($this->once())
-            ->method('setProductSku')
-            ->with($productSku)
-            ->willReturnSelf();
-        $productLinkMock->expects($this->once())
-            ->method('setLinkType')
-            ->with($linkType)
-            ->willReturnSelf();
-        $productLinkMock->expects($this->once())
-            ->method('setLinkedProductSku')
-            ->with($item['sku'])
-            ->willReturnSelf();
-        $productLinkMock->expects($this->once())
-            ->method('setLinkedProductType')
-            ->with($item['type'])
-            ->willReturnSelf();
-        $productLinkMock->expects($this->once())
-            ->method('setPosition')
-            ->with($item['position'])
-            ->willReturnSelf();
-        $this->productLinkFactoryMock->expects($this->once())->method('create')->willReturn($productLinkMock);
-        $this->assertEquals([$productLinkMock], $this->model->getLinkedItemsByType($productSku, $linkType));
+
+        $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku($productSku);
+        $inputRelatedLink->setLinkType($linkType);
+        $inputRelatedLink->setData("sku", "Simple Product 2");
+        $inputRelatedLink->setData("type_id", "simple");
+        $inputRelatedLink->setPosition(0);
+        $links = [$inputRelatedLink];
+
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $this->productMock->expects($this->once())->method('getProductLinks')->willReturn($links);
+        $this->assertEquals($links, $this->model->getLinkedItemsByType($productSku, $linkType));
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\NoSuchEntityException
-     * @expectedExceptionMessage Unknown link type: wrong_type
+     * @expectedExceptionMessage Unknown link type: bad type
      */
     public function testGetLinkedItemsByTypeWithWrongType()
     {
-        $productSku = 'product';
-        $linkType = 'wrong_type';
-
-        $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku)
+        $productSku = 'Simple Product 1';
+        $linkType = 'bad type';
+        $this->productRepositoryMock->expects($this->never())->method('get')->with($productSku)
             ->willReturn($this->productMock);
-        $this->collectionProviderMock->expects($this->once())
-            ->method('getCollection')
-            ->with($this->productMock, $linkType)
-            ->willThrowException(new NoSuchEntityException(__('Collection provider is not registered')));
 
+        $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku($productSku);
+        $inputRelatedLink->setLinkType($linkType);
+        $inputRelatedLink->setData("sku", "Simple Product 2");
+        $inputRelatedLink->setData("type_id", "simple");
+        $inputRelatedLink->setPosition(0);
+        $links = [$inputRelatedLink];
+
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $this->productMock->expects($this->never())->method('getProductLinks')->willReturn($links);
         $this->model->getLinkedItemsByType($productSku, $linkType);
     }
 
     public function testSetProductLinks()
     {
-        $type = 'type';
-        $linkedProductsMock = [];
-        $linksData = [];
-
-        $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']);
-        for ($i = 0; $i < 2; $i++) {
-            $linkMock = $this->getMockForAbstractClass(
-                '\Magento\Catalog\Api\Data\ProductLinkInterface',
-                [],
-                '',
-                false,
-                false,
-                true,
-                ['getLinkedProductSku', '__toArray']
-            );
-            $linkMock->expects($this->exactly(2))
-                ->method('getLinkedProductSku')
-                ->willReturn('linkedProduct' . $i . 'Sku');
-            $linkMock->expects($this->once())->method('__toArray');
-            $linkedProductsMock[$i] = $linkMock;
-            $linksData['productSku']['link'][] = $linkMock;
-        }
-        $linkedSkuList = ['linkedProduct0Sku', 'linkedProduct1Sku'];
-        $linkedProductIds = ['linkedProduct0Sku' => 1, 'linkedProduct1Sku' => 2];
-
-        $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus')->with($linkedSkuList)
-            ->willReturn($linkedProductIds);
-        $this->productRepositoryMock->expects($this->once())->method('get')->willReturn($this->productMock);
-        $this->linkInitializerMock->expects($this->once())->method('initializeLinks')
-            ->with($this->productMock, [$type => [
-                1 => ['product_id' => 1],
-                2 => ['product_id' => 2],
-            ]]);
-        $this->productMock->expects($this->once())->method('save');
-        $this->assertTrue($this->model->setProductLinks('', $type, $linkedProductsMock));
+        $productSku = 'Simple Product 1';
+        $linkType = 'related';
+        $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku)
+            ->willReturn($this->productMock);
+
+        $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku($productSku);
+        $inputRelatedLink->setLinkType($linkType);
+        $inputRelatedLink->setData("sku", "Simple Product 1");
+        $inputRelatedLink->setData("type_id", "related");
+        $inputRelatedLink->setPosition(0);
+        $links = [$inputRelatedLink];
+
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $this->productMock->expects($this->once())->method('getProductLinks')->willReturn([]);
+        $this->productMock->expects($this->once())->method('setProductLinks')->with($links);
+        $this->assertTrue($this->model->setProductLinks($productSku, $linkType, $links));
     }
 
     /**
      * @expectedException \Magento\Framework\Exception\NoSuchEntityException
-     * @expectedExceptionMessage Provided link type "type2" does not exist
+     * @expectedExceptionMessage Provided link type "bad type" does not exist
      */
     public function testSetProductLinksThrowExceptionIfProductLinkTypeDoesNotExist()
     {
-        $type = 'type';
-        $linkedProductsMock = [];
+        $productSku = 'Simple Product 1';
+        $linkType = 'bad type';
+        $this->productRepositoryMock->expects($this->never())->method('get')->with($productSku)
+            ->willReturn($this->productMock);
+
+        $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku($productSku);
+        $inputRelatedLink->setLinkType($linkType);
+        $inputRelatedLink->setData("sku", "Simple Product 2");
+        $inputRelatedLink->setData("type_id", "simple");
+        $inputRelatedLink->setPosition(0);
+        $links = [$inputRelatedLink];
 
-        $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']);
-        $this->assertTrue($this->model->setProductLinks('', 'type2', $linkedProductsMock));
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $this->assertTrue($this->model->setProductLinks('', $linkType, $links));
     }
 
     /**
-     * @dataProvider setProductLinksNoProductExceptionDataProvider
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedExceptionMessage Requested product doesn't exist
      */
-    public function testSetProductLinksNoProductException($exceptionName, $exceptionMessage, $linkedProductIds)
+    public function testSetProductLinksNoProductException()
     {
-        $this->setExpectedException($exceptionName, $exceptionMessage);
-        $linkedProductsMock = [];
-        $type = 'type';
-        $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']);
-        for ($i = 0; $i < 2; $i++) {
-            $productLinkMock = $this->getMock(
-                '\Magento\Catalog\Api\Data\ProductLinkInterface',
-                [
-                    'getLinkedProductSku', 'getProductSku', 'getLinkType',
-                    '__toArray', 'getLinkedProductType', 'getPosition', 'getCustomAttribute', 'getCustomAttributes',
-                    'setCustomAttribute', 'setCustomAttributes', 'getMetadataServiceInterface',
-                    'getExtensionAttributes', 'setExtensionAttributes',
-                    'setLinkedProductSku', 'setProductSku', 'setLinkType', 'setLinkedProductType', 'setPosition'
-                ]
-            );
-            $productLinkMock->expects($this->any())
-                ->method('getLinkedProductSku')
-                ->willReturn('linkedProduct' . $i . 'Sku');
-            $productLinkMock->expects($this->any())->method('getProductSku')->willReturn('productSku');
-            $productLinkMock->expects($this->any())->method('getLinkType')->willReturn('link');
-            $linkedProductsMock[$i] = $productLinkMock;
-        }
-        $linkedSkuList = ['linkedProduct0Sku', 'linkedProduct1Sku'];
-        $this->productResourceMock->expects($this->any())->method('getProductsIdsBySkus')->with($linkedSkuList)
-            ->willReturn($linkedProductIds);
-        $this->model->setProductLinks('', $type, $linkedProductsMock);
-    }
-
-    public function setProductLinksNoProductExceptionDataProvider()
-    {
-        return [
-            [
-                '\Magento\Framework\Exception\NoSuchEntityException',
-                'Product with SKU "linkedProduct0Sku" does not exist',
-                ['linkedProduct1Sku' => 2],
-            ], [
-                '\Magento\Framework\Exception\NoSuchEntityException',
-                'Product with SKU "linkedProduct1Sku" does not exist',
-                ['linkedProduct0Sku' => 1]
-            ]
-        ];
+        $productSku = 'Simple Product 1';
+        $linkType = 'related';
+
+        $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku($productSku);
+        $inputRelatedLink->setLinkType($linkType);
+        $inputRelatedLink->setData("sku", "Simple Product 2");
+        $inputRelatedLink->setData("type_id", "simple");
+        $inputRelatedLink->setPosition(0);
+        $links = [$inputRelatedLink];
+
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->will($this->throwException(
+                new \Magento\Framework\Exception\NoSuchEntityException(__('Requested product doesn\'t exist'))));
+        $this->model->setProductLinks($productSku, $linkType, $links);
     }
 
     /**
@@ -269,39 +200,27 @@ class ManagementTest extends \PHPUnit_Framework_TestCase
      */
     public function testSetProductLinksInvalidDataException()
     {
-        $type = 'type';
-        $linkedProductsMock = [];
-        $linksData = [];
-        $this->linkTypeProviderMock->expects($this->once())->method('getLinkTypes')->willReturn([$type => 'link']);
-        for ($i = 0; $i < 2; $i++) {
-            $linkMock = $this->getMockForAbstractClass(
-                '\Magento\Catalog\Api\Data\ProductLinkInterface',
-                [],
-                '',
-                false,
-                false,
-                true,
-                ['getLinkedProductSku', '__toArray']
-            );
-            $linkMock->expects($this->exactly(2))
-                ->method('getLinkedProductSku')
-                ->willReturn('linkedProduct' . $i . 'Sku');
-            $linkMock->expects($this->once())->method('__toArray');
-            $linkedProductsMock[$i] = $linkMock;
-            $linksData['productSku']['link'][] = $linkMock;
-        }
-        $linkedSkuList = ['linkedProduct0Sku', 'linkedProduct1Sku'];
-        $linkedProductIds = ['linkedProduct0Sku' => 1, 'linkedProduct1Sku' => 2];
-
-        $this->productResourceMock->expects($this->once())->method('getProductsIdsBySkus')->with($linkedSkuList)
-            ->willReturn($linkedProductIds);
-        $this->productRepositoryMock->expects($this->once())->method('get')->willReturn($this->productMock);
-        $this->linkInitializerMock->expects($this->once())->method('initializeLinks')
-            ->with($this->productMock, [$type => [
-                1 => ['product_id' => 1],
-                2 => ['product_id' => 2],
-            ]]);
-        $this->productMock->expects($this->once())->method('save')->willThrowException(new \Exception());
-        $this->model->setProductLinks('', $type, $linkedProductsMock);
+        $productSku = 'Simple Product 1';
+        $linkType = 'related';
+        $this->productRepositoryMock->expects($this->once())->method('get')->with($productSku)
+            ->willReturn($this->productMock);
+
+        $inputRelatedLink = $this->objectManager->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku($productSku);
+        $inputRelatedLink->setLinkType($linkType);
+        $inputRelatedLink->setData("sku", "bad sku");
+        $inputRelatedLink->setData("type_id", "bad type");
+        $inputRelatedLink->setPosition(0);
+        $links = [$inputRelatedLink];
+
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $this->productMock->expects($this->once())->method('getProductLinks')->willReturn([]);
+
+        $this->productRepositoryMock->expects($this->once())->method('save')->willThrowException(new \Exception());
+        $this->model->setProductLinks($productSku, $linkType, $links);
     }
 }
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
index a8ba184519d797718c4613f9e5f4c4c8690764bf..35b260532553ee98240c049b12e1116449fbc9d9 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductRepositoryTest.php
@@ -111,11 +111,19 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $contentValidatorMock;
 
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $linkTypeProviderMock;
+
     /**
      * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
      */
     protected $objectManager;
 
+    /**
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
     protected function setUp()
     {
         $this->productFactoryMock = $this->getMock('Magento\Catalog\Model\ProductFactory', ['create'], [], '', false);
@@ -194,6 +202,8 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
             ->disableOriginalConstructor()
             ->getMock();
         $optionConverter = $this->objectManager->getObject('Magento\Catalog\Model\Product\Option\Converter');
+        $this->linkTypeProviderMock = $this->getMock('Magento\Catalog\Model\Product\LinkTypeProvider',
+            ['getLinkTypes'], [], '', false);
         $this->model = $this->objectManager->getObject(
             'Magento\Catalog\Model\ProductRepository',
             [
@@ -212,6 +222,7 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
                 'fileSystem' => $this->fileSystemMock,
                 'contentFactory' => $this->contentFactoryMock,
                 'mimeTypeExtensionMap' => $this->mimeTypeExtensionMapMock,
+                'linkTypeProvider' => $this->linkTypeProviderMock
             ]
         );
     }
@@ -847,6 +858,11 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->initializedProductMock->setData("product_links", $existingLinks);
 
         if (!empty($newLinks)) {
+            $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+            $this->linkTypeProviderMock->expects($this->once())
+                ->method('getLinkTypes')
+                ->willReturn($linkTypes);
+
             $this->initializedProductMock->setData("ignore_links_flag", false);
             $this->resourceModelMock
                 ->expects($this->any())->method('getProductsIdsBySkus')
@@ -859,6 +875,10 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
             $inputLink->setLinkedProductType($newLinks['linked_product_type']);
             $inputLink->setPosition($newLinks['position']);
 
+            if (isset($newLinks['qty'])) {
+                $inputLink->setQty($newLinks['qty']);
+            }
+
             $this->productData['product_links'] = [$inputLink];
 
             $this->initializedProductMock->expects($this->any())
@@ -898,6 +918,9 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
                 $outputLink->setLinkedProductSku($link['linked_product_sku']);
                 $outputLink->setLinkedProductType($link['linked_product_type']);
                 $outputLink->setPosition($link['position']);
+                if (isset($link['qty'])) {
+                    $outputLink->setQty($link['qty']);
+                }
 
                 $outputLinks[] = $outputLink;
             }
@@ -913,11 +936,11 @@ class ProductRepositoryTest extends \PHPUnit_Framework_TestCase
         // Scenario 1
         // No existing, new links
         $data['scenario_1'] = [
-            'newLinks' => ["product_sku" => "Simple Product 1", "link_type" => "related", "linked_product_sku" =>
-                "Simple Product 2", "linked_product_type" => "simple", "position" => 0],
+            'newLinks' => ["product_sku" => "Simple Product 1", "link_type" => "associated", "linked_product_sku" =>
+                "Simple Product 2", "linked_product_type" => "simple", "position" => 0, "qty" => 1],
             'existingLinks' => [],
-            'expectedData' => [["product_sku" => "Simple Product 1", "link_type" => "related", "linked_product_sku" =>
-                "Simple Product 2", "linked_product_type" => "simple", "position" => 0]]
+            'expectedData' => [["product_sku" => "Simple Product 1", "link_type" => "associated", "linked_product_sku" =>
+                "Simple Product 2", "linked_product_type" => "simple", "position" => 0, "qty" => 1]]
             ];
 
         // Scenario 2
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php
index 4dcd067721f5bc239c97b1aa0141e694fb8a7f07..4cc87c7011c705075f1cb321657748006efc6aa6 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php
@@ -146,6 +146,16 @@ class ProductTest extends \PHPUnit_Framework_TestCase
      */
     protected $attributeValueFactory;
 
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $linkTypeProviderMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $entityCollectionProviderMock;
+
     /**
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
      */
@@ -283,6 +293,11 @@ class ProductTest extends \PHPUnit_Framework_TestCase
         $this->metadataServiceMock = $this->getMock('\Magento\Catalog\Api\ProductAttributeRepositoryInterface');
         $this->attributeValueFactory = $this->getMockBuilder('Magento\Framework\Api\AttributeValueFactory')
             ->disableOriginalConstructor()->getMock();
+        $this->linkTypeProviderMock = $this->getMock('Magento\Catalog\Model\Product\LinkTypeProvider',
+            ['getLinkTypes'], [], '', false);
+        $this->entityCollectionProviderMock = $this->getMock('Magento\Catalog\Model\ProductLink\CollectionProvider',
+            ['getCollection'], [], '', false);
+
         $this->objectManagerHelper = new ObjectManagerHelper($this);
         $this->model = $this->objectManagerHelper->getObject(
             'Magento\Catalog\Model\Product',
@@ -306,6 +321,8 @@ class ProductTest extends \PHPUnit_Framework_TestCase
                 'mediaGalleryEntryFactory' => $this->mediaGalleryEntryFactoryMock,
                 'metadataService' => $this->metadataServiceMock,
                 'customAttributeFactory' => $this->attributeValueFactory,
+                'entityCollectionProvider' => $this->entityCollectionProviderMock,
+                'linkTypeProvider' => $this->linkTypeProviderMock,
                 'data' => ['id' => 1]
             ]
         );
@@ -737,37 +754,43 @@ class ProductTest extends \PHPUnit_Framework_TestCase
      */
     public function testGetProductLinks()
     {
-        $inputLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
-        $inputLink->setProductSku("Simple Product 1");
-        $inputLink->setLinkType("related");
-        $inputLink->setData("sku", "Simple Product 2");
-        $inputLink->setData("type_id", "simple");
-        $inputLink->setPosition(0);
-
-        $outputLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
-        $outputLink->setProductSku("Simple Product 1");
-        $outputLink->setLinkType("related");
-        $outputLink->setLinkedProductSku("Simple Product 2");
-        $outputLink->setLinkedProductType("simple");
-        $outputLink->setPosition(0);
-
-        $productLinks = [];
-        $this->model->setData('related_products', [$inputLink]);
-        $productLinks[] = $outputLink;
-        $outputLink->setLinkType("upsell");
-        $inputLink->setLinkType("upsell");
-        $this->model->setData('up_sell_products', [$inputLink]);
-        $productLinks[] = $outputLink;
-        $outputLink->setLinkType("crosssell");
-        $inputLink->setLinkType("crosssell");
-        $this->model->setData('cross_sell_products', [$inputLink]);
-        $productLinks[] = $outputLink;
-
-        $productLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
-        $this->productLinkFactory->expects($this->atLeastOnce())
-            ->method('create')
-            ->willReturn($productLink);
-
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $inputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku("Simple Product 1");
+        $inputRelatedLink->setLinkType("related");
+        $inputRelatedLink->setData("sku", "Simple Product 2");
+        $inputRelatedLink->setData("type", "simple");
+        $inputRelatedLink->setPosition(0);
+
+        $outputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $outputRelatedLink->setProductSku("Simple Product 1");
+        $outputRelatedLink->setLinkType("related");
+        $outputRelatedLink->setLinkedProductSku("Simple Product 2");
+        $outputRelatedLink->setLinkedProductType("simple");
+        $outputRelatedLink->setPosition(0);
+
+        $this->entityCollectionProviderMock->expects($this->at(0))
+            ->method('getCollection')
+            ->with($this->model, 'related')
+            ->willReturn([$inputRelatedLink]);
+        $this->entityCollectionProviderMock->expects($this->at(1))
+            ->method('getCollection')
+            ->with($this->model, 'upsell')
+            ->willReturn([]);
+        $this->entityCollectionProviderMock->expects($this->at(2))
+            ->method('getCollection')
+            ->with($this->model, 'crosssell')
+            ->willReturn([]);
+        $this->entityCollectionProviderMock->expects($this->at(3))
+            ->method('getCollection')
+            ->with($this->model, 'associated')
+            ->willReturn([]);
+
+        $expectedOutput = [$outputRelatedLink];
         $typeInstanceMock = $this->getMock(
             'Magento\ConfigurableProduct\Model\Product\Type\Simple', ["getSku"], [], '', false);
         $typeInstanceMock
@@ -776,8 +799,13 @@ class ProductTest extends \PHPUnit_Framework_TestCase
             ->willReturn("Simple Product 1");
         $this->model->setTypeInstance($typeInstanceMock);
 
+        $productLink1 = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $this->productLinkFactory->expects($this->at(0))
+            ->method('create')
+            ->willReturn($productLink1);
+
         $links = $this->model->getProductLinks();
-        $this->assertEquals($links, $productLinks);
+        $this->assertEquals($links, $expectedOutput);
     }
 
     /**
diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml
index acd741fdbbe66d5c107a3d24ce3d99a5ec716b52..8a4efe5a2b862b4be9e489a7a9c01b186ad08b0c 100644
--- a/app/code/Magento/Catalog/etc/di.xml
+++ b/app/code/Magento/Catalog/etc/di.xml
@@ -98,6 +98,7 @@
     <type name="Magento\Catalog\Model\Product">
         <arguments>
             <argument name="catalogProductStatus" xsi:type="object">Magento\Catalog\Model\Product\Attribute\Source\Status\Proxy</argument>
+            <argument name="productLink" xsi:type="object">Magento\Catalog\Model\Product\Link\Proxy</argument>
         </arguments>
     </type>
     <type name="Magento\Catalog\Model\Resource\Product\Collection">
@@ -398,6 +399,11 @@
             <argument name="validatorFile" xsi:type="object">Magento\Catalog\Model\Product\Option\Type\File\ValidatorFile\Proxy</argument>
         </arguments>
     </type>
+    <type name="Magento\Catalog\Model\Attribute\Config">
+        <arguments>
+            <argument name="dataStorage" xsi:type="object">Magento\Catalog\Model\Attribute\Config\Data\Proxy</argument>
+        </arguments>
+    </type>
     <virtualType name="Magento\Catalog\Model\Layer\Search\Context" type="Magento\Catalog\Model\Layer\Context">
         <arguments>
             <argument name="collectionProvider" xsi:type="object">Magento\Catalog\Model\Layer\Search\ItemCollectionProvider</argument>
@@ -464,4 +470,9 @@
     <type name="Magento\Catalog\Api\ProductRepositoryInterface">
         <plugin name="transactionWrapper" type="\Magento\Catalog\Model\Plugin\ProductRepository\TransactionWrapper" sortOrder="-1"/>
     </type>
+    <type name="Magento\Catalog\Model\CategoryRepository">
+        <arguments>
+            <argument name="categoryResource" xsi:type="object">Magento\Catalog\Model\Resource\Category\Proxy</argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Catalog/view/base/web/js/price-box.js b/app/code/Magento/Catalog/view/base/web/js/price-box.js
index 667a30836e529533ce04c77b9f5605668852b04b..eed8ac4b9752c2eabcf80175699cc778d7d491c0 100644
--- a/app/code/Magento/Catalog/view/base/web/js/price-box.js
+++ b/app/code/Magento/Catalog/view/base/web/js/price-box.js
@@ -190,7 +190,7 @@ define([
             if (_.isEmpty(prices)) {
                 priceHolders.each(function (index, element) {
                     var type = $(element).data('priceType'),
-                        amount = $(element).data('priceAmount');
+                        amount = parseFloat($(element).data('priceAmount'));
 
                     if (type && amount) {
                         prices[type] = {
diff --git a/app/code/Magento/CatalogImportExport/Model/Export/Product.php b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
index 15ab59aaab2b3b5e2b694f685e7d7523fcb5c565..72810dc82611f66c38e19f027215af9cd063721f 100644
--- a/app/code/Magento/CatalogImportExport/Model/Export/Product.php
+++ b/app/code/Magento/CatalogImportExport/Model/Export/Product.php
@@ -802,7 +802,7 @@ class Product extends \Magento\ImportExport\Model\Export\Entity\AbstractEntity
                 }
             }
         } catch (\Exception $e) {
-            $this->_logger->logException($e);
+            $this->_logger->critical($e);
         }
         return $exportData;
     }
diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php b/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php
index a71fe9dcc496215b69d7573c094c6762a8be4c4d..0f68ad9e99503a7932d2407dd79bd15d55c870f5 100644
--- a/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php
+++ b/app/code/Magento/CatalogImportExport/Model/Import/Proxy/Product/Resource.php
@@ -13,23 +13,4 @@ namespace Magento\CatalogImportExport\Model\Import\Proxy\Product;
 
 class Resource extends \Magento\Catalog\Model\Resource\Product
 {
-    /**
-     * Product to category table.
-     *
-     * @return string
-     */
-    public function getProductCategoryTable()
-    {
-        return $this->_productCategoryTable;
-    }
-
-    /**
-     * Product to website table.
-     *
-     * @return string
-     */
-    public function getProductWebsiteTable()
-    {
-        return $this->_productWebsiteTable;
-    }
 }
diff --git a/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php b/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php
index 46163c26be6d3df33831a35e9344333ffeaef7ef..0a620ce1562d529959ce09af000bc2f3b9923b7a 100644
--- a/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php
+++ b/app/code/Magento/CatalogInventory/Api/Data/StockItemInterface.php
@@ -55,7 +55,7 @@ interface StockItemInterface extends ExtensibleDataInterface
     const CUSTOMER_GROUP_ID = 'customer_group_id';
 
     /**
-     * @return int
+     * @return int|null
      */
     public function getItemId();
 
@@ -66,7 +66,7 @@ interface StockItemInterface extends ExtensibleDataInterface
     public function setItemId($itemId);
 
     /**
-     * @return int
+     * @return int|null
      */
     public function getProductId();
 
@@ -79,7 +79,7 @@ interface StockItemInterface extends ExtensibleDataInterface
     /**
      * Retrieve Website Id
      *
-     * @return int
+     * @return int|null
      */
     public function getWebsiteId();
 
@@ -94,7 +94,7 @@ interface StockItemInterface extends ExtensibleDataInterface
     /**
      * Retrieve stock identifier
      *
-     * @return int
+     * @return int|null
      */
     public function getStockId();
 
diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/AfterProductLoad.php b/app/code/Magento/CatalogInventory/Model/Plugin/AfterProductLoad.php
new file mode 100644
index 0000000000000000000000000000000000000000..3d6c9d404afe0e0e4594ef8f467c630575c69fab
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/Plugin/AfterProductLoad.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogInventory\Model\Plugin;
+
+class AfterProductLoad
+{
+    /**
+     * @var \Magento\CatalogInventory\Api\StockRegistryInterface
+     */
+    protected $stockRegistry;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductExtensionFactory
+     */
+    protected $productExtensionFactory;
+
+    /**
+     * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
+     * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
+     */
+    public function __construct(
+        \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
+        \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
+    ) {
+        $this->stockRegistry = $stockRegistry;
+        $this->productExtensionFactory = $productExtensionFactory;
+    }
+
+    /**
+     * Add stock item information to the product's extension attributes
+     *
+     * @param \Magento\Catalog\Model\Product $product
+     * @return \Magento\Catalog\Model\Product
+     */
+    public function afterLoad(\Magento\Catalog\Model\Product $product)
+    {
+        $productExtension = $product->getExtensionAttributes();
+        if ($productExtension === null) {
+            $productExtension = $this->productExtensionFactory->create();
+        }
+        // stockItem := \Magento\CatalogInventory\Api\Data\StockItemInterface
+        $productExtension->setStockItem($this->stockRegistry->getStockItem($product->getId()));
+        $product->setExtensionAttributes($productExtension);
+        return $product;
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php
new file mode 100644
index 0000000000000000000000000000000000000000..0b222de117c9e84cb9aea4c009889f57ccbb8c80
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Model/Plugin/AroundProductRepositorySave.php
@@ -0,0 +1,95 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\CatalogInventory\Model\Plugin;
+
+class AroundProductRepositorySave
+{
+    /**
+     * @var \Magento\CatalogInventory\Api\StockRegistryInterface
+     */
+    protected $stockRegistry;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface
+     */
+    protected $storeManager;
+
+    /**
+     * @param \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry
+     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
+     */
+    public function __construct(
+        \Magento\CatalogInventory\Api\StockRegistryInterface $stockRegistry,
+        \Magento\Store\Model\StoreManagerInterface $storeManager
+    ) {
+        $this->stockRegistry = $stockRegistry;
+        $this->storeManager = $storeManager;
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject
+     * @param callable $proceed
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param bool $saveOptions
+     * @return \Magento\Catalog\Api\Data\ProductInterface
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function aroundSave(
+        \Magento\Catalog\Api\ProductRepositoryInterface $subject,
+        \Closure $proceed,
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        $saveOptions = false
+    ) {
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $result */
+        $result = $proceed($product, $saveOptions);
+
+        /* @var \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem */
+        $stockItem = $this->getStockItemToBeUpdated($product);
+        if ($stockItem == null) {
+            return $result;
+        }
+
+        // set fields that the customer should not care about
+        $stockItem->setProductId($result->getId());
+        $stockItem->setWebsiteId($this->storeManager->getStore($result->getStoreId())->getWebsiteId());
+
+        $this->stockRegistry->updateStockItemBySku($result->getSku(), $stockItem);
+
+        // since we just saved a portion of the product, force a reload of it before returning it
+        return $subject->get($result->getSku(), false, $result->getStoreId(), true);
+    }
+
+    /**
+     * Return the stock item that needs to be updated.
+     * If the stock item does not need to be updated, return null.
+     *
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @return \Magento\CatalogInventory\Api\Data\StockItemInterface|null
+     */
+    protected function getStockItemToBeUpdated($product)
+    {
+        // from the API, all the data we care about will exist as extension attributes of the original product
+        $extendedAttributes = $product->getExtensionAttributes();
+        if ($extendedAttributes !== null) {
+            $stockItem = $extendedAttributes->getStockItem();
+            if ($stockItem != null) {
+                return $stockItem;
+            }
+        }
+
+        // we have no new stock item information to update, however we need to ensure that the product does have some
+        // stock item information present.  On a newly created product, it will not have any stock item info.
+        $stockItem = $this->stockRegistry->getStockItem($product->getId());
+        if ($stockItem->getItemId() != null) {
+            // we already have stock item info, so we return null since nothing more needs to be updated
+            return null;
+        }
+
+        return $stockItem;
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Model/Stock/Item.php b/app/code/Magento/CatalogInventory/Model/Stock/Item.php
index cb622e904c6bbab1838275d66a5f9129e6640a24..15fb84875aafeac95108233e7f4ea482fc295a26 100644
--- a/app/code/Magento/CatalogInventory/Model/Stock/Item.php
+++ b/app/code/Magento/CatalogInventory/Model/Stock/Item.php
@@ -239,11 +239,11 @@ class Item extends AbstractExtensibleModel implements StockItemInterface
     }
 
     /**
-     * @return int Timestamp
+     * @return string Timestamp
      */
     public function getLowStockDate()
     {
-        return (int) $this->_getData(static::LOW_STOCK_DATE);
+        return $this->_getData(static::LOW_STOCK_DATE);
     }
 
     /**
diff --git a/app/code/Magento/CatalogInventory/Model/StockManagement.php b/app/code/Magento/CatalogInventory/Model/StockManagement.php
index 1f50b8b5dceaa3b64ff14c83044ac6e62f7e85cb..3d5fee6d9cef90eb8bfcf98d78b6c45e68c0bca2 100644
--- a/app/code/Magento/CatalogInventory/Model/StockManagement.php
+++ b/app/code/Magento/CatalogInventory/Model/StockManagement.php
@@ -84,7 +84,7 @@ class StockManagement implements StockManagementInterface
             $orderedQty = $items[$productId];
             $stockItem = $this->stockRegistryProvider->getStockItem($productId, $websiteId);
             $canSubtractQty = $stockItem->getItemId() && $this->canSubtractQty($stockItem);
-            if (!$canSubtractQty || !$this->stockConfiguration->isQty($this->getProductType($productId))) {
+            if (!$canSubtractQty || !$this->stockConfiguration->isQty($lockedItemRecord['type_id'])) {
                 continue;
             }
             if (!$stockItem->hasAdminArea()
diff --git a/app/code/Magento/CatalogInventory/Model/StockRegistry.php b/app/code/Magento/CatalogInventory/Model/StockRegistry.php
index fefbf9f34899f031a374d551e390b24235d3f9c0..f32aeb2104b686091e06120271c462ec33542f08 100644
--- a/app/code/Magento/CatalogInventory/Model/StockRegistry.php
+++ b/app/code/Magento/CatalogInventory/Model/StockRegistry.php
@@ -15,6 +15,8 @@ use Magento\CatalogInventory\Model\Spi\StockRegistryProviderInterface;
 
 /**
  * Class StockRegistry
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class StockRegistry implements StockRegistryInterface
 {
@@ -185,9 +187,7 @@ class StockRegistry implements StockRegistryInterface
         $origStockItem = $this->getStockItem($productId, $websiteId);
         $data = $stockItem->getData();
         if ($origStockItem->getItemId()) {
-            if (isset($data['item_id'])) {
-                unset($data['item_id']);
-            }
+            unset($data['item_id']);
         }
         $origStockItem->addData($data);
         $origStockItem->setProductId($productId);
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AfterProductLoadTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..77b8985cac84c3d21bb9eeb7a598ea9138c366d6
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AfterProductLoadTest.php
@@ -0,0 +1,104 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+// @codingStandardsIgnoreFile
+
+namespace Magento\CatalogInventory\Test\Unit\Model\Plugin;
+
+class AfterProductLoadTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\CatalogInventory\Model\Plugin\AfterProductLoad
+     */
+    protected $plugin;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductExtensionFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionFactoryMock;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductExtension|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionMock;
+
+    protected function setUp()
+    {
+        $stockRegistryMock = $this->getMock('\Magento\CatalogInventory\Api\StockRegistryInterface');
+        $this->productExtensionFactoryMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->plugin = new \Magento\CatalogInventory\Model\Plugin\AfterProductLoad(
+            $stockRegistryMock,
+            $this->productExtensionFactoryMock
+        );
+
+        $productId = 5494;
+        $stockItemMock = $this->getMock('\Magento\CatalogInventory\Api\Data\StockItemInterface');
+
+        $stockRegistryMock->expects($this->once())
+            ->method('getStockItem')
+            ->with($productId)
+            ->willReturn($stockItemMock);
+
+        $this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['setStockItem'])
+            ->getMock();
+        $this->productExtensionMock->expects($this->once())
+            ->method('setStockItem')
+            ->with($stockItemMock)
+            ->willReturnSelf();
+
+        $this->productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->productMock->expects($this->once())
+            ->method('setExtensionAttributes')
+            ->with($this->productExtensionMock)
+            ->willReturnSelf();
+        $this->productMock->expects(($this->once()))
+            ->method('getId')
+            ->will($this->returnValue($productId));
+    }
+
+    public function testAfterLoad()
+    {
+        // test when extension attributes are not (yet) present in the product
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn(null);
+        $this->productExtensionFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($this->productExtensionMock);
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->plugin->afterLoad($this->productMock)
+        );
+    }
+
+    public function testAfterLoadWithExistingExtensionAttributes()
+    {
+        // test when extension attributes already exist
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionFactoryMock->expects($this->never())
+            ->method('create');
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->plugin->afterLoad($this->productMock)
+        );
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..95560e7ce7be3144937120e89eb40f41484b7586
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
@@ -0,0 +1,176 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+// @codingStandardsIgnoreFile
+
+namespace Magento\CatalogInventory\Test\Unit\Model\Plugin;
+
+class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave
+     */
+    protected $plugin;
+
+    /**
+     * @var \Magento\CatalogInventory\Api\Data\StockItemInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockItemMock;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $savedProductMock;
+
+    /**
+     * @var \Magento\CatalogInventory\Api\StockRegistryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockRegistry;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManager;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductExtension|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionMock;
+
+    /**
+     * @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productRepositoryMock;
+
+    /**
+     * @var \Closure
+     */
+    protected $closureMock;
+
+    public function setUp()
+    {
+        $this->stockRegistry = $this->getMock('\Magento\CatalogInventory\Api\StockRegistryInterface');
+        $this->storeManager = $this->getMock('\Magento\Store\Model\StoreManagerInterface');
+
+        $this->plugin = new \Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave(
+            $this->stockRegistry,
+            $this->storeManager
+        );
+
+        $this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['getStockItem'])
+            ->getMock();
+        $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface');
+        $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
+        $this->savedProductMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
+        $this->closureMock = function () {
+            return $this->savedProductMock;
+        };
+        $this->stockItemMock = $this->getMock('\Magento\CatalogInventory\Api\Data\StockItemInterface');
+    }
+
+    public function testAroundSaveWhenProductHasNoStockItemNeedingToBeUpdated()
+    {
+        // pretend we have no extension attributes at all
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->never())->method('getStockItem');
+
+        // pretend that the product already has existing stock item information
+        $this->stockRegistry->expects($this->once())->method('getStockItem')->willReturn($this->stockItemMock);
+        $this->stockItemMock->expects($this->once())->method('getItemId')->willReturn(1);
+        $this->stockItemMock->expects($this->never())->method('setProductId');
+        $this->stockItemMock->expects($this->never())->method('setWebsiteId');
+
+        // expect that there are no changes to the existing stock item information
+        $this->assertEquals(
+            $this->savedProductMock,
+            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    public function testAroundSaveWhenProductHasNoPersistentStockItemInfo()
+    {
+        // pretend we do have extension attributes, but none for 'stock_item'
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getStockItem')
+            ->willReturn(null);
+
+        $storeMock = $this->getMockBuilder('\Magento\Store\Model\Store')
+            ->disableOriginalConstructor()->getMock();
+        $storeMock->expects($this->once())->method('getWebsiteId')->willReturn(1);
+        $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock);
+
+        $this->stockRegistry->expects($this->once())->method('getStockItem')->willReturn($this->stockItemMock);
+        $this->stockRegistry->expects($this->once())->method('updateStockItemBySku');
+
+        $this->stockItemMock->expects($this->once())->method('getItemId')->willReturn(null);
+        $this->stockItemMock->expects($this->once())->method('setProductId');
+        $this->stockItemMock->expects($this->once())->method('setWebsiteId');
+
+        $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
+            ->disableOriginalConstructor()->getMock();
+        $this->productRepositoryMock->expects($this->once())->method('get')->willReturn($newProductMock);
+
+        $this->assertEquals(
+            $newProductMock,
+            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    public function testAroundSave()
+    {
+        $productId = 5494;
+        $websiteId = 1;
+        $storeId = 2;
+        $sku = 'my product that needs saving';
+
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getStockItem')
+            ->willReturn($this->stockItemMock);
+
+        $storeMock = $this->getMockBuilder('\Magento\Store\Model\Store')
+            ->disableOriginalConstructor()->getMock();
+        $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId);
+        $this->storeManager->expects($this->once())->method('getStore')->with($storeId)->willReturn($storeMock);
+
+        $this->savedProductMock->expects(($this->once()))->method('getId')->willReturn($productId);
+        $this->savedProductMock->expects(($this->atLeastOnce()))->method('getStoreId')->willReturn($storeId);
+        $this->savedProductMock->expects($this->atLeastOnce())->method('getSku')->willReturn($sku);
+
+        $this->stockItemMock->expects($this->once())->method('setProductId')->with($productId);
+        $this->stockItemMock->expects($this->once())->method('setWebsiteId')->with($websiteId);
+
+        $this->stockRegistry->expects($this->once())
+            ->method('updateStockItemBySku')
+            ->with($sku, $this->stockItemMock);
+
+        $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
+            ->disableOriginalConstructor()->getMock();
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($sku, false, $storeId, true)
+            ->willReturn($newProductMock);
+
+        $this->assertEquals(
+            $newProductMock,
+            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+}
diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php
index 20de61fbf3ba19c6851e47bc016e3ee38e82d9eb..f6cfa8b4e7e6643d3b4df80ae83fe79bd57dd0fc 100644
--- a/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php
+++ b/app/code/Magento/CatalogInventory/Test/Unit/Model/Stock/ItemTest.php
@@ -395,4 +395,12 @@ class ItemTest extends \PHPUnit_Framework_TestCase
             [0, $this->storeId],
         ];
     }
+
+    public function testGetLowStockDate()
+    {
+        // ensure we do *not* return '2015' due to casting to an int
+        $date = '2015-4-17';
+        $this->item->setLowStockDate($date);
+        $this->assertEquals($date, $this->item->getLowStockDate());
+    }
 }
diff --git a/app/code/Magento/CatalogInventory/etc/di.xml b/app/code/Magento/CatalogInventory/etc/di.xml
index 11c98534f732718a84bb3cde28379c3f76137b61..b1ad4a03847bbd997e93dc255b0d7de325f11f9d 100644
--- a/app/code/Magento/CatalogInventory/etc/di.xml
+++ b/app/code/Magento/CatalogInventory/etc/di.xml
@@ -52,4 +52,15 @@
     <type name="Magento\Catalog\Block\Product\View">
         <plugin name="quantityValidators" type="Magento\CatalogInventory\Block\Plugin\ProductView" />
     </type>
+    <type name="Magento\CatalogInventory\Model\Configuration">
+        <arguments>
+            <argument name="config" xsi:type="object">Magento\Catalog\Model\ProductTypes\Config\Proxy</argument>
+        </arguments>
+    </type>
+    <type name="Magento\Catalog\Model\Product">
+        <plugin name="catalogInventoryAfterLoad" type="\Magento\CatalogInventory\Model\Plugin\AfterProductLoad"/>
+    </type>
+    <type name="Magento\Catalog\Api\ProductRepositoryInterface">
+        <plugin name="catalogInventoryAroundSave" type="\Magento\CatalogInventory\Model\Plugin\AroundProductRepositorySave"/>
+    </type>
 </config>
diff --git a/app/code/Magento/CatalogInventory/etc/service_data_attributes.xml b/app/code/Magento/CatalogInventory/etc/service_data_attributes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..587b98b401a0981d8ca14b75242f0adac67312a2
--- /dev/null
+++ b/app/code/Magento/CatalogInventory/etc/service_data_attributes.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
+        <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItemInterface">
+            <resources>
+                <resource ref="Magento_CatalogInventory::cataloginventory"/>
+            </resources>
+        </attribute>
+    </extension_attributes>
+</config>
diff --git a/app/code/Magento/Checkout/Model/Session.php b/app/code/Magento/Checkout/Model/Session.php
index 0f7d18e41355b5ffbae94f02f0f6272485d59cbd..da634e082756d1e8be81769a6af6dc32a6874fbe 100644
--- a/app/code/Magento/Checkout/Model/Session.php
+++ b/app/code/Magento/Checkout/Model/Session.php
@@ -90,6 +90,7 @@ class Session extends \Magento\Framework\Session\SessionManager
      * @param \Magento\Framework\Session\StorageInterface $storage
      * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
      * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
+     * @param \Magento\Framework\App\State $appState
      * @param \Magento\Sales\Model\OrderFactory $orderFactory
      * @param \Magento\Customer\Model\Session $customerSession
      * @param \Magento\Quote\Model\QuoteRepository $quoteRepository
@@ -97,6 +98,7 @@ class Session extends \Magento\Framework\Session\SessionManager
      * @param \Magento\Framework\Event\ManagerInterface $eventManager
      * @param \Magento\Store\Model\StoreManagerInterface $storeManager
      * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
+     * @throws \Magento\Framework\Exception\SessionException
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -108,6 +110,7 @@ class Session extends \Magento\Framework\Session\SessionManager
         \Magento\Framework\Session\StorageInterface $storage,
         \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
         \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory,
+        \Magento\Framework\App\State $appState,
         \Magento\Sales\Model\OrderFactory $orderFactory,
         \Magento\Customer\Model\Session $customerSession,
         \Magento\Quote\Model\QuoteRepository $quoteRepository,
@@ -131,9 +134,9 @@ class Session extends \Magento\Framework\Session\SessionManager
             $validator,
             $storage,
             $cookieManager,
-            $cookieMetadataFactory
+            $cookieMetadataFactory,
+            $appState
         );
-        $this->start();
     }
 
     /**
diff --git a/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php b/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php
index fed2f67da6f177806559e463deb8fda56a2fbb3f..11a83c181d64f504d267524440423a30c90b2970 100644
--- a/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php
+++ b/app/code/Magento/Cms/Block/Adminhtml/Block/Widget/Chooser.php
@@ -81,7 +81,7 @@ class Chooser extends \Magento\Backend\Block\Widget\Grid\Extended
         if ($element->getValue()) {
             $block = $this->_blockFactory->create()->load($element->getValue());
             if ($block->getId()) {
-                $chooser->setLabel($block->getTitle());
+                $chooser->setLabel($this->escapeHtml($block->getTitle()));
             }
         }
 
diff --git a/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php b/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php
index addaf3f4926b8e05f48833b16e540eb73ad295ef..54c169c890a9b8f8901203bff0a9a3a6d4e69275 100644
--- a/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php
+++ b/app/code/Magento/Cms/Block/Adminhtml/Page/Widget/Chooser.php
@@ -98,7 +98,7 @@ class Chooser extends \Magento\Backend\Block\Widget\Grid\Extended
         if ($element->getValue()) {
             $page = $this->_pageFactory->create()->load((int)$element->getValue());
             if ($page->getId()) {
-                $chooser->setLabel($page->getTitle());
+                $chooser->setLabel($this->escapeHtml($page->getTitle()));
             }
         }
 
diff --git a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php
index 0c075194e3330d099d7bb12b2427b971621f8a81..55761dae44ac94230eb126aa182e22394aaf1d32 100644
--- a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php
+++ b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Block/Widget/ChooserTest.php
@@ -35,6 +35,11 @@ class ChooserTest extends \PHPUnit_Framework_TestCase
      */
     protected $urlBuilderMock;
 
+    /**
+     * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $escaper;
+
     /**
      * @var \Magento\Cms\Model\BlockFactory|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -66,6 +71,14 @@ class ChooserTest extends \PHPUnit_Framework_TestCase
         $this->urlBuilderMock = $this->getMockBuilder('Magento\Framework\UrlInterface')
             ->disableOriginalConstructor()
             ->getMock();
+        $this->escaper = $this->getMockBuilder('Magento\Framework\Escaper')
+            ->disableOriginalConstructor()
+            ->setMethods(
+                [
+                    'escapeHtml',
+                ]
+            )
+            ->getMock();
         $this->blockFactoryMock = $this->getMockBuilder('Magento\Cms\Model\BlockFactory')
             ->setMethods(
                 [
@@ -90,6 +103,7 @@ class ChooserTest extends \PHPUnit_Framework_TestCase
                 [
                     'getTitle',
                     'load',
+                    'getId',
                 ]
             )
             ->getMock();
@@ -112,15 +126,16 @@ class ChooserTest extends \PHPUnit_Framework_TestCase
         $this->context = $objectManager->getObject(
             'Magento\Backend\Block\Template\Context',
             [
-                'layout' => $this->layoutMock,
+                'layout'     => $this->layoutMock,
                 'mathRandom' => $this->mathRandomMock,
-                'urlBuilder' => $this->urlBuilderMock
+                'urlBuilder' => $this->urlBuilderMock,
+                'escaper'    => $this->escaper,
             ]
         );
         $this->this = $objectManager->getObject(
             'Magento\Cms\Block\Adminhtml\Block\Widget\Chooser',
             [
-                'context' => $this->context,
+                'context'      => $this->context,
                 'blockFactory' => $this->blockFactoryMock
             ]
         );
@@ -135,13 +150,14 @@ class ChooserTest extends \PHPUnit_Framework_TestCase
      */
     public function testPrepareElementHtml($elementValue, $modelBlockId)
     {
-        $elementId = 1;
-        $uniqId = '126hj4h3j73hk7b347jhkl37gb34';
-        $sourceUrl = 'cms/block_widget/chooser/126hj4h3j73hk7b347jhkl37gb34';
-        $config = ['key1' => 'value1'];
-        $fieldsetId = 2;
-        $html = 'some html';
-        $title = 'some title';
+        $elementId    = 1;
+        $uniqId       = '126hj4h3j73hk7b347jhkl37gb34';
+        $sourceUrl    = 'cms/block_widget/chooser/126hj4h3j73hk7b347jhkl37gb34';
+        $config       = ['key1' => 'value1'];
+        $fieldsetId   = 2;
+        $html         = 'some html';
+        $title        = 'some "><img src=y onerror=prompt(document.domain)>; title';
+        $titleEscaped = 'some &quot;&gt;&lt;img src=y onerror=prompt(document.domain)&gt;; title';
 
         $this->this->setConfig($config);
         $this->this->setFieldsetId($fieldsetId);
@@ -197,13 +213,18 @@ class ChooserTest extends \PHPUnit_Framework_TestCase
         $this->modelBlockMock->expects($this->any())
             ->method('getTitle')
             ->willReturn($title);
-        $this->chooserMock->expects($this->any())
-            ->method('setLabel')
-            ->with($title)
-            ->willReturnSelf();
         $this->chooserMock->expects($this->atLeastOnce())
             ->method('toHtml')
             ->willReturn($html);
+        if (!empty($elementValue) && !empty($modelBlockId)) {
+            $this->escaper->expects(($this->atLeastOnce()))
+                ->method('escapeHtml')
+                ->willReturn($titleEscaped);
+            $this->chooserMock->expects($this->atLeastOnce())
+                ->method('setLabel')
+                ->with($titleEscaped)
+                ->willReturnSelf();
+        }
         $this->elementMock->expects($this->atLeastOnce())
             ->method('setData')
             ->with('after_element_html', $html)
diff --git a/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..75107bcb42de11db287607e49595aa6fecfd637b
--- /dev/null
+++ b/app/code/Magento/Cms/Test/Unit/Block/Adminhtml/Page/Widget/ChooserTest.php
@@ -0,0 +1,271 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Cms\Test\Unit\Block\Adminhtml\Page\Widget;
+
+/**
+ * @covers \Magento\Cms\Block\Adminhtml\Page\Widget\Chooser
+ */
+class ChooserTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Cms\Block\Adminhtml\Page\Widget\Chooser
+     */
+    protected $this;
+
+    /**
+     * @var \Magento\Backend\Block\Template\Context
+     */
+    protected $context;
+
+    /**
+     * @var \Magento\Framework\Math\Random|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $mathRandomMock;
+
+    /**
+     * @var \Magento\Framework\UrlInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $urlBuilderMock;
+
+    /**
+     * @var \Magento\Framework\Escaper|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $escaper;
+
+    /**
+     * @var \Magento\Cms\Model\Page|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $cmsPageMock;
+
+    /**
+     * @var \Magento\Framework\View\LayoutInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $layoutMock;
+
+    /**
+     * @var \Magento\Cms\Model\PageFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $pageFactoryMock;
+
+    /**
+     * @var \Magento\Framework\Data\Form\Element\AbstractElement|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $elementMock;
+
+    /**
+     * @var \Magento\Framework\View\Element\BlockInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $chooserMock;
+
+    protected function setUp()
+    {
+        $this->layoutMock = $this->getMockBuilder('Magento\Framework\View\LayoutInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->mathRandomMock = $this->getMockBuilder('Magento\Framework\Math\Random')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->urlBuilderMock = $this->getMockBuilder('Magento\Framework\UrlInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->escaper = $this->getMockBuilder('Magento\Framework\Escaper')
+            ->disableOriginalConstructor()
+            ->setMethods(
+                [
+                    'escapeHtml',
+                ]
+            )
+            ->getMock();
+        $this->pageFactoryMock = $this->getMockBuilder('Magento\Cms\Model\PageFactory')
+            ->setMethods(
+                [
+                    'create',
+                ]
+            )
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->elementMock = $this->getMockBuilder('Magento\Framework\Data\Form\Element\AbstractElement')
+            ->disableOriginalConstructor()
+            ->setMethods(
+                [
+                    'getId',
+                    'getValue',
+                    'setData',
+                ]
+            )
+            ->getMock();
+        $this->cmsPageMock = $this->getMockBuilder('Magento\Cms\Model\Page')
+            ->disableOriginalConstructor()
+            ->setMethods(
+                [
+                    'getTitle',
+                    'load',
+                    'getId',
+                ]
+            )
+            ->getMock();
+        $this->chooserMock = $this->getMockBuilder('Magento\Framework\View\Element\BlockInterface')
+            ->disableOriginalConstructor()
+            ->setMethods(
+                [
+                    'setElement',
+                    'setConfig',
+                    'setFieldsetId',
+                    'setSourceUrl',
+                    'setUniqId',
+                    'setLabel',
+                    'toHtml',
+                ]
+            )
+            ->getMock();
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->context = $objectManager->getObject(
+            'Magento\Backend\Block\Template\Context',
+            [
+                'layout'     => $this->layoutMock,
+                'mathRandom' => $this->mathRandomMock,
+                'urlBuilder' => $this->urlBuilderMock,
+                'escaper'    => $this->escaper,
+            ]
+        );
+        $this->this = $objectManager->getObject(
+            'Magento\Cms\Block\Adminhtml\Page\Widget\Chooser',
+            [
+                'context'     => $this->context,
+                'pageFactory' => $this->pageFactoryMock
+            ]
+        );
+    }
+
+    /**
+     * @covers \Magento\Cms\Block\Adminhtml\Block\Widget\Chooser::prepareElementHtml
+     *
+     * @param string $elementValue
+     * @param integer|null $cmsPageId
+     *
+     * @dataProvider prepareElementHtmlDataProvider
+     */
+    public function testPrepareElementHtml($elementValue, $cmsPageId)
+    {
+        //$elementValue = 12345;
+        //$cmsPageId    = 1;
+        $elementId    = 1;
+        $uniqId       = '126hj4h3j73hk7b347jhkl37gb34';
+        $sourceUrl    = 'cms/page_widget/chooser/126hj4h3j73hk7b347jhkl37gb34';
+        $config       = ['key1' => 'value1'];
+        $fieldsetId   = 2;
+        $html         = 'some html';
+        $title        = 'some "><img src=y onerror=prompt(document.domain)>; title';
+        $titleEscaped = 'some &quot;&gt;&lt;img src=y onerror=prompt(document.domain)&gt;; title';
+
+        $this->this->setConfig($config);
+        $this->this->setFieldsetId($fieldsetId);
+
+        $this->elementMock->expects($this->atLeastOnce())
+            ->method('getId')
+            ->willReturn($elementId);
+        $this->mathRandomMock->expects($this->atLeastOnce())
+            ->method('getUniqueHash')
+            ->with($elementId)
+            ->willReturn($uniqId);
+        $this->urlBuilderMock->expects($this->atLeastOnce())
+            ->method('getUrl')
+            ->with('cms/page_widget/chooser', ['uniq_id' => $uniqId])
+            ->willReturn($sourceUrl);
+        $this->layoutMock->expects($this->atLeastOnce())
+            ->method('createBlock')
+            ->with('Magento\Widget\Block\Adminhtml\Widget\Chooser')
+            ->willReturn($this->chooserMock);
+        $this->chooserMock->expects($this->atLeastOnce())
+            ->method('setElement')
+            ->with($this->elementMock)
+            ->willReturnSelf();
+        $this->chooserMock->expects($this->atLeastOnce())
+            ->method('setConfig')
+            ->with($config)
+            ->willReturnSelf();
+        $this->chooserMock->expects($this->atLeastOnce())
+            ->method('setFieldsetId')
+            ->with($fieldsetId)
+            ->willReturnSelf();
+        $this->chooserMock->expects($this->atLeastOnce())
+            ->method('setSourceUrl')
+            ->with($sourceUrl)
+            ->willReturnSelf();
+        $this->chooserMock->expects($this->atLeastOnce())
+            ->method('setUniqId')
+            ->with($uniqId)
+            ->willReturnSelf();
+        $this->elementMock->expects($this->atLeastOnce())
+            ->method('getValue')
+            ->willReturn($elementValue);
+        $this->pageFactoryMock->expects($this->any())
+            ->method('create')
+            ->willReturn($this->cmsPageMock);
+        $this->cmsPageMock->expects($this->any())
+            ->method('load')
+            ->with((int)$elementValue)
+            ->willReturnSelf();
+        $this->cmsPageMock->expects($this->any())
+            ->method('getId')
+            ->willReturn($cmsPageId);
+        $this->cmsPageMock->expects($this->any())
+            ->method('getTitle')
+            ->willReturn($title);
+        $this->chooserMock->expects($this->atLeastOnce())
+            ->method('toHtml')
+            ->willReturn($html);
+        if (!empty($elementValue) && !empty($cmsPageId)) {
+            $this->escaper->expects(($this->atLeastOnce()))
+                ->method('escapeHtml')
+                ->willReturn($titleEscaped);
+            $this->chooserMock->expects($this->atLeastOnce())
+                ->method('setLabel')
+                ->with($titleEscaped)
+                ->willReturnSelf();
+        }
+        $this->elementMock->expects($this->atLeastOnce())
+            ->method('setData')
+            ->with('after_element_html', $html)
+            ->willReturnSelf();
+
+        $this->assertEquals($this->elementMock, $this->this->prepareElementHtml($this->elementMock));
+    }
+
+    public function prepareElementHtmlDataProvider()
+    {
+        return [
+            'elementValue NOT EMPTY, modelBlockId NOT EMPTY' => [
+                'elementValue' => 'some value',
+                'cmsPageId' => 1,
+            ],
+            'elementValue NOT EMPTY, modelBlockId IS EMPTY' => [
+                'elementValue' => 'some value',
+                'cmsPageId' => null,
+            ],
+            'elementValue IS EMPTY, modelBlockId NEVER REACHED' => [
+                'elementValue' => '',
+                'cmsPageId' => 1,
+            ]
+        ];
+    }
+
+    /**
+     * @covers \Magento\Cms\Block\Adminhtml\Page\Widget\Chooser::getGridUrl
+     */
+    public function testGetGridUrl()
+    {
+        $url = 'some url';
+
+        $this->urlBuilderMock->expects($this->atLeastOnce())
+            ->method('getUrl')
+            ->with('cms/page_widget/chooser', ['_current' => true])
+            ->willReturn($url);
+
+        $this->assertEquals($url, $this->this->getGridUrl());
+    }
+}
diff --git a/app/code/Magento/Config/Model/Config/Backend/File.php b/app/code/Magento/Config/Model/Config/Backend/File.php
index e7a119f79af9f0bf78d8f459ff0d30875abd097b..a08ed7d4f23894640e6592728f9a2c6a8eb1432b 100644
--- a/app/code/Magento/Config/Model/Config/Backend/File.php
+++ b/app/code/Magento/Config/Model/Config/Backend/File.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Config\Model\Config\Backend;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\App\Filesystem\DirectoryList;
 use Magento\Framework\Filesystem;
 
@@ -196,7 +197,7 @@ class File extends \Magento\Framework\App\Config\Value
     protected function _prependScopeInfo($path)
     {
         $scopeInfo = $this->getScope();
-        if (\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT != $this->getScope()) {
+        if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT != $this->getScope()) {
             $scopeInfo .= '/' . $this->getScopeId();
         }
         return $scopeInfo . '/' . $path;
@@ -213,7 +214,7 @@ class File extends \Magento\Framework\App\Config\Value
     protected function _appendScopeInfo($path)
     {
         $path .= '/' . $this->getScope();
-        if (\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT != $this->getScope()) {
+        if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT != $this->getScope()) {
             $path .= '/' . $this->getScopeId();
         }
         return $path;
diff --git a/app/code/Magento/Config/Model/Config/Backend/Locale.php b/app/code/Magento/Config/Model/Config/Backend/Locale.php
index 0881afdebfc3839d52d0c27de0052ae6a8bd68b1..3d057fd6df864d23061ba4d1d762e265482eb595 100644
--- a/app/code/Magento/Config/Model/Config/Backend/Locale.php
+++ b/app/code/Magento/Config/Model/Config/Backend/Locale.php
@@ -9,6 +9,8 @@
  */
 namespace Magento\Config\Model\Config\Backend;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class Locale extends \Magento\Framework\App\Config\Value
 {
     /**
@@ -91,7 +93,7 @@ class Locale extends \Magento\Framework\App\Config\Value
                     }
 
                     switch ($data->getScope()) {
-                        case \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT:
+                        case ScopeConfigInterface::SCOPE_TYPE_DEFAULT:
                             $scopeName = __('Default scope');
                             break;
 
diff --git a/app/code/Magento/Config/Model/Config/ScopeDefiner.php b/app/code/Magento/Config/Model/Config/ScopeDefiner.php
index 893496d874009b9834fc2f1821057283ba01e323..f69e9dd96d2eb820a87a9d1f5905445df9c576df 100644
--- a/app/code/Magento/Config/Model/Config/ScopeDefiner.php
+++ b/app/code/Magento/Config/Model/Config/ScopeDefiner.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Config\Model\Config;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Store\Model\ScopeInterface as StoreScopeInterface;
 
 /**
@@ -38,6 +39,6 @@ class ScopeDefiner
             'store'
         ) ? StoreScopeInterface::SCOPE_STORE : ($this->_request->getParam(
             'website'
-        ) ? StoreScopeInterface::SCOPE_WEBSITE : \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT);
+        ) ? StoreScopeInterface::SCOPE_WEBSITE : ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
     }
 }
diff --git a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php
index e10a1a1c3e725333743bea341d6170865f17d119..03ed66f29abec3ebd79735f648aefcd633dc44cc 100644
--- a/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php
+++ b/app/code/Magento/Config/Model/Config/Structure/AbstractElement.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Config\Model\Config\Structure;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Store\Model\StoreManagerInterface;
 
 abstract class AbstractElement implements ElementInterface
@@ -136,7 +137,7 @@ abstract class AbstractElement implements ElementInterface
         $showInScope = [
             \Magento\Store\Model\ScopeInterface::SCOPE_STORE => $this->_hasVisibilityValue('showInStore'),
             \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE => $this->_hasVisibilityValue('showInWebsite'),
-            \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT => $this->_hasVisibilityValue('showInDefault'),
+            ScopeConfigInterface::SCOPE_TYPE_DEFAULT => $this->_hasVisibilityValue('showInDefault'),
         ];
 
         if ($this->_storeManager->isSingleStoreMode()) {
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php
index 23069beec258fa8c6ab4e398c537a4874a9010b7..e0c64a35053ad44c88baa18861e84641a995d17d 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/ScopeDefinerTest.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Config\Test\Unit\Model\Config;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 class ScopeDefinerTest extends \PHPUnit_Framework_TestCase
@@ -31,7 +32,7 @@ class ScopeDefinerTest extends \PHPUnit_Framework_TestCase
 
     public function testGetScopeReturnsDefaultScopeIfNoScopeDataIsSpecified()
     {
-        $this->assertEquals(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $this->_model->getScope());
+        $this->assertEquals(ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $this->_model->getScope());
     }
 
     public function testGetScopeReturnsStoreScopeIfStoreIsSpecified()
diff --git a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php
index 03e9fd8d2266e5c9256c74a81716e59e4eb40ac0..778141ab2132096aa8d0e750f7175bebdcea935b 100644
--- a/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php
+++ b/app/code/Magento/Config/Test/Unit/Model/Config/Structure/AbstractElementTest.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Config\Test\Unit\Model\Config\Structure;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class AbstractElementTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -77,7 +79,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase
         $this->_storeManager->expects($this->once())->method('isSingleStoreMode')->will($this->returnValue(true));
         $this->_model->setData(
             ['showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 0],
-            \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+            ScopeConfigInterface::SCOPE_TYPE_DEFAULT
         );
         $this->assertTrue($this->_model->isVisible());
     }
@@ -87,7 +89,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase
         $this->_storeManager->expects($this->once())->method('isSingleStoreMode')->will($this->returnValue(true));
         $this->_model->setData(
             ['hide_in_single_store_mode' => 1, 'showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 0],
-            \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+            ScopeConfigInterface::SCOPE_TYPE_DEFAULT
         );
         $this->assertFalse($this->_model->isVisible());
     }
@@ -100,7 +102,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase
         $this->_storeManager->expects($this->once())->method('isSingleStoreMode')->will($this->returnValue(true));
         $this->_model->setData(
             ['showInDefault' => 0, 'showInStore' => 0, 'showInWebsite' => 0],
-            \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+            ScopeConfigInterface::SCOPE_TYPE_DEFAULT
         );
         $this->assertFalse($this->_model->isVisible());
     }
@@ -121,7 +123,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase
         return [
             [
                 ['showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 0],
-                \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+                ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
             ],
             [
                 ['showInDefault' => 0, 'showInStore' => 1, 'showInWebsite' => 0],
@@ -150,7 +152,7 @@ class AbstractElementTest extends \PHPUnit_Framework_TestCase
         return [
             [
                 ['showInDefault' => 0, 'showInStore' => 1, 'showInWebsite' => 1],
-                \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+                ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
             ],
             [
                 ['showInDefault' => 1, 'showInStore' => 0, 'showInWebsite' => 1],
diff --git a/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php b/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php
index 82e73fe8686e268e1b3701c216fe3bb8a2d4ee95..1c60ed2762efbb9c8e1a4fc6bd62c511dc98f7f1 100644
--- a/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php
+++ b/app/code/Magento/ConfigurableProduct/Api/Data/OptionInterface.php
@@ -41,17 +41,6 @@ interface OptionInterface extends \Magento\Framework\Api\ExtensibleDataInterface
      */
     public function setLabel($label);
 
-    /**
-     * @return string|null
-     */
-    public function getType();
-
-    /**
-     * @param string $type
-     * @return $this
-     */
-    public function setType($type);
-
     /**
      * @return int|null
      */
diff --git a/app/code/Magento/ConfigurableProduct/Api/OptionTypesListInterface.php b/app/code/Magento/ConfigurableProduct/Api/OptionTypesListInterface.php
deleted file mode 100644
index 0978b9c705c4933d3590a69b9454432c4cf3f0d6..0000000000000000000000000000000000000000
--- a/app/code/Magento/ConfigurableProduct/Api/OptionTypesListInterface.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-/**
- *
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\ConfigurableProduct\Api;
-
-interface OptionTypesListInterface
-{
-    /**
-     * Get all available option types for configurable product
-     *
-     * @return string[]
-     * @throws \Magento\Framework\Exception\NoSuchEntityException
-     * @throws \Magento\Framework\Exception\InputException
-     */
-    public function getItems();
-}
diff --git a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php
index 3e18c09648b9f9a6087e3daef9d67c1f4279b0e1..68cdb235bdeb03bd1e15e7b2146d92586236aac0 100644
--- a/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php
+++ b/app/code/Magento/ConfigurableProduct/Model/OptionRepository.php
@@ -87,26 +87,17 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
     public function get($sku, $id)
     {
         $product = $this->getProduct($sku);
-        $collection = $this->getConfigurableAttributesCollection($product);
-        $collection->addFieldToFilter($collection->getResource()->getIdFieldName(), $id);
-        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $configurableAttribute */
-        $configurableAttribute = $collection->getFirstItem();
-        if (!$configurableAttribute->getId()) {
-            throw new NoSuchEntityException(__('Requested option doesn\'t exist: %1', $id));
-        }
-        $prices = $configurableAttribute->getPrices();
-        if (is_array($prices)) {
-            foreach ($prices as $price) {
-                /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */
-                $value = $this->optionValueFactory->create();
-                $value->setValueIndex($price['value_index'])
-                    ->setPricingValue($price['pricing_value'])
-                    ->setIsPercent($price['is_percent']);
-                $values[] = $value;
+
+        $extensionAttribute = $product->getExtensionAttributes();
+        if ($extensionAttribute !== null) {
+            $options = $extensionAttribute->getConfigurableProductOptions();
+            foreach ($options as $option) {
+                if ($option->getId() == $id) {
+                    return $option;
+                }
             }
         }
-        $configurableAttribute->setValues($values);
-        return $configurableAttribute;
+        throw new NoSuchEntityException(__('Requested option doesn\'t exist: %1', $id));
     }
 
     /**
@@ -116,21 +107,10 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
     {
         $options = [];
         $product = $this->getProduct($sku);
-        foreach ($this->getConfigurableAttributesCollection($product) as $option) {
-            $values = [];
-            $prices = $option->getPrices();
-            if (is_array($prices)) {
-                foreach ($prices as $price) {
-                    /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */
-                    $value = $this->optionValueFactory->create();
-                    $value->setValueIndex($price['value_index'])
-                        ->setPricingValue($price['pricing_value'])
-                        ->setIsPercent($price['is_percent']);
-                    $values[] = $value;
-                }
-            }
-            $option->setValues($values);
-            $options[] = $option;
+
+        $extensionAttribute = $product->getExtensionAttributes();
+        if ($extensionAttribute !== null) {
+            $options = $extensionAttribute->getConfigurableProductOptions();
         }
         return $options;
     }
@@ -259,17 +239,6 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
         return $product;
     }
 
-    /**
-     * Retrieve configurable attribute collection through product object
-     *
-     * @param \Magento\Catalog\Model\Product $product
-     * @return \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Collection
-     */
-    private function getConfigurableAttributesCollection(\Magento\Catalog\Model\Product $product)
-    {
-        return $this->configurableType->getConfigurableAttributeCollection($product);
-    }
-
     /**
      * Ensure that all necessary data is available for a new option creation.
      *
@@ -284,9 +253,6 @@ class OptionRepository implements \Magento\ConfigurableProduct\Api\OptionReposit
         if (!$option->getAttributeId()) {
             $inputException->addError(__('Option attribute ID is not specified.'));
         }
-        if (!$option->getType()) {
-            $inputException->addError(__('Option type is not specified.'));
-        }
         if (!$option->getLabel()) {
             $inputException->addError(__('Option label is not specified.'));
         }
diff --git a/app/code/Magento/ConfigurableProduct/Model/OptionTypesList.php b/app/code/Magento/ConfigurableProduct/Model/OptionTypesList.php
deleted file mode 100644
index 23b871b21248166e892ec10b45801cf1184dcf39..0000000000000000000000000000000000000000
--- a/app/code/Magento/ConfigurableProduct/Model/OptionTypesList.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-/**
- *
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\ConfigurableProduct\Model;
-
-class OptionTypesList implements \Magento\ConfigurableProduct\Api\OptionTypesListInterface
-{
-    /**
-     * @var \Magento\Catalog\Model\System\Config\Source\Inputtype
-     */
-    protected $inputType;
-
-    /**
-     * @param \Magento\Catalog\Model\System\Config\Source\Inputtype $inputType
-     */
-    public function __construct(\Magento\Catalog\Model\System\Config\Source\Inputtype $inputType)
-    {
-        $this->inputType = $inputType;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    public function getItems()
-    {
-        return array_map(
-            function ($inputType) {
-                return $inputType['value'];
-            },
-            $this->inputType->toOptionArray()
-        );
-    }
-}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php
new file mode 100644
index 0000000000000000000000000000000000000000..6bf640f16735294afeb92a35513ebbbd58e7e5f0
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/AfterProductLoad.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\ConfigurableProduct\Model\Plugin;
+
+class AfterProductLoad
+{
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductExtensionFactory
+     */
+    protected $productExtensionFactory;
+
+    /**
+     * @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory
+     */
+    protected $optionValueFactory;
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
+     * @param \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory
+     */
+    public function __construct(
+        \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory,
+        \Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory $optionValueFactory
+    ) {
+        $this->productExtensionFactory = $productExtensionFactory;
+        $this->optionValueFactory = $optionValueFactory;
+    }
+
+    /**
+     * @param \Magento\Catalog\Model\Product $subject
+     * @return \Magento\Catalog\Model\Product
+     */
+    public function afterLoad(\Magento\Catalog\Model\Product $product)
+    {
+        if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) {
+            return $product;
+        }
+
+        $productExtension = $product->getExtensionAttributes();
+        if ($productExtension === null) {
+            $productExtension = $this->productExtensionFactory->create();
+        }
+
+        $productExtension->setConfigurableProductOptions($this->getOptions($product));
+        $productExtension->setConfigurableProductLinks($this->getLinkedProducts($product));
+
+        $product->setExtensionAttributes($productExtension);
+
+        return $product;
+    }
+
+    /**
+     * @param \Magento\Catalog\Model\Product $product
+     * @return \Magento\ConfigurableProduct\Api\Data\OptionInterface[]
+     */
+    protected function getOptions(\Magento\Catalog\Model\Product $product)
+    {
+        $options = [];
+        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */
+        $typeInstance = $product->getTypeInstance();
+        $attributeCollection = $typeInstance->getConfigurableAttributes($product);
+        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $option */
+        foreach ($attributeCollection as $option) {
+            $values = [];
+            $prices = $option->getPrices();
+            if (is_array($prices)) {
+                foreach ($prices as $price) {
+                    /** @var \Magento\ConfigurableProduct\Api\Data\OptionValueInterface $value */
+                    $value = $this->optionValueFactory->create();
+                    $value->setValueIndex($price['value_index'])
+                        ->setPricingValue($price['pricing_value'])
+                        ->setIsPercent($price['is_percent']);
+                    $values[] = $value;
+                }
+            }
+            $option->setValues($values);
+            $options[] = $option;
+        }
+        return $options;
+    }
+
+    /**
+     * @param \Magento\Catalog\Model\Product $product
+     * @return int[]
+     */
+    protected function getLinkedProducts(\Magento\Catalog\Model\Product $product)
+    {
+        /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $typeInstance */
+        $typeInstance = $product->getTypeInstance();
+        $childrenIds = $typeInstance->getChildrenIds($product->getId());
+
+        if (isset($childrenIds[0])) {
+            return $childrenIds[0];
+        } else {
+            return [];
+        }
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php
new file mode 100644
index 0000000000000000000000000000000000000000..f0a12b809dcd9c484b63f379c94f1749ff5d6555
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Model/Plugin/AroundProductRepositorySave.php
@@ -0,0 +1,197 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\ConfigurableProduct\Model\Plugin;
+
+use Magento\Framework\Exception\InputException;
+
+class AroundProductRepositorySave
+{
+    /**
+     * @var \Magento\ConfigurableProduct\Api\OptionRepositoryInterface
+     */
+    protected $optionRepository;
+
+    /**
+     * @var \Magento\Catalog\Model\ProductFactory
+     */
+    protected $productFactory;
+
+    /**
+     * Type configurable factory
+     *
+     * @var \Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory
+     */
+    protected $typeConfigurableFactory;
+
+    /*
+     * @var \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data
+     */
+    protected $priceData;
+
+    /**
+     * @param \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository
+     * @param \Magento\Catalog\Model\ProductFactory $productFactory
+     * @param \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data $priceData
+     * @param \Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory $typeConfigurableFactory
+     */
+    public function __construct(
+        \Magento\ConfigurableProduct\Api\OptionRepositoryInterface $optionRepository,
+        \Magento\Catalog\Model\ProductFactory $productFactory,
+        \Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data $priceData,
+        \Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory $typeConfigurableFactory
+    ) {
+        $this->optionRepository = $optionRepository;
+        $this->productFactory = $productFactory;
+        $this->priceData = $priceData;
+        $this->typeConfigurableFactory = $typeConfigurableFactory;
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject
+     * @param callable $proceed
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param bool $saveOptions
+     * @return \Magento\Catalog\Api\Data\ProductInterface
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
+     */
+    public function aroundSave(
+        \Magento\Catalog\Api\ProductRepositoryInterface $subject,
+        \Closure $proceed,
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        $saveOptions = false
+    ) {
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $result */
+        $result = $proceed($product, $saveOptions);
+
+        if ($product->getTypeId() != \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE) {
+            return $result;
+        }
+
+        $extendedAttributes = $product->getExtensionAttributes();
+        if ($extendedAttributes === null) {
+            return $result;
+        }
+        $configurableProductOptions = $extendedAttributes->getConfigurableProductOptions();
+        $configurableProductLinks = $extendedAttributes->getConfigurableProductLinks();
+        if ($configurableProductOptions === null && $configurableProductLinks === null) {
+            return $result;
+        }
+        if ($configurableProductOptions !== null) {
+            $this->saveConfigurableProductOptions($result, $configurableProductOptions);
+            $result->getTypeInstance()->resetConfigurableAttributes($result);
+        }
+        if ($configurableProductLinks !== null) {
+            $this->saveConfigurableProductLinks($result, $configurableProductLinks);
+        }
+        $this->priceData->setProductPrice($result->getId(), null);
+        return $subject->get($result->getSku(), false, $result->getStoreId(), true);
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param \Magento\ConfigurableProduct\Api\Data\OptionInterface[] $options
+     * @return $this
+     */
+    protected function saveConfigurableProductOptions(
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        array $options
+    ) {
+        $existingOptionIds = [];
+        if ($product->getExtensionAttributes() !== null) {
+            $extensionAttributes = $product->getExtensionAttributes();
+            if ($extensionAttributes->getConfigurableProductOptions() !== null) {
+                $existingOptions = $extensionAttributes->getConfigurableProductOptions();
+                foreach ($existingOptions as $option) {
+                    $existingOptionIds[] = $option->getId();
+                }
+            }
+        }
+
+        $updatedOptionIds = [];
+        foreach ($options as $option) {
+            if ($option->getId()) {
+                $updatedOptionIds[] = $option->getId();
+            }
+            $this->optionRepository->save($product->getSku(), $option);
+        }
+
+        $optionIdsToDelete = array_diff($existingOptionIds, $updatedOptionIds);
+        foreach ($optionIdsToDelete as $optionId) {
+            $this->optionRepository->deleteById($product->getSku(), $optionId);
+        }
+        return $this;
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param int[] $linkIds
+     * @return $this
+     */
+    protected function saveConfigurableProductLinks(
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        array $linkIds
+    ) {
+        $configurableProductTypeResource = $this->typeConfigurableFactory->create();
+        if (!empty($linkIds)) {
+            /** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable $configurableProductType */
+            $configurableProductType = $product->getTypeInstance();
+            $configurableAttributes = $configurableProductType->getConfigurableAttributes($product);
+            $attributeCodes = [];
+            foreach ($configurableAttributes as $configurableAttribute) {
+                /** @var \Magento\Catalog\Model\Resource\Eav\Attribute $productAttribute */
+                $productAttribute = $configurableAttribute->getProductAttribute();
+                $attributeCode = $productAttribute->getAttributeCode();
+                $attributeCodes[] = $attributeCode;
+            }
+            $this->validateProductLinks($attributeCodes, $linkIds);
+        }
+
+        $configurableProductTypeResource->saveProducts($product, $linkIds);
+        return $this;
+    }
+
+    /**
+     * @param array $attributeCodes
+     * @param array $linkIds
+     * @throws InputException
+     * @return $this
+     */
+    protected function validateProductLinks(array $attributeCodes, array $linkIds)
+    {
+        $valueMap = [];
+        if (empty($attributeCodes) && !empty($linkIds)) {
+            throw new InputException(
+                __('The configurable product does not have any variation attribute.')
+            );
+        }
+
+        foreach ($linkIds as $productId) {
+            $variation = $this->productFactory->create()->load($productId);
+            if (!$variation->getId()) {
+                throw new InputException(__('Product with id "%1" does not exist.', $productId));
+            }
+            $valueKey = '';
+            foreach ($attributeCodes as $attributeCode) {
+                if (!$variation->getData($attributeCode)) {
+                    throw new InputException(
+                        __('Product with id "%1" does not contain required attribute "%2".', $productId, $attributeCode)
+                    );
+                }
+                $valueKey = $valueKey . $attributeCode . ':' . $variation->getData($attributeCode) . ';';
+            }
+            if (isset($valueMap[$valueKey])) {
+                throw new InputException(
+                    __('Products "%1" and %2 have the same set of attribute values.', $productId, $valueMap[$valueKey])
+                );
+            }
+            $valueMap[$valueKey] = $productId;
+        }
+        return $this;
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
index 5922c01bad8a3868bd867b8bd1522d3b2593ef30..c1fb0b41ef427e758954ee9728084e67eb9c5bed 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable.php
@@ -377,6 +377,18 @@ class Configurable extends \Magento\Catalog\Model\Product\Type\AbstractType
         return $product->getData($this->_configurableAttributes);
     }
 
+    /**
+     * Reset the cached configurable attributes of a product
+     *
+     * @param \Magento\Catalog\Model\Product $product
+     * @return $this
+     */
+    public function resetConfigurableAttributes($product)
+    {
+        $product->unsetData($this->_configurableAttributes);
+        return $this;
+    }
+
     /**
      * Retrieve Configurable Attributes as array
      *
diff --git a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
index f02e39626d66a7571bea42e1c5b2b1af804caf9c..8cf22a9b6dc3b4127baa3b8e9c8e5a9c0ade4dce 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Product/Type/Configurable/Attribute.php
@@ -24,7 +24,6 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
      */
     const KEY_ATTRIBUTE_ID = 'attribute_id';
     const KEY_LABEL = 'label';
-    const KEY_TYPE = 'type';
     const KEY_POSITION = 'position';
     const KEY_IS_USE_DEFAULT = 'is_use_default';
     const KEY_VALUES = 'values';
@@ -119,15 +118,6 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
         return $this->getData(self::KEY_ATTRIBUTE_ID);
     }
 
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getType()
-    {
-        return $this->getData(self::KEY_TYPE);
-    }
-
     /**
      * {@inheritdoc}
      * @codeCoverageIgnore
@@ -174,15 +164,6 @@ class Attribute extends \Magento\Framework\Model\AbstractExtensibleModel impleme
         return $this->setData(self::KEY_LABEL, $label);
     }
 
-    /**
-     * @param string $type
-     * @return $this
-     */
-    public function setType($type)
-    {
-        return $this->setData(self::KEY_TYPE, $type);
-    }
-
     /**
      * @param int $position
      * @return $this
diff --git a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php
index e556661f45b1a802fd4f9024cbcbbd4889653cd0..671aa6b136ab40b63ad67c9f7229dcdb7a8ae1f9 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable.php
@@ -180,10 +180,6 @@ class Configurable extends \Magento\Framework\Model\Resource\Db\AbstractDb
                 implode(
                     ' AND ',
                     [
-                        $this->_getReadAdapter()->quoteInto(
-                            'entity_value.entity_type_id = ?',
-                            $product->getEntityTypeId()
-                        ),
                         'entity_value.attribute_id = super_attribute.attribute_id',
                         'entity_value.store_id = 0',
                         'entity_value.entity_id = product_link.product_id'
diff --git a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
index 9d2ca8b5414c7920d4fb9235e6c6eaaaf4882368..f10a02db96f2297d1050a322c9bb92eaaa28cb91 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
@@ -263,7 +263,12 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
             $values = $this->getPriceValues();
 
             foreach ($values as $data) {
-                $this->getItemById($data['product_super_attribute_id'])->addPrice($data);
+                $item = $this->getItemById($data['product_super_attribute_id']);
+                //the price values is cached, it could have gotten out of sync with current items
+                //when a filter is added, in that case, we just ignore the data from the cache
+                if ($item) {
+                    $item->addPrice($data);
+                }
             }
         }
         return $this;
diff --git a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php
index 248ed3ede9f343883132711c056d9966431c763e..3e5c714e425e4d50f4eec11eb6f1a09879c1ec87 100644
--- a/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php
+++ b/app/code/Magento/ConfigurableProduct/Model/Resource/Product/Type/Configurable/Attribute/Price/Data.php
@@ -24,10 +24,10 @@ class Data
 
     /**
      * @param int $productId
-     * @param array $priceData
+     * @param array|null $priceData
      * @return void
      */
-    public function setProductPrice($productId, array $priceData)
+    public function setProductPrice($productId, array $priceData = null)
     {
         $this->prices[$productId] = $priceData;
     }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..34b66ecb320dc65bed96338f0435a8253b7aca5b
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionRepositoryTest.php
@@ -0,0 +1,210 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\ConfigurableProduct\Test\Unit\Model;
+
+class OptionRepositoryTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\ConfigurableProduct\Model\OptionRepository
+     */
+    protected $model;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    protected function setUp()
+    {
+        $this->productRepositoryMock = $this->getMock('\Magento\Catalog\Api\ProductRepositoryInterface');
+        $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject(
+            '\Magento\ConfigurableProduct\Model\OptionRepository',
+            [
+                'productRepository' => $this->productRepositoryMock,
+            ]
+        );
+    }
+
+    public function testGet()
+    {
+        $productSku = "configurable";
+        $optionId = 3;
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+
+        $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
+        $optionMock->expects($this->once())
+            ->method('getId')
+            ->willReturn($optionId);
+        $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['getConfigurableProductOptions'])
+            ->getMock();
+        $productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn([$optionMock]);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($productExtensionMock);
+
+        $this->assertEquals($optionMock, $this->model->get($productSku, $optionId));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Only implemented for configurable product: configurable
+     */
+    public function testGetNotConfigurableProduct()
+    {
+        $productSku = "configurable";
+        $optionId = 3;
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn('simple');
+
+        $this->model->get($productSku, $optionId);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedExceptionMessage Requested option doesn't exist: 3
+     */
+    public function testGetEmptyExtensionAttribute()
+    {
+        $productSku = "configurable";
+        $optionId = 3;
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn(null);
+
+        $this->model->get($productSku, $optionId);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\NoSuchEntityException
+     * @expectedExceptionMessage Requested option doesn't exist: 3
+     */
+    public function testGetOptionIdNotFound()
+    {
+        $productSku = "configurable";
+        $optionId = 3;
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+
+        $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
+        $optionMock->expects($this->once())
+            ->method('getId')
+            ->willReturn(6);
+        $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['getConfigurableProductOptions'])
+            ->getMock();
+        $productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn([$optionMock]);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($productExtensionMock);
+
+        $this->model->get($productSku, $optionId);
+    }
+
+    public function testGetList()
+    {
+        $productSku = "configurable";
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+
+        $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
+        $productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['getConfigurableProductOptions'])
+            ->getMock();
+        $productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn([$optionMock]);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($productExtensionMock);
+
+        $this->assertEquals([$optionMock], $this->model->getList($productSku));
+    }
+
+    public function testGetListNullExtensionAttribute()
+    {
+        $productSku = "configurable";
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn(null);
+
+        $this->assertEquals([], $this->model->getList($productSku));
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Only implemented for configurable product: configurable
+     */
+    public function testGetListNotConfigurableProduct()
+    {
+        $productSku = "configurable";
+
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku)
+            ->willReturn($this->productMock);
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn('simple');
+
+        $this->model->getList($productSku);
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionTypesListTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionTypesListTest.php
deleted file mode 100644
index 79867bb302e321ff1da0c4e887a1d2f754bb115b..0000000000000000000000000000000000000000
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/OptionTypesListTest.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\ConfigurableProduct\Test\Unit\Model;
-
-class OptionTypesListTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * @var OptionTypesList
-     */
-    protected $model;
-
-    /**
-     * @var \PHPUnit_Framework_MockObject_MockObject
-     */
-    protected $sourceMock;
-
-    protected function setUp()
-    {
-        $this->sourceMock = $this->getMock('\Magento\Catalog\Model\System\Config\Source\Inputtype', [], [], '', false);
-        $this->model = new \Magento\ConfigurableProduct\Model\OptionTypesList($this->sourceMock);
-    }
-
-    public function testGetItems()
-    {
-        $data = [
-            ['value' => 'multiselect', 'label' => __('Multiple Select')],
-            ['value' => 'select', 'label' => __('Dropdown')]
-        ];
-        $this->sourceMock->expects($this->once())->method('toOptionArray')->willReturn($data);
-        $expected = ['multiselect', 'select'];
-        $this->assertEquals($expected, $this->model->getItems());
-    }
-}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..af3e73f0b855eeddb16f965f7438d8a16cbcd601
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AfterProductLoadTest.php
@@ -0,0 +1,239 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin;
+
+use Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad;
+
+class AfterProductLoadTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var AfterProductLoad
+     */
+    protected $model;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $optionValueFactory;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionFactory;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject|Magento\Catalog\Model\Product
+     */
+    protected $productMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $configurableProductTypeInstanceMock;
+
+    protected function setUp()
+    {
+        $this->optionValueFactory = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Api\Data\OptionValueInterfaceFactory'
+        )->disableOriginalConstructor()->getMock();
+        $this->productMock = $this->getMockBuilder('Magento\Catalog\Model\Product')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->configurableProductTypeInstanceMock = $this->getMock(
+            '\Magento\ConfigurableProduct\Model\Product\Type\Configurable',
+            [],
+            [],
+            '',
+            false
+        );
+        $this->productMock->expects($this->any())
+            ->method('getTypeInstance')
+            ->willReturn($this->configurableProductTypeInstanceMock);
+        $this->productExtensionFactory = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->model = new \Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad(
+            $this->productExtensionFactory,
+            $this->optionValueFactory
+        );
+    }
+
+    protected function setupOptions()
+    {
+        $optionValues = [
+            [
+                'value_index' => 5,
+                'pricing_value' => 10,
+                'is_percent' => 0,
+            ],
+            [
+                'value_index' => 6,
+                'pricing_value' => 5,
+                'is_percent' => 1,
+            ],
+        ];
+        $optionMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute')
+            ->disableOriginalConstructor()
+            ->setMethods(['getPrices', 'setValues'])
+            ->getMock();
+        $optionMock1->expects($this->once())
+            ->method('getPrices')
+            ->willReturn($optionValues);
+
+        $optionValueMock1 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $optionValueMock1->expects($this->once())
+            ->method('setValueIndex')
+            ->with($optionValues[0]['value_index'])
+            ->willReturnSelf();
+        $optionValueMock1->expects($this->once())
+            ->method('setPricingValue')
+            ->with($optionValues[0]['pricing_value'])
+            ->willReturnSelf();
+        $optionValueMock1->expects($this->once())
+            ->method('setIsPercent')
+            ->with($optionValues[0]['is_percent'])
+            ->willReturnSelf();
+
+        $optionValueMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Api\Data\OptionValueInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $optionValueMock2->expects($this->once())
+            ->method('setValueIndex')
+            ->with($optionValues[1]['value_index'])
+            ->willReturnSelf();
+        $optionValueMock2->expects($this->once())
+            ->method('setPricingValue')
+            ->with($optionValues[1]['pricing_value'])
+            ->willReturnSelf();
+        $optionValueMock2->expects($this->once())
+            ->method('setIsPercent')
+            ->with($optionValues[1]['is_percent'])
+            ->willReturnSelf();
+
+        $this->optionValueFactory->expects($this->at(0))
+            ->method('create')
+            ->willReturn($optionValueMock1);
+        $optionMock1->expects($this->once())
+            ->method('setValues')
+            ->with([$optionValueMock1, $optionValueMock2]);
+
+        $optionMock2 = $this->getMockBuilder('\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute')
+            ->disableOriginalConstructor()
+            ->setMethods(['getPrices', 'setValues'])
+            ->getMock();
+        $optionMock2->expects($this->once())
+            ->method('getPrices')
+            ->willReturn([]);
+        $optionMock2->expects($this->once())
+            ->method('setValues')
+            ->with([]);
+        $this->optionValueFactory->expects($this->at(1))
+            ->method('create')
+            ->willReturn($optionValueMock2);
+
+        $options = [$optionMock1, $optionMock2];
+
+        $this->configurableProductTypeInstanceMock->expects($this->once())
+            ->method('getConfigurableAttributes')
+            ->with($this->productMock)
+            ->willReturn($options);
+        return $options;
+    }
+
+    protected function setupLinks()
+    {
+        $id = 5;
+        $links = [6, 7];
+        $this->productMock->expects($this->once())
+            ->method('getId')
+            ->willReturn($id);
+        $this->configurableProductTypeInstanceMock->expects($this->once())
+            ->method('getChildrenIds')
+            ->with($id)
+            ->willReturn([$links]);
+        return $links;
+    }
+
+    public function testAfterLoad()
+    {
+        $options = $this->setupOptions();
+        $links = $this->setupLinks();
+
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+
+        $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks'])
+            ->getMock();
+
+        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions')
+            ->with($options)
+            ->willReturnSelf();
+        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks')
+            ->with($links)
+            ->willReturnSelf();
+
+        $this->productExtensionFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($extensionAttributeMock);
+
+        $this->productMock->expects($this->once())
+            ->method('setExtensionAttributes')
+            ->with($extensionAttributeMock)
+            ->willReturnSelf();
+
+        $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock));
+    }
+
+    public function testAfterLoadNotConfigurableProduct()
+    {
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn('simple');
+
+        $this->productMock->expects($this->never())
+            ->method('getExtensionAttributes');
+        $this->productMock->expects($this->never())
+            ->method('setExtensionAttributes');
+        $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock));
+    }
+
+    public function testAfterLoadNoLinks()
+    {
+        $options = $this->setupOptions();
+
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+
+        $extensionAttributeMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['setConfigurableProductOptions', 'setConfigurableProductLinks'])
+            ->getMock();
+
+        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductOptions')
+            ->with($options)
+            ->willReturnSelf();
+        $extensionAttributeMock->expects($this->once())->method('setConfigurableProductLinks')
+            ->with([])
+            ->willReturnSelf();
+
+        $this->productExtensionFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($extensionAttributeMock);
+
+        $this->productMock->expects($this->once())
+            ->method('setExtensionAttributes')
+            ->with($extensionAttributeMock)
+            ->willReturnSelf();
+
+        $this->assertEquals($this->productMock, $this->model->afterLoad($this->productMock));
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6e750f9e13fcee390a5ae1a4fab496a507816f6c
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
@@ -0,0 +1,553 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\ConfigurableProduct\Test\Unit\Model\Plugin;
+
+use Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave;
+
+class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var AroundProductRepositorySave
+     */
+    protected $plugin;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productOptionRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $configurableTypeFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $priceDataMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productInterfaceMock;
+
+    /**
+     * @var \Closure
+     */
+    protected $closureMock;
+
+    protected function setUp()
+    {
+        $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface');
+        $this->productOptionRepositoryMock = $this->getMock(
+            'Magento\ConfigurableProduct\Api\OptionRepositoryInterface'
+        );
+        $this->configurableTypeFactoryMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Resource\Product\Type\ConfigurableFactory'
+        )->disableOriginalConstructor()->getMock();
+        $this->priceDataMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data'
+        )->disableOriginalConstructor()->getMock();
+        $this->productInterfaceMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
+        $this->productMock = $this->getMock(
+            'Magento\Catalog\Model\Product',
+            ['getExtensionAttributes', 'getTypeId', 'getSku', 'getStoreId', 'getId', 'getTypeInstance'],
+            [],
+            '',
+            false
+        );
+        $this->closureMock = function () {
+            return $this->productMock;
+        };
+
+        $this->productFactoryMock = $this->getMockBuilder('\Magento\Catalog\Model\ProductFactory')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->plugin = $objectManager->getObject(
+            'Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave',
+            [
+                'optionRepository' => $this->productOptionRepositoryMock,
+                'productFactory' => $this->productFactoryMock,
+                'priceData' => $this->priceDataMock,
+                'typeConfigurableFactory' => $this->configurableTypeFactoryMock
+            ]
+        );
+
+        $this->productExtensionMock = $this->getMock(
+            'Magento\Catalog\Api\Data\ProductExtension',
+            [
+                'getConfigurableProductOptions',
+                'getConfigurableProductLinks',
+                'setConfigurableProductOptions',
+                'setConfigurableProductLinks',
+            ],
+            [],
+            '',
+            false
+        );
+    }
+
+    public function testAroundSaveWhenProductIsSimple()
+    {
+        $this->productMock->expects($this->once())->method('getTypeId')->willReturn('simple');
+        $this->productMock->expects($this->never())->method('getExtensionAttributes');
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    public function testAroundSaveWithoutOptions()
+    {
+        $this->productInterfaceMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+        $this->productInterfaceMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn(null);
+
+        $this->priceDataMock->expects($this->never())
+            ->method('setProductPrice');
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock)
+        );
+    }
+
+    protected function setupProducts($productIds, $attributeCode, $additionalProductId = null)
+    {
+        $count = 0;
+        $products = [];
+        foreach ($productIds as $productId) {
+            $productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
+                ->disableOriginalConstructor()
+                ->getMock();
+            $productMock->expects($this->once())
+                ->method('load')
+                ->with($productId)
+                ->willReturnSelf();
+            $productMock->expects($this->once())
+                ->method('getId')
+                ->willReturn($productId);
+            $productMock->expects($this->any())
+                ->method('getData')
+                ->with($attributeCode)
+                ->willReturn($productId);
+            $this->productFactoryMock->expects($this->at($count))
+                ->method('create')
+                ->willReturn($productMock);
+            $products[] = $productMock;
+            $count++;
+        }
+
+        if ($additionalProductId) {
+            $nonExistingProductMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
+                ->disableOriginalConstructor()
+                ->getMock();
+            $nonExistingProductMock->expects($this->once())
+                ->method('load')
+                ->with($additionalProductId)
+                ->willReturnSelf();
+            $this->productFactoryMock->expects($this->at($count))
+                ->method('create')
+                ->willReturn($nonExistingProductMock);
+            $products[] = $nonExistingProductMock;
+        }
+        return $products;
+    }
+
+    protected function setupConfigurableProductAttributes($attributeCodes)
+    {
+        $configurableProductTypeMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Product\Type\Configurable'
+        )->disableOriginalConstructor()->getMock();
+
+        $this->productMock->expects($this->once())
+            ->method('getTypeInstance')
+            ->willReturn($configurableProductTypeMock);
+
+        $configurableAttributes = [];
+        foreach ($attributeCodes as $attributeCode) {
+            $configurableAttribute = $this->getMockBuilder(
+                '\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute'
+            )->setMethods(['getProductAttribute'])
+                ->disableOriginalConstructor()
+                ->getMock();
+            $productAttributeMock = $this->getMockBuilder('\Magento\Catalog\Model\Resource\Eav\Attribute')
+                ->disableOriginalConstructor()
+                ->getMock();
+            $productAttributeMock->expects($this->once())
+                ->method('getAttributeCode')
+                ->willReturn($attributeCode);
+            $configurableAttribute->expects($this->once())
+                ->method('getProductAttribute')
+                ->willReturn($productAttributeMock);
+            $configurableAttributes[] = $configurableAttribute;
+        }
+
+        $configurableProductTypeMock->expects($this->once())
+            ->method('getConfigurableAttributes')
+            ->with($this->productMock)
+            ->willReturn($configurableAttributes);
+
+        return $this;
+    }
+
+    public function testAroundSaveWithLinks()
+    {
+        $links = [4, 5];
+        $configurableAttributeCode = 'color';
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn($links);
+
+        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
+        $this->setupProducts($links, $configurableAttributeCode);
+
+        $configurableTypeMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable'
+        )->disableOriginalConstructor()->getMock();
+        $this->configurableTypeFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($configurableTypeMock);
+        $configurableTypeMock->expects($this->once())
+            ->method('saveProducts')
+            ->with($this->productMock, $links);
+
+        $productId = 3;
+        $this->productMock->expects($this->any())
+            ->method('getId')
+            ->willReturn($productId);
+        $this->priceDataMock->expects($this->once())
+            ->method('setProductPrice')
+            ->with($productId, null);
+
+        $productSku = 'configurable-sku';
+        $this->productMock->expects($this->any())
+            ->method('getSku')
+            ->willReturn($productSku);
+        $newProductMock = $this->setupReload($productSku);
+
+        $this->assertEquals(
+            $newProductMock,
+            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Product with id "6" does not exist.
+     */
+    public function testAroundSaveWithNonExistingLinks()
+    {
+        $links = [4, 5];
+        $nonExistingId = 6;
+        $configurableAttributeCode = 'color';
+
+        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
+        $productMocks = $this->setupProducts($links, $configurableAttributeCode, $nonExistingId);
+        $nonExistingProductMock = $productMocks[2];
+        $nonExistingProductMock->expects($this->once())
+            ->method('getId')
+            ->willReturn(null);
+        $links[] = $nonExistingId;
+
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn($links);
+
+        $configurableTypeMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable'
+        )->disableOriginalConstructor()->getMock();
+        $this->configurableTypeFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($configurableTypeMock);
+        $configurableTypeMock->expects($this->never())
+            ->method('saveProducts')
+            ->with($this->productMock, $links);
+
+        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Product with id "6" does not contain required attribute "color".
+     */
+    public function testAroundSaveWithLinksWithMissingAttribute()
+    {
+        $links = [4, 5];
+        $simpleProductId = 6;
+        $configurableAttributeCode = 'color';
+
+        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
+        $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId);
+        $simpleProductMock = $productMocks[2];
+        $simpleProductMock->expects($this->once())
+            ->method('getId')
+            ->willReturn($simpleProductId);
+        $simpleProductMock->expects($this->any())
+            ->method('getData')
+            ->with($configurableAttributeCode)
+            ->willReturn(null);
+
+        $links[] = $simpleProductId;
+
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn($links);
+
+        $configurableTypeMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable'
+        )->disableOriginalConstructor()->getMock();
+        $this->configurableTypeFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($configurableTypeMock);
+        $configurableTypeMock->expects($this->never())
+            ->method('saveProducts')
+            ->with($this->productMock, $links);
+
+        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage Products "6" and 4 have the same set of attribute values.
+     */
+    public function testAroundSaveWithLinksWithDuplicateAttributes()
+    {
+        $links = [4, 5];
+        $simpleProductId = 6;
+        $configurableAttributeCode = 'color';
+
+        $this->setupConfigurableProductAttributes([$configurableAttributeCode]);
+        $productMocks = $this->setupProducts($links, $configurableAttributeCode, $simpleProductId);
+        $simpleProductMock = $productMocks[2];
+        $simpleProductMock->expects($this->once())
+            ->method('getId')
+            ->willReturn($simpleProductId);
+        $simpleProductMock->expects($this->any())
+            ->method('getData')
+            ->with($configurableAttributeCode)
+            ->willReturn(4);
+
+        $links[] = $simpleProductId;
+
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn($links);
+
+        $configurableTypeMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable'
+        )->disableOriginalConstructor()->getMock();
+        $this->configurableTypeFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($configurableTypeMock);
+        $configurableTypeMock->expects($this->never())
+            ->method('saveProducts')
+            ->with($this->productMock, $links);
+
+        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
+    }
+
+    /**
+     * @expectedException \Magento\Framework\Exception\InputException
+     * @expectedExceptionMessage The configurable product does not have any variation attribute.
+     */
+    public function testAroundSaveWithLinksWithoutVariationAttributes()
+    {
+        $links = [4, 5];
+
+        $this->setupConfigurableProductAttributes([]);
+
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductOptions')
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getConfigurableProductLinks')
+            ->willReturn($links);
+
+        $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock);
+    }
+
+    public function testAroundSaveWithOptions()
+    {
+        $productSku = "configurable_sku";
+        $this->productInterfaceMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE);
+        //two options with id 5 and 6
+        $options = $this->setupOptions([5, 6]);
+        //two existing options with id 4 and 5
+        $this->setupExistingOptions([4, 5]);
+
+        $this->productMock->expects($this->any())->method('getSku')
+            ->will($this->returnValue($productSku));
+
+        $this->productOptionRepositoryMock->expects($this->at(0))
+            ->method('save')
+            ->with($productSku, $options[0]);
+        $this->productOptionRepositoryMock->expects($this->at(1))
+            ->method('save')
+            ->with($productSku, $options[1]);
+        $this->productOptionRepositoryMock->expects($this->at(2))
+            ->method('deleteById')
+            ->with($productSku, 4);
+
+        $configurableProductTypeMock = $this->getMockBuilder(
+            '\Magento\ConfigurableProduct\Model\Product\Type\Configurable'
+        )->disableOriginalConstructor()->getMock();
+        $configurableProductTypeMock->expects($this->once())
+            ->method('resetConfigurableAttributes')
+            ->with($this->productMock)
+            ->willReturnSelf();
+        $this->productMock->expects($this->any())
+            ->method('getTypeInstance')
+            ->willReturn($configurableProductTypeMock);
+
+        $productId = 3;
+        $this->productMock->expects($this->once())
+            ->method('getId')
+            ->willReturn($productId);
+        $this->priceDataMock->expects($this->once())
+            ->method('setProductPrice')
+            ->with($productId, null);
+
+        $newProductMock = $this->setupReload($productSku);
+
+        $this->assertEquals(
+            $newProductMock,
+            $this->plugin->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productInterfaceMock)
+        );
+    }
+
+    protected function setupReload($productSku)
+    {
+        $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
+            ->disableOriginalConstructor()->getMock();
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku, false, null, true)
+            ->willReturn($newProductMock);
+        return $newProductMock;
+    }
+
+    protected function setupExistingOptions(array $existingOptionIds)
+    {
+        $options = [];
+        foreach ($existingOptionIds as $existingOptionId) {
+            $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
+            $optionMock->expects($this->any())
+                ->method('getId')
+                ->willReturn($existingOptionId);
+            $options[] = $optionMock;
+        }
+
+        $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension')
+            ->disableOriginalConstructor()
+            ->setMethods(['getConfigurableProductOptions'])
+            ->getMock();
+        $productExtensionMock->expects($this->any())
+            ->method('getConfigurableProductOptions')
+            ->willReturn($options);
+
+        $this->productMock->expects($this->any())
+            ->method('getExtensionAttributes')
+            ->willReturn($productExtensionMock);
+    }
+
+    protected function setupOptions(array $optionIds)
+    {
+        $options = [];
+        foreach ($optionIds as $optionId) {
+            $optionMock = $this->getMock('\Magento\ConfigurableProduct\Api\Data\OptionInterface');
+            $optionMock->expects($this->any())
+                ->method('getId')
+                ->willReturn($optionId);
+            $options[] = $optionMock;
+        }
+
+        $productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension')
+            ->disableOriginalConstructor()
+            ->setMethods(['getConfigurableProductOptions', 'getConfigurableProductLinks'])
+            ->getMock();
+        $productExtensionMock->expects($this->any())
+            ->method('getConfigurableProductOptions')
+            ->willReturn($options);
+        $productExtensionMock->expects($this->any())
+            ->method('getConfigurableProductLinks')
+            ->willReturn(null);
+
+        $this->productInterfaceMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($productExtensionMock);
+        return $options;
+    }
+}
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
index d0c5ae32ab1dc80a675f55233119de9eedcdc708..4ca090adef44b5e3786bd066067c7c6aaa03ac53 100644
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
+++ b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/Product/Type/ConfigurableTest.php
@@ -408,6 +408,19 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
         ];
     }
 
+    public function testResetConfigurableAttributes()
+    {
+        $product = $this->getMockBuilder('\Magento\Catalog\Model\Product')
+            ->setMethods(['unsetData', '__wakeup', '__sleep'])
+            ->disableOriginalConstructor()
+            ->getMock();
+        $product->expects($this->any())->method('unsetData')
+            ->with('_cache_instance_configurable_attributes')
+            ->will($this->returnSelf());
+
+        $this->assertEquals($this->_model, $this->_model->resetConfigurableAttributes($product));
+    }
+
     public function testHasOptions()
     {
         $productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
@@ -584,10 +597,6 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
             ->setMethods(['getId', 'getAttributeCode'])
             ->disableOriginalConstructor()
             ->getMock();
-        $productResource = $this->getMockBuilder('\Magento\Catalog\Model\Resource\Product')
-            ->setMethods(['__wakeup', 'loadAllAttributes', 'getSortedAttributes'])
-            ->disableOriginalConstructor()
-            ->getMock();
         $productCollection = $this->getMockBuilder(
             'Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Product\Collection'
         )
@@ -616,14 +625,12 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
             ->method('getData')
             ->with('_cache_instance_store_filter')
             ->willReturn('some_filter');
-        $productMock->expects($this->once())->method('hasData')->willReturn(true);
-        $productMock->expects($this->at(3))->method('getData')->willReturn([$usedProductMock]);
-        $productMock->expects($this->once())->method('getResource')->willReturn($productResource);
-        $productMock->expects($this->once())->method('getAttributeSetId')->willReturn(5);
-        $productResource->expects($this->once())->method('loadAllAttributes')->with($productMock)->willReturnSelf();
-        $productResource->expects($this->once())
-            ->method('getSortedAttributes')
-            ->with(5)
+        $productMock->expects($this->any())->method('hasData')->willReturn(true);
+        $productMock->expects($this->at(3))->method('getData')
+            ->with('_cache_instance_products')
+            ->willReturn([$usedProductMock]);
+        $productMock->expects($this->at(5))->method('getData')
+            ->with('_cache_instance_product_set_attributes')
             ->willReturn([$eavAttributeMock]);
         $eavAttributeMock->expects($this->once())->method('getId')->willReturn(1);
         $eavAttributeMock->expects($this->once())->method('getAttributeCode')->willReturn('attr_code');
diff --git a/app/code/Magento/ConfigurableProduct/etc/di.xml b/app/code/Magento/ConfigurableProduct/etc/di.xml
index fcd5d77b81e4de8e9a5df180968909b02d290221..490c15a70a51fc150d80d3e8b42dd5c4ef8aa47e 100644
--- a/app/code/Magento/ConfigurableProduct/etc/di.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/di.xml
@@ -6,7 +6,6 @@
  */
 -->
 <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
-    <preference for="Magento\ConfigurableProduct\Api\OptionTypesListInterface" type="Magento\ConfigurableProduct\Model\OptionTypesList" />
     <preference for="Magento\ConfigurableProduct\Api\ConfigurableProductManagementInterface" type="Magento\ConfigurableProduct\Model\ConfigurableProductManagement" />
     <preference for="Magento\ConfigurableProduct\Api\LinkManagementInterface" type="Magento\ConfigurableProduct\Model\LinkManagement" />
     <preference for="Magento\ConfigurableProduct\Api\OptionRepositoryInterface" type="Magento\ConfigurableProduct\Model\OptionRepository" />
@@ -59,6 +58,12 @@
     <type name="Magento\Catalog\Model\Product\Type">
         <plugin name="configurable_output" type="Magento\ConfigurableProduct\Model\Product\Type\Plugin" />
     </type>
+    <type name="Magento\Catalog\Api\ProductRepositoryInterface">
+        <plugin name="configurableProductSaveOptions" type="\Magento\ConfigurableProduct\Model\Plugin\AroundProductRepositorySave"/>
+    </type>
+    <type name="Magento\Catalog\Model\Product">
+        <plugin name="configurableProductLoadAfter" type="\Magento\ConfigurableProduct\Model\Plugin\AfterProductLoad"/>
+    </type>
     <virtualType name="Magento\ConfigurableProduct\Pricing\Price\Pool" type="Magento\Framework\Pricing\Price\Pool">
         <arguments>
             <argument name="prices" xsi:type="array">
diff --git a/app/code/Magento/ConfigurableProduct/etc/service_data_attributes.xml b/app/code/Magento/ConfigurableProduct/etc/service_data_attributes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..74edcdbf65a91e08cc384b50b416deff63e8780c
--- /dev/null
+++ b/app/code/Magento/ConfigurableProduct/etc/service_data_attributes.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
+        <attribute code="configurable_product_options" type="Magento\ConfigurableProduct\Api\Data\OptionInterface[]" />
+        <attribute code="configurable_product_links" type="int[]" />
+    </extension_attributes>
+</config>
diff --git a/app/code/Magento/ConfigurableProduct/etc/webapi.xml b/app/code/Magento/ConfigurableProduct/etc/webapi.xml
index 9c68aa71869f44782120dda382eb7de6c34e9f2d..158db3c3c5fa8313960e1735250f3f7cfb250388 100644
--- a/app/code/Magento/ConfigurableProduct/etc/webapi.xml
+++ b/app/code/Magento/ConfigurableProduct/etc/webapi.xml
@@ -43,12 +43,6 @@
             <resource ref="Magento_Catalog::products"/>
         </resources>
     </route>
-    <route url="/V1/configurable-products/options/types" method="GET">
-        <service class="Magento\ConfigurableProduct\Api\OptionTypesListInterface" method="getItems" />
-        <resources>
-            <resource ref="Magento_Catalog::products" />
-        </resources>
-    </route>
     <route url="/V1/configurable-products/:sku/options" method="POST">
         <service class="Magento\ConfigurableProduct\Api\OptionRepositoryInterface" method="save" />
         <resources>
diff --git a/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php b/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php
index 7e187a71e5c08bdad11cf84b9cfdc3c9ca85632c..b25f4c6d0b3a3cb83b3693f1d2d7c08fb4859247 100644
--- a/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php
+++ b/app/code/Magento/Customer/Model/Config/Backend/Address/Street.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Customer\Model\Config\Backend\Address;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 /**
  * Line count config model for customer address street attribute
  *
@@ -64,7 +66,7 @@ class Street extends \Magento\Framework\App\Config\Value
                 }
                 break;
 
-            case \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT:
+            case ScopeConfigInterface::SCOPE_TYPE_DEFAULT:
                 $attribute->setData('multiline_count', $value);
                 break;
         }
diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php
index c65cda9e51bbae8fa77d7f89b0bb440a7fb121f7..776c8e74e1ace9aa8cc4205174c6fb0977032368 100644
--- a/app/code/Magento/Customer/Model/Customer.php
+++ b/app/code/Magento/Customer/Model/Customer.php
@@ -12,6 +12,7 @@ use Magento\Customer\Model\Resource\Address\CollectionFactory;
 use Magento\Customer\Model\Resource\Customer as ResourceCustomer;
 use Magento\Customer\Api\Data\CustomerInterfaceFactory;
 use Magento\Customer\Model\Data\Customer as CustomerData;
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\Reflection\DataObjectProcessor;
 use Magento\Framework\Exception\EmailNotConfirmedException;
 use Magento\Framework\Exception\InvalidEmailOrPasswordException;
@@ -1310,7 +1311,7 @@ class Customer extends \Magento\Framework\Model\AbstractModel
     {
         return (int)$this->_scopeConfig->getValue(
             self::XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD,
-            \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+            ScopeConfigInterface::SCOPE_TYPE_DEFAULT
         );
     }
 
diff --git a/app/code/Magento/Customer/Model/Resource/Address.php b/app/code/Magento/Customer/Model/Resource/Address.php
index 57a5c1cbcf3d9c28e975a999395d5ffd18600696..3b6ae0a28c8f9e812e762b72dbfe45d35681ebe9 100644
--- a/app/code/Magento/Customer/Model/Resource/Address.php
+++ b/app/code/Magento/Customer/Model/Resource/Address.php
@@ -45,13 +45,22 @@ class Address extends \Magento\Eav\Model\Entity\AbstractEntity
      */
     protected function _construct()
     {
-        $resource = $this->_resource;
-        $this->setType(
-            'customer_address'
-        )->setConnection(
-            $resource->getConnection('customer_read'),
-            $resource->getConnection('customer_write')
-        );
+        $this->_read = 'customer_read';
+        $this->_write = 'customer_write';
+    }
+
+    /**
+     * Getter and lazy loader for _type
+     *
+     * @throws \Magento\Framework\Exception\LocalizedException
+     * @return \Magento\Eav\Model\Entity\Type
+     */
+    public function getEntityType()
+    {
+        if (empty($this->_type)) {
+            $this->setType('customer_address');
+        }
+        return parent::getEntityType();
     }
 
     /**
diff --git a/app/code/Magento/Customer/Model/Session.php b/app/code/Magento/Customer/Model/Session.php
index c01cd99d642cf7cb72c963fc20513f2e8e525235..4212be78b59114439b049203b03eca3a488991e4 100644
--- a/app/code/Magento/Customer/Model/Session.php
+++ b/app/code/Magento/Customer/Model/Session.php
@@ -102,6 +102,7 @@ class Session extends \Magento\Framework\Session\SessionManager
      * @param \Magento\Framework\Session\StorageInterface $storage
      * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
      * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
+     * @param \Magento\Framework\App\State $appState
      * @param Share $configShare
      * @param \Magento\Framework\Url\Helper\Data $coreUrl
      * @param \Magento\Customer\Model\Url $customerUrl
@@ -113,6 +114,7 @@ class Session extends \Magento\Framework\Session\SessionManager
      * @param \Magento\Framework\App\Http\Context $httpContext
      * @param CustomerRepositoryInterface $customerRepository
      * @param GroupManagementInterface $groupManagement
+     * @throws \Magento\Framework\Exception\SessionException
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
     public function __construct(
@@ -124,6 +126,7 @@ class Session extends \Magento\Framework\Session\SessionManager
         \Magento\Framework\Session\StorageInterface $storage,
         \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
         \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory,
+        \Magento\Framework\App\State $appState,
         Config\Share $configShare,
         \Magento\Framework\Url\Helper\Data $coreUrl,
         \Magento\Customer\Model\Url $customerUrl,
@@ -154,9 +157,9 @@ class Session extends \Magento\Framework\Session\SessionManager
             $validator,
             $storage,
             $cookieManager,
-            $cookieMetadataFactory
+            $cookieMetadataFactory,
+            $appState
         );
-        $this->start();
         $this->groupManagement = $groupManagement;
         $this->_eventManager->dispatch('customer_session_init', ['customer_session' => $this]);
     }
diff --git a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php
index 5d1e91708b4fcd6da306011c551415bf3586d66a..834c64df6701f19605ccd05fb8298d1def87fa4a 100644
--- a/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Block/Widget/DobTest.php
@@ -10,6 +10,7 @@ namespace Magento\Customer\Test\Unit\Block\Widget;
 
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Customer\Block\Widget\Dob;
+use Magento\Framework\Locale\Resolver;
 
 class DobTest extends \PHPUnit_Framework_TestCase
 {
@@ -69,7 +70,7 @@ class DobTest extends \PHPUnit_Framework_TestCase
         $localeResolver = $this->getMock('\Magento\Framework\Locale\ResolverInterface');
         $localeResolver->expects($this->any())
             ->method('getLocale')
-            ->willReturn(\Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE);
+            ->willReturn(Resolver::DEFAULT_LOCALE);
         $timezone = $objectManager->getObject(
             'Magento\Framework\Stdlib\DateTime\Timezone',
             ['localeResolver' => $localeResolver]
diff --git a/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php b/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php
index fe17aa1aca70eb47723da99b6c9a8639944b76de..c15cf1596c8245fb30d8a79ba097923d545c26b0 100644
--- a/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php
+++ b/app/code/Magento/Customer/Test/Unit/Model/Resource/AddressTest.php
@@ -18,6 +18,9 @@ class AddressTest extends \PHPUnit_Framework_TestCase
     /** @var \Magento\Customer\Model\CustomerFactory|\PHPUnit_Framework_MockObject_MockObject */
     protected $customerFactory;
 
+    /** @var \Magento\Eav\Model\Entity\Type */
+    protected $eavConfigType;
+
     protected function setUp()
     {
         $this->addressResource = (new ObjectManagerHelper($this))->getObject(
@@ -178,16 +181,16 @@ class AddressTest extends \PHPUnit_Framework_TestCase
                 )
             );
 
-        $eavConfigType = $this->getMock(
+        $this->eavConfigType = $this->getMock(
             'Magento\Eav\Model\Entity\Type',
             ['getEntityIdField', 'getId', 'getEntityTable', '__wakeup'],
             [],
             '',
             false
         );
-        $eavConfigType->expects($this->any())->method('getEntityIdField')->willReturn(false);
-        $eavConfigType->expects($this->any())->method('getId')->willReturn(false);
-        $eavConfigType->expects($this->any())->method('getEntityTable')->willReturn('customer_address_entity');
+        $this->eavConfigType->expects($this->any())->method('getEntityIdField')->willReturn(false);
+        $this->eavConfigType->expects($this->any())->method('getId')->willReturn(false);
+        $this->eavConfigType->expects($this->any())->method('getEntityTable')->willReturn('customer_address_entity');
 
         $eavConfig = $this->getMock(
             'Magento\Eav\Model\Config',
@@ -199,10 +202,10 @@ class AddressTest extends \PHPUnit_Framework_TestCase
         $eavConfig->expects($this->any())
             ->method('getEntityType')
             ->with('customer_address')
-            ->willReturn($eavConfigType);
+            ->willReturn($this->eavConfigType);
         $eavConfig->expects($this->any())
             ->method('getEntityAttributeCodes')
-            ->with($eavConfigType)
+            ->with($this->eavConfigType)
             ->willReturn(
                 [
                     'entity_type_id',
@@ -217,13 +220,13 @@ class AddressTest extends \PHPUnit_Framework_TestCase
         $eavConfig->expects($this->any())
             ->method('getAttribute')
             ->willReturnMap([
-                [$eavConfigType, 'entity_type_id', $attributeMock],
-                [$eavConfigType, 'attribute_set_id', $attributeMock],
-                [$eavConfigType, 'created_at', $attributeMock],
-                [$eavConfigType, 'updated_at', $attributeMock],
-                [$eavConfigType, 'parent_id', $attributeMock],
-                [$eavConfigType, 'increment_id', $attributeMock],
-                [$eavConfigType, 'entity_id', $attributeMock],
+                [$this->eavConfigType, 'entity_type_id', $attributeMock],
+                [$this->eavConfigType, 'attribute_set_id', $attributeMock],
+                [$this->eavConfigType, 'created_at', $attributeMock],
+                [$this->eavConfigType, 'updated_at', $attributeMock],
+                [$this->eavConfigType, 'parent_id', $attributeMock],
+                [$this->eavConfigType, 'increment_id', $attributeMock],
+                [$this->eavConfigType, 'entity_id', $attributeMock],
             ]);
 
         return $eavConfig;
@@ -261,4 +264,9 @@ class AddressTest extends \PHPUnit_Framework_TestCase
         $this->customerFactory = $this->getMock('Magento\Customer\Model\CustomerFactory', ['create'], [], '', false);
         return $this->customerFactory;
     }
+
+    public function testGetType()
+    {
+        $this->assertSame($this->eavConfigType, $this->addressResource->getEntityType());
+    }
 }
diff --git a/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php b/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php
index af413fcd477b0b044e36d1bc9ed4bfc3c6637eb5..cb54368e519392d63b842cd6227cce1644e51c7d 100644
--- a/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php
+++ b/app/code/Magento/DesignEditor/Model/Url/NavigationMode.php
@@ -38,7 +38,7 @@ class NavigationMode extends \Magento\Framework\Url
      * @param \Magento\Framework\Url\ScopeResolverInterface $scopeResolver
      * @param \Magento\Framework\Session\Generic $session
      * @param \Magento\Framework\Session\SidResolverInterface $sidResolver
-     * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver
+     * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory
      * @param \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
      * @param string $scopeType
@@ -53,7 +53,7 @@ class NavigationMode extends \Magento\Framework\Url
         \Magento\Framework\Url\ScopeResolverInterface $scopeResolver,
         \Magento\Framework\Session\Generic $session,
         \Magento\Framework\Session\SidResolverInterface $sidResolver,
-        \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver,
+        \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory,
         \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver,
         \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
         $scopeType,
@@ -75,7 +75,7 @@ class NavigationMode extends \Magento\Framework\Url
             $scopeResolver,
             $session,
             $sidResolver,
-            $routeParamsResolver,
+            $routeParamsResolverFactory,
             $queryParamsResolver,
             $scopeConfig,
             $scopeType,
diff --git a/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php b/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php
index e6de82bcf54ee3798e263bafb0d3a5f77c5e1435..e4ee8ff76365954f2ec2b404f72515d4c2cc3249 100644
--- a/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php
+++ b/app/code/Magento/DesignEditor/Test/Unit/Model/Url/NavigationModeTest.php
@@ -90,7 +90,7 @@ class NavigationModeTest extends \PHPUnit_Framework_TestCase
             [
                 'helper' => $this->_designHelperMock,
                 'data' => $this->_testData,
-                'routeParamsResolver' => $this->_routeParamsMock,
+                'routeParamsResolverFactory' => $this->_routeParamsMock,
                 'scopeResolver' => $this->_scopeResolverMock
             ]
         );
diff --git a/app/code/Magento/Developer/etc/adminhtml/system.xml b/app/code/Magento/Developer/etc/adminhtml/system.xml
index f858f6b51bc275787d5b476b42a1a3f62277ec11..13ec5c3a13106fc5820440bb0b7754e6404a5074 100644
--- a/app/code/Magento/Developer/etc/adminhtml/system.xml
+++ b/app/code/Magento/Developer/etc/adminhtml/system.xml
@@ -9,7 +9,7 @@
         <section id="dev" translate="label">
             <group id="front_end_development_workflow" translate="label" type="text" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1">
                 <label>Front-end development workflow</label>
-                <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
+                <field id="type" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
                     <label>Workflow type</label>
                     <comment>Not available in production mode</comment>
                     <source_model>Magento\Developer\Model\Config\Source\WorkflowType</source_model>
diff --git a/app/code/Magento/Directory/Model/Currency.php b/app/code/Magento/Directory/Model/Currency.php
index 9e7d2b9abef186b2f221c94c7ef61007534c81eb..31ab3bf266dd24b8fb1b657731f5b8bcf2c1bda4 100644
--- a/app/code/Magento/Directory/Model/Currency.php
+++ b/app/code/Magento/Directory/Model/Currency.php
@@ -311,6 +311,16 @@ class Currency extends \Magento\Framework\Model\AbstractModel
         return $this->_localeCurrency->getCurrency($this->getCode())->toCurrency($price, $options);
     }
 
+    /**
+     * Return currency symbol for current locale and currency code
+     *
+     * @return string
+     */
+    public function getCurrencySymbol()
+    {
+        return $this->_localeCurrency->getCurrency($this->getCode())->getSymbol();
+    }
+
     /**
      * @return string
      */
diff --git a/app/code/Magento/Directory/Model/PriceCurrency.php b/app/code/Magento/Directory/Model/PriceCurrency.php
index 3aa697742a41687cf66d0e78517afd3549aaac1c..9cf7dabd2de746d5193abe8bbf7b1c1f5662e550 100644
--- a/app/code/Magento/Directory/Model/PriceCurrency.php
+++ b/app/code/Magento/Directory/Model/PriceCurrency.php
@@ -117,6 +117,16 @@ class PriceCurrency implements \Magento\Framework\Pricing\PriceCurrencyInterface
         return $currentCurrency;
     }
 
+    /**
+     * @param null|string|bool|int|\Magento\Framework\App\ScopeInterface $scope
+     * @param \Magento\Framework\Model\AbstractModel|string|null $currency
+     * @return string
+     */
+    public function getCurrencySymbol($scope = null, $currency = null)
+    {
+        return $this->getCurrency($scope, $currency)->getCurrencySymbol();
+    }
+
     /**
      * Get store model
      *
diff --git a/app/code/Magento/Directory/Test/Unit/Model/CurrencyTest.php b/app/code/Magento/Directory/Test/Unit/Model/CurrencyTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..33a312880adc33b16f3c0f2327573b2f08d1ae32
--- /dev/null
+++ b/app/code/Magento/Directory/Test/Unit/Model/CurrencyTest.php
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Directory\Test\Unit\Model;
+
+use Magento\Directory\Model\Currency;
+
+class CurrencyTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var Currency
+     */
+    protected $currency;
+
+    protected $currencyCode = 'USD';
+
+    /**
+     * @var \Magento\Framework\Locale\CurrencyInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $localeCurrencyMock;
+
+    public function setUp()
+    {
+        $this->localeCurrencyMock = $this->getMock('\Magento\Framework\Locale\CurrencyInterface');
+
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->currency = $objectManager->getObject('Magento\Directory\Model\Currency', [
+            'localeCurrency' => $this->localeCurrencyMock,
+            'data' => [
+                'currency_code' => $this->currencyCode,
+            ]
+        ]);
+    }
+
+    public function testGetCurrencySymbol()
+    {
+        $currencySymbol = '$';
+
+        $currencyMock = $this->getMockBuilder('\Magento\Framework\Currency')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $currencyMock->expects($this->once())
+            ->method('getSymbol')
+            ->willReturn($currencySymbol);
+
+        $this->localeCurrencyMock->expects($this->once())
+            ->method('getCurrency')
+            ->with($this->currencyCode)
+            ->willReturn($currencyMock);
+        $this->assertEquals($currencySymbol, $this->currency->getCurrencySymbol());
+    }
+}
diff --git a/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php b/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php
index 205a63e7b0c33f3ffaa312cf9256ee2607f4aab8..b934435151a040f0f7156c532aecdb85f313e6e8 100644
--- a/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php
+++ b/app/code/Magento/Directory/Test/Unit/Model/PriceCurrencyTest.php
@@ -168,6 +168,18 @@ class PriceCurrencyTest extends \PHPUnit_Framework_TestCase
         ));
     }
 
+    public function testGetCurrencySymbol()
+    {
+        $storeId = 2;
+        $currencySymbol = '$';
+
+        $currencyMock = $this->getCurrentCurrencyMock();
+        $currencyMock->expects($this->once())
+            ->method('getCurrencySymbol')
+            ->willReturn($currencySymbol);
+        $this->assertEquals($currencySymbol, $this->priceCurrency->getCurrencySymbol($storeId, $currencyMock));
+    }
+
     protected function getCurrentCurrencyMock()
     {
         $currency = $this->getMockBuilder('Magento\Directory\Model\Currency')
diff --git a/app/code/Magento/Downloadable/Api/Data/LinkContentInterface.php b/app/code/Magento/Downloadable/Api/Data/LinkContentInterface.php
deleted file mode 100644
index 1972068f435512611b3d564f875ec666f6d20b56..0000000000000000000000000000000000000000
--- a/app/code/Magento/Downloadable/Api/Data/LinkContentInterface.php
+++ /dev/null
@@ -1,194 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Downloadable\Api\Data;
-
-/**
- * @codeCoverageIgnore
- */
-interface LinkContentInterface extends \Magento\Framework\Api\ExtensibleDataInterface
-{
-    /**
-     * Retrieve sample title
-     *
-     * @return string
-     */
-    public function getTitle();
-
-    /**
-     * Set sample title
-     *
-     * @param string $title
-     * @return $this
-     */
-    public function setTitle($title);
-
-    /**
-     * Retrieve sample sort order
-     *
-     * @return int
-     */
-    public function getSortOrder();
-
-    /**
-     * Set sample sort order
-     *
-     * @param int $sortOrder
-     * @return $this
-     */
-    public function setSortOrder($sortOrder);
-
-    /**
-     * Retrieve link price
-     *
-     * @return string
-     */
-    public function getPrice();
-
-    /**
-     * Set link price
-     *
-     * @param string $price
-     * @return $this
-     */
-    public function setPrice($price);
-
-    /**
-     * Retrieve number of allowed downloads of the link
-     *
-     * @return int
-     */
-    public function getNumberOfDownloads();
-
-    /**
-     * Set number of allowed downloads of the link
-     *
-     * @param int $numberOfDownloads
-     * @return $this
-     */
-    public function setNumberOfDownloads($numberOfDownloads);
-
-    /**
-     * Check if link is shareable
-     *
-     * @return bool
-     */
-    public function isShareable();
-
-    /**
-     * Set whether link is shareable
-     *
-     * @param bool $shareable
-     * @return $this
-     */
-    public function setShareable($shareable);
-
-    /**
-     * Retrieve link file content
-     *
-     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null
-     */
-    public function getLinkFile();
-
-    /**
-     * Set link file content
-     *
-     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFile
-     * @return $this
-     */
-    public function setLinkFile(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFile = null);
-
-    /**
-     * Retrieve link URL
-     *
-     * @return string|null
-     */
-    public function getLinkUrl();
-
-    /**
-     * Set link URL
-     *
-     * @param string $linkUrl
-     * @return $this
-     */
-    public function setLinkUrl($linkUrl);
-
-    /**
-     * Retrieve link type ('url' or 'file')
-     *
-     * @return string|null
-     */
-    public function getLinkType();
-
-    /**
-     * Set link type ('url' or 'file')
-     *
-     * @param string $linkType
-     * @return $this
-     */
-    public function setLinkType($linkType);
-
-    /**
-     * Retrieve sample file content
-     *
-     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null
-     */
-    public function getSampleFile();
-
-    /**
-     * Retrieve sample file content
-     *
-     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile
-     * @return $this
-     */
-    public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null);
-
-    /**
-     * Retrieve sample URL
-     *
-     * @return string|null
-     */
-    public function getSampleUrl();
-
-    /**
-     * Set sample URL
-     *
-     * @param string $sampleUrl
-     * @return $this
-     */
-    public function setSampleUrl($sampleUrl);
-
-    /**
-     * Retrieve sample type ('url' or 'file')
-     *
-     * @return string|null
-     */
-    public function getSampleType();
-
-    /**
-     * Set sample type ('url' or 'file')
-     *
-     * @param string $sampleType
-     * @return $this
-     */
-    public function setSampleType($sampleType);
-
-    /**
-     * Retrieve existing extension attributes object or create a new one.
-     *
-     * @return \Magento\Downloadable\Api\Data\LinkContentExtensionInterface|null
-     */
-    public function getExtensionAttributes();
-
-    /**
-     * Set an extension attributes object.
-     *
-     * @param \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes
-     * @return $this
-     */
-    public function setExtensionAttributes(
-        \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes
-    );
-}
diff --git a/app/code/Magento/Downloadable/Api/Data/LinkInterface.php b/app/code/Magento/Downloadable/Api/Data/LinkInterface.php
index 0152a1a256540db9b0cab423e2804bd906475245..6fe62e070349e96c265a7d6d5e32da64de0eb976 100644
--- a/app/code/Magento/Downloadable/Api/Data/LinkInterface.php
+++ b/app/code/Magento/Downloadable/Api/Data/LinkInterface.php
@@ -119,9 +119,24 @@ interface LinkInterface extends \Magento\Framework\Api\ExtensibleDataInterface
     public function setLinkFile($linkFile);
 
     /**
-     * Return URL or NULL when type is 'file'
+     * Return file content
      *
-     * @return string|null file URL
+     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null
+     */
+    public function getLinkFileContent();
+
+    /**
+     * Set file content
+     *
+     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent
+     * @return $this
+     */
+    public function setLinkFileContent(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent = null);
+
+    /**
+     * Return link url or null when type is 'file'
+     *
+     * @return string|null
      */
     public function getLinkUrl();
 
@@ -159,6 +174,23 @@ interface LinkInterface extends \Magento\Framework\Api\ExtensibleDataInterface
      */
     public function setSampleFile($sampleFile);
 
+    /**
+     * Return sample file content when type is 'file'
+     *
+     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null relative file path
+     */
+    public function getSampleFileContent();
+
+    /**
+     * Set sample file content
+     *
+     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent
+     * @return $this
+     */
+    public function setSampleFileContent(
+        \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent = null
+    );
+
     /**
      * Return URL or NULL when type is 'file'
      *
diff --git a/app/code/Magento/Downloadable/Api/Data/SampleContentInterface.php b/app/code/Magento/Downloadable/Api/Data/SampleContentInterface.php
deleted file mode 100644
index b4b0580bda1879b6aa75ab70371f6c522b7d1100..0000000000000000000000000000000000000000
--- a/app/code/Magento/Downloadable/Api/Data/SampleContentInterface.php
+++ /dev/null
@@ -1,104 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Downloadable\Api\Data;
-
-/**
- * @codeCoverageIgnore
- */
-interface SampleContentInterface extends \Magento\Framework\Api\ExtensibleDataInterface
-{
-    /**
-     * Retrieve sample title
-     *
-     * @return string
-     */
-    public function getTitle();
-
-    /**
-     * Set sample title
-     *
-     * @param string $title
-     * @return $this
-     */
-    public function setTitle($title);
-
-    /**
-     * Retrieve sample type ('url' or 'file')
-     *
-     * @return string|null
-     */
-    public function getSampleType();
-
-    /**
-     * Set sample type ('url' or 'file')
-     *
-     * @param string $sampleType
-     * @return $this
-     */
-    public function setSampleType($sampleType);
-
-    /**
-     * Retrieve sample file content
-     *
-     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null
-     */
-    public function getSampleFile();
-
-    /**
-     * Set sample file content
-     *
-     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile
-     * @return $this
-     */
-    public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null);
-
-    /**
-     * Retrieve sample sort order
-     *
-     * @return int
-     */
-    public function getSortOrder();
-
-    /**
-     * Set sample sort order
-     *
-     * @param int $sortOrder
-     * @return $this
-     */
-    public function setSortOrder($sortOrder);
-
-    /**
-     * Retrieve sample URL
-     *
-     * @return string|null
-     */
-    public function getSampleUrl();
-
-    /**
-     * Set sample URL
-     *
-     * @param string $sampleUrl
-     * @return $this
-     */
-    public function setSampleUrl($sampleUrl);
-
-    /**
-     * Retrieve existing extension attributes object or create a new one.
-     *
-     * @return \Magento\Downloadable\Api\Data\SampleContentExtensionInterface|null
-     */
-    public function getExtensionAttributes();
-
-    /**
-     * Set an extension attributes object.
-     *
-     * @param \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes
-     * @return $this
-     */
-    public function setExtensionAttributes(
-        \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes
-    );
-}
diff --git a/app/code/Magento/Downloadable/Api/Data/SampleInterface.php b/app/code/Magento/Downloadable/Api/Data/SampleInterface.php
index 3d6dfc937f3871a4a489e43c1f1912c5a7498082..8e30bf6f0e1ea0f3fc8bcf78b4261696e6119106 100644
--- a/app/code/Magento/Downloadable/Api/Data/SampleInterface.php
+++ b/app/code/Magento/Downloadable/Api/Data/SampleInterface.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Downloadable\Api\Data;
 
+use \Magento\Downloadable\Api\Data\File\ContentInterface;
+
 /**
  * @codeCoverageIgnore
  */
@@ -82,6 +84,21 @@ interface SampleInterface extends \Magento\Framework\Api\ExtensibleDataInterface
      */
     public function setSampleFile($sampleFile);
 
+    /**
+     * Retrieve sample file content
+     *
+     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null
+     */
+    public function getSampleFileContent();
+
+    /**
+     * Set sample file content
+     *
+     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent
+     * @return $this
+     */
+    public function setSampleFileContent(ContentInterface $sampleFileContent = null);
+
     /**
      * Return URL or NULL when type is 'file'
      *
diff --git a/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php b/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php
index a8bd6f83d9be190834e0e92d34c039010a4d870c..3a0d8053e4f8d599af652e83d830ad6c7ead7b65 100644
--- a/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php
+++ b/app/code/Magento/Downloadable/Api/LinkRepositoryInterface.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Downloadable\Api;
 
-use Magento\Downloadable\Api\Data\LinkContentInterface;
+use Magento\Downloadable\Api\Data\LinkInterface;
 
 interface LinkRepositoryInterface
 {
@@ -17,6 +17,14 @@ interface LinkRepositoryInterface
      */
     public function getSamples($sku);
 
+    /**
+     * List of samples for downloadable product
+     *
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @return \Magento\Downloadable\Api\Data\SampleInterface[]
+     */
+    public function getSamplesByProduct(\Magento\Catalog\Api\Data\ProductInterface $product);
+
     /**
      * List of links with associated samples
      *
@@ -25,22 +33,29 @@ interface LinkRepositoryInterface
      */
     public function getLinks($sku);
 
+    /**
+     * List of links with associated samples
+     *
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @return \Magento\Downloadable\Api\Data\LinkInterface[]
+     */
+    public function getLinksByProduct(\Magento\Catalog\Api\Data\ProductInterface $product);
+
     /**
      * Update downloadable link of the given product (link type and its resources cannot be changed)
      *
      * @param string $sku
-     * @param \Magento\Downloadable\Api\Data\LinkContentInterface $linkContent
-     * @param int $linkId
+     * @param \Magento\Downloadable\Api\Data\LinkInterface $link
      * @param bool $isGlobalScopeContent
      * @return int
      */
-    public function save($sku, LinkContentInterface $linkContent, $linkId = null, $isGlobalScopeContent = false);
+    public function save($sku, LinkInterface $link, $isGlobalScopeContent = false);
 
     /**
      * Delete downloadable link
      *
-     * @param int $linkId
+     * @param int $id
      * @return bool
      */
-    public function delete($linkId);
+    public function delete($id);
 }
diff --git a/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php b/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php
index cd72680b21bae5b75bd3b245d5a867ebd8e7f168..9a52cea8819f4e413e8c5a1964e5732e5936ed6f 100644
--- a/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php
+++ b/app/code/Magento/Downloadable/Api/SampleRepositoryInterface.php
@@ -5,31 +5,29 @@
  */
 namespace Magento\Downloadable\Api;
 
-use Magento\Downloadable\Api\Data\SampleContentInterface;
+use Magento\Downloadable\Api\Data\SampleInterface;
 
 interface SampleRepositoryInterface
 {
     /**
-     * Update downloadable sample of the given product (sample type and its resource cannot be changed)
+     * Update downloadable sample of the given product
      *
-     * @param string $productSku
-     * @param \Magento\Downloadable\Api\Data\SampleContentInterface $sampleContent
-     * @param int $sampleId
+     * @param string $sku
+     * @param \Magento\Downloadable\Api\Data\SampleInterface $sample
      * @param bool $isGlobalScopeContent
      * @return int
      */
     public function save(
-        $productSku,
-        SampleContentInterface $sampleContent,
-        $sampleId = null,
+        $sku,
+        SampleInterface $sample,
         $isGlobalScopeContent = false
     );
 
     /**
      * Delete downloadable sample
      *
-     * @param int $sampleId
+     * @param int $id
      * @return bool
      */
-    public function delete($sampleId);
+    public function delete($id);
 }
diff --git a/app/code/Magento/Downloadable/Model/Link.php b/app/code/Magento/Downloadable/Model/Link.php
index 84de54915ab0edec47b492ac19a2fde4f5d131b4..66703a9dccb811ce294d5c4b39b053db7ec07cc2 100644
--- a/app/code/Magento/Downloadable/Model/Link.php
+++ b/app/code/Magento/Downloadable/Model/Link.php
@@ -43,9 +43,11 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C
     const KEY_NUMBER_OF_DOWNLOADS = 'number_of_downloads';
     const KEY_LINK_TYPE = 'link_type';
     const KEY_LINK_FILE = 'link_file';
+    const KEY_LINK_FILE_CONTENT = 'link_file_content';
     const KEY_LINK_URL = 'link_url';
     const KEY_SAMPLE_TYPE = 'sample_type';
     const KEY_SAMPLE_FILE = 'sample_file';
+    const KEY_SAMPLE_FILE_CONTENT = 'sample_file_content';
     const KEY_SAMPLE_URL = 'sample_url';
     /**#@-*/
 
@@ -213,6 +215,16 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C
         return $this->getData(self::KEY_LINK_FILE);
     }
 
+    /**
+     * Return file content
+     *
+     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null
+     */
+    public function getLinkFileContent()
+    {
+        return $this->getData(self::KEY_LINK_FILE_CONTENT);
+    }
+
     /**
      * {@inheritdoc}
      * @codeCoverageIgnore
@@ -240,6 +252,16 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C
         return $this->getData(self::KEY_SAMPLE_FILE);
     }
 
+    /**
+     * Return sample file content when type is 'file'
+     *
+     * @return \Magento\Downloadable\Api\Data\File\ContentInterface|null relative file path
+     */
+    public function getSampleFileContent()
+    {
+        return $this->getData(self::KEY_SAMPLE_FILE_CONTENT);
+    }
+
     /**
      * {@inheritdoc}
      * @codeCoverageIgnore
@@ -320,6 +342,17 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C
         return $this->setData(self::KEY_LINK_FILE, $linkFile);
     }
 
+    /**
+     * Set file content
+     *
+     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent
+     * @return $this
+     */
+    public function setLinkFileContent(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFileContent = null)
+    {
+        return $this->setData(self::KEY_LINK_FILE_CONTENT, $linkFileContent);
+    }
+
     /**
      * Set URL
      *
@@ -351,6 +384,19 @@ class Link extends \Magento\Framework\Model\AbstractExtensibleModel implements C
         return $this->setData(self::KEY_SAMPLE_FILE, $sampleFile);
     }
 
+    /**
+     * Set sample file content
+     *
+     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent
+     * @return $this
+     */
+    public function setSampleFileContent(
+        \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent = null
+    ) {
+        return $this->setData(self::KEY_SAMPLE_FILE_CONTENT, $sampleFileContent);
+    }
+
+
     /**
      * Set URL
      *
diff --git a/app/code/Magento/Downloadable/Model/Link/Content.php b/app/code/Magento/Downloadable/Model/Link/Content.php
deleted file mode 100644
index a40f76faf1cff5c353a8406943c9b51b4f95e428..0000000000000000000000000000000000000000
--- a/app/code/Magento/Downloadable/Model/Link/Content.php
+++ /dev/null
@@ -1,268 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Downloadable\Model\Link;
-
-use Magento\Downloadable\Api\Data\LinkContentInterface;
-
-/**
- * @codeCoverageIgnore
- */
-class Content extends \Magento\Framework\Model\AbstractExtensibleModel implements LinkContentInterface
-{
-    const TITLE = 'title';
-    const PRICE = 'price';
-    const NUMBER_OF_DOWNLOADS = 'number_of_downloads';
-    const SHAREABLE = 'shareable';
-    const SORT_ORDER = 'sort_order';
-    const LINK_FILE = 'link_file';
-    const LINK_URL = 'link_url';
-    const LINK_TYPE = 'link_type';
-    const SAMPLE_FILE = 'sample_file';
-    const SAMPLE_URL = 'sample_url';
-    const SAMPLE_TYPE = 'sample_type';
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getTitle()
-    {
-        return $this->getData(self::TITLE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSortOrder()
-    {
-        return $this->getData(self::SORT_ORDER);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getPrice()
-    {
-        return $this->getData(self::PRICE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getNumberOfDownloads()
-    {
-        return $this->getData(self::NUMBER_OF_DOWNLOADS);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function isShareable()
-    {
-        return $this->getData(self::SHAREABLE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getLinkFile()
-    {
-        return $this->getData(self::LINK_FILE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getLinkUrl()
-    {
-        return $this->getData(self::LINK_URL);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getLinkType()
-    {
-        return $this->getData(self::LINK_TYPE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSampleFile()
-    {
-        return $this->getData(self::SAMPLE_FILE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSampleUrl()
-    {
-        return $this->getData(self::SAMPLE_URL);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSampleType()
-    {
-        return $this->getData(self::SAMPLE_TYPE);
-    }
-
-    /**
-     * Set sample title
-     *
-     * @param string $title
-     * @return $this
-     */
-    public function setTitle($title)
-    {
-        return $this->setData(self::TITLE, $title);
-    }
-
-    /**
-     * Set sample sort order
-     *
-     * @param int $sortOrder
-     * @return $this
-     */
-    public function setSortOrder($sortOrder)
-    {
-        return $this->setData(self::SORT_ORDER, $sortOrder);
-    }
-
-    /**
-     * Set link price
-     *
-     * @param string $price
-     * @return $this
-     */
-    public function setPrice($price)
-    {
-        return $this->setData(self::PRICE, $price);
-    }
-
-    /**
-     * Set number of allowed downloads of the link
-     *
-     * @param int $numberOfDownloads
-     * @return $this
-     */
-    public function setNumberOfDownloads($numberOfDownloads)
-    {
-        return $this->setData(self::NUMBER_OF_DOWNLOADS, $numberOfDownloads);
-    }
-
-    /**
-     * Set whether link is shareable
-     *
-     * @param bool $shareable
-     * @return $this
-     */
-    public function setShareable($shareable)
-    {
-        return $this->setData(self::SHAREABLE, $shareable);
-    }
-
-    /**
-     * Set link file content
-     *
-     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $linkFile
-     * @return $this
-     */
-    public function setLinkFile(\Magento\Downloadable\Api\Data\File\ContentInterface $linkFile = null)
-    {
-        return $this->setData(self::LINK_FILE, $linkFile);
-    }
-
-    /**
-     * Set link URL
-     *
-     * @param string $linkUrl
-     * @return $this
-     */
-    public function setLinkUrl($linkUrl)
-    {
-        return $this->setData(self::LINK_URL, $linkUrl);
-    }
-
-    /**
-     * Set link type ('url' or 'file')
-     *
-     * @param string $linkType
-     * @return $this
-     */
-    public function setLinkType($linkType)
-    {
-        return $this->setData(self::LINK_TYPE, $linkType);
-    }
-
-    /**
-     * Retrieve sample file content
-     *
-     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile
-     * @return $this
-     */
-    public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null)
-    {
-        return $this->setData(self::SAMPLE_FILE, $sampleFile);
-    }
-
-    /**
-     * Set sample URL
-     *
-     * @param string $sampleUrl
-     * @return $this
-     */
-    public function setSampleUrl($sampleUrl)
-    {
-        return $this->setData(self::SAMPLE_URL, $sampleUrl);
-    }
-
-    /**
-     * Set sample type ('url' or 'file')
-     *
-     * @param string $sampleType
-     * @return $this
-     */
-    public function setSampleType($sampleType)
-    {
-        return $this->setData(self::SAMPLE_TYPE, $sampleType);
-    }
-
-    /**
-     * {@inheritdoc}
-     *
-     * @return \Magento\Downloadable\Api\Data\LinkContentExtensionInterface|null
-     */
-    public function getExtensionAttributes()
-    {
-        return $this->_getExtensionAttributes();
-    }
-
-    /**
-     * {@inheritdoc}
-     *
-     * @param \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes
-     * @return $this
-     */
-    public function setExtensionAttributes(
-        \Magento\Downloadable\Api\Data\LinkContentExtensionInterface $extensionAttributes
-    ) {
-        return $this->_setExtensionAttributes($extensionAttributes);
-    }
-}
diff --git a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php
index 708302b7871c21c7bd1921b7a522019cbcd25aef..f1bf1d2dcbe85f819a0af7091d8806bee9cdcc12 100644
--- a/app/code/Magento/Downloadable/Model/Link/ContentValidator.php
+++ b/app/code/Magento/Downloadable/Model/Link/ContentValidator.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Downloadable\Model\Link;
 
-use Magento\Downloadable\Api\Data\LinkContentInterface;
+use Magento\Downloadable\Api\Data\LinkInterface;
 use Magento\Downloadable\Model\File\ContentValidator as FileContentValidator;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Url\Validator as UrlValidator;
@@ -37,43 +37,50 @@ class ContentValidator
     /**
      * Check if link content is valid
      *
-     * @param LinkContentInterface $linkContent
+     * @param LinkInterface $link
+     * @param bool $validateLinkContent
+     * @param bool $validateSampleContent
      * @return bool
      * @throws InputException
      */
-    public function isValid(LinkContentInterface $linkContent)
+    public function isValid(LinkInterface $link, $validateLinkContent = true, $validateSampleContent = true)
     {
-        if (!is_numeric($linkContent->getPrice()) || $linkContent->getPrice() < 0) {
+        if (!is_numeric($link->getPrice()) || $link->getPrice() < 0) {
             throw new InputException(__('Link price must have numeric positive value.'));
         }
-        if (!is_int($linkContent->getNumberOfDownloads()) || $linkContent->getNumberOfDownloads() < 0) {
+        if (!is_int($link->getNumberOfDownloads()) || $link->getNumberOfDownloads() < 0) {
             throw new InputException(__('Number of downloads must be a positive integer.'));
         }
-        if (!is_int($linkContent->getSortOrder()) || $linkContent->getSortOrder() < 0) {
+        if (!is_int($link->getSortOrder()) || $link->getSortOrder() < 0) {
             throw new InputException(__('Sort order must be a positive integer.'));
         }
 
-        $this->validateLinkResource($linkContent);
-        $this->validateSampleResource($linkContent);
+        if ($validateLinkContent) {
+            $this->validateLinkResource($link);
+        }
+        if ($validateSampleContent) {
+            $this->validateSampleResource($link);
+        }
         return true;
     }
 
     /**
      * Validate link resource (file or URL)
      *
-     * @param LinkContentInterface $linkContent
+     * @param LinkInterface $link
      * @throws InputException
      * @return void
      */
-    protected function validateLinkResource(LinkContentInterface $linkContent)
+    protected function validateLinkResource(LinkInterface $link)
     {
-        if ($linkContent->getLinkType() == 'url'
-            && !$this->urlValidator->isValid($linkContent->getLinkUrl())
+        if ($link->getLinkType() == 'url'
+            && !$this->urlValidator->isValid($link->getLinkUrl())
         ) {
             throw new InputException(__('Link URL must have valid format.'));
         }
-        if ($linkContent->getLinkType() == 'file'
-            && (!$linkContent->getLinkFile() || !$this->fileContentValidator->isValid($linkContent->getLinkFile()))
+        if ($link->getLinkType() == 'file'
+            && (!$link->getLinkFileContent()
+                || !$this->fileContentValidator->isValid($link->getLinkFileContent()))
         ) {
             throw new InputException(__('Provided file content must be valid base64 encoded data.'));
         }
@@ -82,19 +89,20 @@ class ContentValidator
     /**
      * Validate sample resource (file or URL)
      *
-     * @param LinkContentInterface $linkContent
+     * @param LinkInterface $link
      * @throws InputException
      * @return void
      */
-    protected function validateSampleResource(LinkContentInterface $linkContent)
+    protected function validateSampleResource(LinkInterface $link)
     {
-        if ($linkContent->getSampleType() == 'url'
-            && !$this->urlValidator->isValid($linkContent->getSampleUrl())
+        if ($link->getSampleType() == 'url'
+            && !$this->urlValidator->isValid($link->getSampleUrl())
         ) {
             throw new InputException(__('Sample URL must have valid format.'));
         }
-        if ($linkContent->getSampleType() == 'file'
-            && (!$linkContent->getSampleFile() || !$this->fileContentValidator->isValid($linkContent->getSampleFile()))
+        if ($link->getSampleType() == 'file'
+            && (!$link->getSampleFileContent()
+                || !$this->fileContentValidator->isValid($link->getSampleFileContent()))
         ) {
             throw new InputException(__('Provided file content must be valid base64 encoded data.'));
         }
diff --git a/app/code/Magento/Downloadable/Model/LinkRepository.php b/app/code/Magento/Downloadable/Model/LinkRepository.php
index 426e8bf63f21ed644dae4394470c8bf71d6482b4..bca06682ba718dee04cdd59e0edbfc275cc18efb 100644
--- a/app/code/Magento/Downloadable/Model/LinkRepository.php
+++ b/app/code/Magento/Downloadable/Model/LinkRepository.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Downloadable\Model;
 
-use Magento\Downloadable\Api\Data\LinkContentInterface;
+use Magento\Downloadable\Api\Data\LinkInterface;
 use Magento\Downloadable\Api\Data\File\ContentUploaderInterface;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Exception\NoSuchEntityException;
@@ -92,9 +92,18 @@ class LinkRepository implements \Magento\Downloadable\Api\LinkRepositoryInterfac
      */
     public function getLinks($sku)
     {
-        $linkList = [];
         /** @var \Magento\Catalog\Model\Product $product */
         $product = $this->productRepository->get($sku);
+        return $this->getLinksByProduct($product);
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @return array
+     */
+    public function getLinksByProduct(\Magento\Catalog\Api\Data\ProductInterface $product)
+    {
+        $linkList = [];
         $links = $this->downloadableType->getLinks($product);
         /** @var \Magento\Downloadable\Model\Link $link */
         foreach ($links as $link) {
@@ -152,9 +161,17 @@ class LinkRepository implements \Magento\Downloadable\Api\LinkRepositoryInterfac
      */
     public function getSamples($sku)
     {
-        $sampleList = [];
-        /** @var \Magento\Catalog\Model\Product $product */
         $product = $this->productRepository->get($sku);
+        return $this->getSamplesByProduct($product);
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @return array
+     */
+    public function getSamplesByProduct(\Magento\Catalog\Api\Data\ProductInterface $product)
+    {
+        $sampleList = [];
         $samples = $this->downloadableType->getSamples($product);
         /** @var \Magento\Downloadable\Model\Sample $sample */
         foreach ($samples as $sample) {
@@ -181,113 +198,149 @@ class LinkRepository implements \Magento\Downloadable\Api\LinkRepositoryInterfac
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      * @SuppressWarnings(PHPMD.NPathComplexity)
      */
-    public function save($sku, LinkContentInterface $linkContent, $linkId = null, $isGlobalScopeContent = false)
+    public function save($sku, LinkInterface $link, $isGlobalScopeContent = false)
     {
         $product = $this->productRepository->get($sku, true);
-        if ($linkId) {
-
-            /** @var $link \Magento\Downloadable\Model\Link */
-            $link = $this->linkFactory->create()->load($linkId);
-            if (!$link->getId()) {
-                throw new NoSuchEntityException(__('There is no downloadable link with provided ID.'));
-            }
-            if ($link->getProductId() != $product->getId()) {
-                throw new InputException(__('Provided downloadable link is not related to given product.'));
-            }
-            if (!$this->contentValidator->isValid($linkContent)) {
-                throw new InputException(__('Provided link information is invalid.'));
-            }
-            if ($isGlobalScopeContent) {
-                $product->setStoreId(0);
-            }
-            $title = $linkContent->getTitle();
-            if (empty($title)) {
-                if ($isGlobalScopeContent) {
-                    throw new InputException(__('Link title cannot be empty.'));
-                }
-                // use title from GLOBAL scope
-                $link->setTitle(null);
-            } else {
-                $link->setTitle($linkContent->getTitle());
-            }
-
-            $link->setProductId($product->getId())
-                ->setStoreId($product->getStoreId())
-                ->setWebsiteId($product->getStore()->getWebsiteId())
-                ->setProductWebsiteIds($product->getWebsiteIds())
-                ->setSortOrder($linkContent->getSortOrder())
-                ->setPrice($linkContent->getPrice())
-                ->setIsShareable($linkContent->isShareable())
-                ->setNumberOfDownloads($linkContent->getNumberOfDownloads())
-                ->save();
-            return $link->getId();
+        if ($link->getId() !== null) {
+            return $this->updateLink($product, $link, $isGlobalScopeContent);
         } else {
-            $product = $this->productRepository->get($sku, true);
             if ($product->getTypeId() !== \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) {
                 throw new InputException(__('Product type of the product must be \'downloadable\'.'));
             }
-            if (!$this->contentValidator->isValid($linkContent)) {
+            if (!$this->contentValidator->isValid($link)) {
                 throw new InputException(__('Provided link information is invalid.'));
             }
 
-            if (!in_array($linkContent->getLinkType(), ['url', 'file'])) {
+            if (!in_array($link->getLinkType(), ['url', 'file'])) {
                 throw new InputException(__('Invalid link type.'));
             }
-            $title = $linkContent->getTitle();
+            $title = $link->getTitle();
             if (empty($title)) {
                 throw new InputException(__('Link title cannot be empty.'));
             }
+            return $this->saveLink($product, $link, $isGlobalScopeContent);
+        }
+    }
 
-            $linkData = [
-                'link_id' => 0,
-                'is_delete' => 0,
-                'type' => $linkContent->getLinkType(),
-                'sort_order' => $linkContent->getSortOrder(),
-                'title' => $linkContent->getTitle(),
-                'price' => $linkContent->getPrice(),
-                'number_of_downloads' => $linkContent->getNumberOfDownloads(),
-                'is_shareable' => $linkContent->isShareable(),
-            ];
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param LinkInterface $link
+     * @param bool $isGlobalScopeContent
+     * @return int
+     */
+    protected function saveLink(
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        LinkInterface $link,
+        $isGlobalScopeContent
+    ) {
+        $linkData = [
+            'link_id' => $link->getid() === null ? 0 : $link->getid(),
+            'is_delete' => 0,
+            'type' => $link->getLinkType(),
+            'sort_order' => $link->getSortOrder(),
+            'title' => $link->getTitle(),
+            'price' => $link->getPrice(),
+            'number_of_downloads' => $link->getNumberOfDownloads(),
+            'is_shareable' => $link->getIsShareable(),
+        ];
 
-            if ($linkContent->getLinkType() == 'file') {
-                $linkData['file'] = $this->jsonEncoder->encode(
+        if ($link->getLinkType() == 'file' && $link->getLinkFile() === null) {
+            $linkData['file'] = $this->jsonEncoder->encode(
+                [
+                    $this->fileContentUploader->upload($link->getLinkFileContent(), 'link_file'),
+                ]
+            );
+        } elseif ($link->getLinkType() === 'url') {
+            $linkData['link_url'] = $link->getLinkUrl();
+        } else {
+            //existing link file
+            $linkData['file'] = $this->jsonEncoder->encode(
+                [
                     [
-                        $this->fileContentUploader->upload($linkContent->getLinkFile(), 'link_file'),
+                        'file' => $link->getLinkFile(),
+                        'status' => 'old',
                     ]
-                );
-            } else {
-                $linkData['link_url'] = $linkContent->getLinkUrl();
-            }
+                ]
+            );
+        }
 
-            if ($linkContent->getSampleType() == 'file') {
-                $linkData['sample']['type'] = 'file';
-                $linkData['sample']['file'] = $this->jsonEncoder->encode(
-                    [
-                        $this->fileContentUploader->upload($linkContent->getSampleFile(), 'link_sample_file'),
-                    ]
-                );
-            } elseif ($linkContent->getSampleType() == 'url') {
-                $linkData['sample']['type'] = 'url';
-                $linkData['sample']['url'] = $linkContent->getSampleUrl();
-            }
+        if ($link->getSampleType() == 'file' && $link->getSampleFile() === null) {
+            $linkData['sample']['type'] = 'file';
+            $linkData['sample']['file'] = $this->jsonEncoder->encode(
+                [
+                    $this->fileContentUploader->upload($link->getSampleFileContent(), 'link_sample_file'),
+                ]
+            );
+        } elseif ($link->getSampleType() == 'url') {
+            $linkData['sample']['type'] = 'url';
+            $linkData['sample']['url'] = $link->getSampleUrl();
+        }
 
-            $downloadableData = ['link' => [$linkData]];
-            $product->setDownloadableData($downloadableData);
+        $downloadableData = ['link' => [$linkData]];
+        $product->setDownloadableData($downloadableData);
+        if ($isGlobalScopeContent) {
+            $product->setStoreId(0);
+        }
+        $this->downloadableType->save($product);
+        return $product->getLastAddedLinkId();
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param LinkInterface $link
+     * @param bool $isGlobalScopeContent
+     * @return mixed
+     * @throws InputException
+     * @throws NoSuchEntityException
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    protected function updateLink(
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        LinkInterface $link,
+        $isGlobalScopeContent
+    ) {
+        /** @var $existingLink \Magento\Downloadable\Model\Link */
+        $existingLink = $this->linkFactory->create()->load($link->getId());
+        if (!$existingLink->getId()) {
+            throw new NoSuchEntityException(__('There is no downloadable link with provided ID.'));
+        }
+        if ($existingLink->getProductId() != $product->getId()) {
+            throw new InputException(__('Provided downloadable link is not related to given product.'));
+        }
+        $validateLinkContent = $link->getLinkFileContent() === null ? false : true;
+        $validateSampleContent = $link->getSampleFileContent() === null ? false : true;
+        if (!$this->contentValidator->isValid($link, $validateLinkContent, $validateSampleContent)) {
+            throw new InputException(__('Provided link information is invalid.'));
+        }
+        if ($isGlobalScopeContent) {
+            $product->setStoreId(0);
+        }
+        $title = $link->getTitle();
+        if (empty($title)) {
             if ($isGlobalScopeContent) {
-                $product->setStoreId(0);
+                throw new InputException(__('Link title cannot be empty.'));
             }
-            $product->save();
-            return $product->getLastAddedLinkId();
         }
+
+        if ($link->getLinkType() == 'file' && $link->getLinkFileContent() === null) {
+            $link->setLinkFile($existingLink->getLinkFile());
+        }
+        if ($link->getSampleType() == 'file' && $link->getSampleFileContent() === null) {
+            $link->setSampleFile($existingLink->getSampleFile());
+        }
+
+        $this->saveLink($product, $link, $isGlobalScopeContent);
+        return $existingLink->getId();
     }
 
     /**
      * {@inheritdoc}
      */
-    public function delete($linkId)
+    public function delete($id)
     {
         /** @var $link \Magento\Downloadable\Model\Link */
-        $link = $this->linkFactory->create()->load($linkId);
+        $link = $this->linkFactory->create()->load($id);
         if (!$link->getId()) {
             throw new NoSuchEntityException(__('There is no downloadable link with provided ID.'));
         }
diff --git a/app/code/Magento/Downloadable/Model/Observer.php b/app/code/Magento/Downloadable/Model/Observer.php
index 0fd516bfa9fafc120ee467f52725eb0e974de334..68dc8f018d7f00aef5a9556f6622d518bb924142 100644
--- a/app/code/Magento/Downloadable/Model/Observer.php
+++ b/app/code/Magento/Downloadable/Model/Observer.php
@@ -114,10 +114,10 @@ class Observer
             //order not saved in the database
             return $this;
         }
-        $product = $orderItem->getProduct();
-        if ($product && $product->getTypeId() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) {
+        if ($orderItem->getProductType() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) {
             return $this;
         }
+        $product = $orderItem->getProduct();
         $purchasedLink = $this->_createPurchasedModel()->load($orderItem->getId(), 'order_item_id');
         if ($purchasedLink->getId()) {
             return $this;
diff --git a/app/code/Magento/Downloadable/Model/Plugin/AfterProductLoad.php b/app/code/Magento/Downloadable/Model/Plugin/AfterProductLoad.php
new file mode 100644
index 0000000000000000000000000000000000000000..83466a0fa080f34d7aa3d38fbcfd0fd8905d1101
--- /dev/null
+++ b/app/code/Magento/Downloadable/Model/Plugin/AfterProductLoad.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Downloadable\Model\Plugin;
+
+class AfterProductLoad
+{
+    /**
+     * @var \Magento\Downloadable\Api\LinkRepositoryInterface
+     */
+    protected $linkRepository;
+
+    /**
+     * @var \Magento\Catalog\Api\Data\ProductExtensionFactory
+     */
+    protected $productExtensionFactory;
+
+    /**
+     * @param \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository
+     * @param \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
+     */
+    public function __construct(
+        \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository,
+        \Magento\Catalog\Api\Data\ProductExtensionFactory $productExtensionFactory
+    ) {
+        $this->linkRepository = $linkRepository;
+        $this->productExtensionFactory = $productExtensionFactory;
+    }
+
+    /**
+     * @param \Magento\Catalog\Model\Product $product
+     * @return \Magento\Catalog\Model\Product
+     */
+    public function afterLoad(
+        \Magento\Catalog\Model\Product $product
+    ) {
+        if ($product->getTypeId() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) {
+            return $product;
+        }
+
+        $productExtension = $product->getExtensionAttributes();
+        if ($productExtension === null) {
+            $productExtension = $this->productExtensionFactory->create();
+        }
+        $links = $this->linkRepository->getLinksByProduct($product);
+        if ($links !== null) {
+            $productExtension->setDownloadableProductLinks($links);
+        }
+        $samples = $this->linkRepository->getSamplesByProduct($product);
+        if ($samples !== null) {
+            $productExtension->setDownloadableProductSamples($samples);
+        }
+
+        $product->setExtensionAttributes($productExtension);
+
+        return $product;
+    }
+}
diff --git a/app/code/Magento/Downloadable/Model/Plugin/AroundProductRepositorySave.php b/app/code/Magento/Downloadable/Model/Plugin/AroundProductRepositorySave.php
new file mode 100644
index 0000000000000000000000000000000000000000..13dd932c3a7683109c4fcef022556abb0dbfa519
--- /dev/null
+++ b/app/code/Magento/Downloadable/Model/Plugin/AroundProductRepositorySave.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Downloadable\Model\Plugin;
+
+class AroundProductRepositorySave
+{
+    /**
+     * @var \Magento\Downloadable\Api\LinkRepositoryInterface
+     */
+    protected $linkRepository;
+
+    /**
+     * @var \Magento\Downloadable\Api\SampleRepositoryInterface
+     */
+    protected $sampleRepository;
+
+    /**
+     * @param \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository
+     * @param \Magento\Downloadable\Api\SampleRepositoryInterface $sampleRepository
+     */
+    public function __construct(
+        \Magento\Downloadable\Api\LinkRepositoryInterface $linkRepository,
+        \Magento\Downloadable\Api\SampleRepositoryInterface $sampleRepository
+    ) {
+        $this->linkRepository = $linkRepository;
+        $this->sampleRepository = $sampleRepository;
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\ProductRepositoryInterface $subject
+     * @param callable $proceed
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param bool $saveOptions
+     * @return \Magento\Catalog\Api\Data\ProductInterface
+     * @throws \Magento\Framework\Exception\CouldNotSaveException
+     */
+    public function aroundSave(
+        \Magento\Catalog\Api\ProductRepositoryInterface $subject,
+        \Closure $proceed,
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        $saveOptions = false
+    ) {
+        /** @var \Magento\Catalog\Api\Data\ProductInterface $result */
+        $result = $proceed($product, $saveOptions);
+
+        if ($product->getTypeId() != \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) {
+            return $result;
+        }
+
+        /* @var \Magento\Catalog\Api\Data\ProductExtensionInterface $options */
+        $extendedAttributes = $product->getExtensionAttributes();
+        if ($extendedAttributes === null) {
+            return $result;
+        }
+        $links = $extendedAttributes->getDownloadableProductLinks();
+        $samples = $extendedAttributes->getDownloadableProductSamples();
+
+        if ($links === null && $samples === null) {
+            return $result;
+        }
+
+        if ($links !== null) {
+            $this->saveLinks($result, $links);
+        }
+        if ($samples !== null) {
+            $this->saveSamples($result, $samples);
+        }
+
+        return $subject->get($result->getSku(), false, $result->getStoreId(), true);
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param \Magento\Downloadable\Api\Data\LinkInterface[] $links
+     * @return $this
+     */
+    protected function saveLinks(\Magento\Catalog\Api\Data\ProductInterface $product, array $links)
+    {
+        $existingLinkIds = [];
+        //get existing links from extension attribute
+        $extensionAttributes = $product->getExtensionAttributes();
+        if ($extensionAttributes !== null) {
+            $existingLinks = $extensionAttributes->getDownloadableProductLinks();
+            if ($existingLinks !== null) {
+                foreach ($existingLinks as $existingLink) {
+                    $existingLinkIds[] = $existingLink->getId();
+                }
+            }
+        }
+
+        $updatedLinkIds = [];
+        foreach ($links as $link) {
+            $linkId = $link->getId();
+            if ($linkId) {
+                $updatedLinkIds[] = $linkId;
+            }
+            $this->linkRepository->save($product->getSku(), $link);
+        }
+        $linkIdsToDelete = array_diff($existingLinkIds, $updatedLinkIds);
+
+        foreach ($linkIdsToDelete as $linkId) {
+            $this->linkRepository->delete($linkId);
+        }
+        return $this;
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param \Magento\Downloadable\Api\Data\SampleInterface[] $samples
+     * @return $this
+     */
+    protected function saveSamples(\Magento\Catalog\Api\Data\ProductInterface $product, array $samples)
+    {
+        $existingSampleIds = [];
+        $extensionAttributes = $product->getExtensionAttributes();
+        if ($extensionAttributes !== null) {
+            $existingSamples = $extensionAttributes->getDownloadableProductSamples();
+            if ($existingSamples !== null) {
+                foreach ($existingSamples as $existingSample) {
+                    $existingSampleIds[] = $existingSample->getId();
+                }
+            }
+        }
+
+        $updatedSampleIds = [];
+        foreach ($samples as $sample) {
+            $sampleId = $sample->getId();
+            if ($sampleId) {
+                $updatedSampleIds[] = $sampleId;
+            }
+            $this->sampleRepository->save($product->getSku(), $sample);
+        }
+        $sampleIdsToDelete = array_diff($existingSampleIds, $updatedSampleIds);
+
+        foreach ($sampleIdsToDelete as $sampleId) {
+            $this->sampleRepository->delete($sampleId);
+        }
+        return $this;
+    }
+}
diff --git a/app/code/Magento/Downloadable/Model/Sample.php b/app/code/Magento/Downloadable/Model/Sample.php
index 07dd821050aad1d60ae18abc38211afdd1fb4a55..4909fe6a39e649e071b47859b77a136eb31a1241 100644
--- a/app/code/Magento/Downloadable/Model/Sample.php
+++ b/app/code/Magento/Downloadable/Model/Sample.php
@@ -27,6 +27,7 @@ class Sample extends \Magento\Framework\Model\AbstractExtensibleModel implements
     const KEY_SORT_ORDER = 'sort_order';
     const KEY_SAMPLE_TYPE = 'sample_type';
     const KEY_SAMPLE_FILE = 'sample_file';
+    const KEY_SAMPLE_FILE_CONTENT = 'sample_file_content';
     const KEY_SAMPLE_URL = 'sample_url';
     /**#@-*/
 
@@ -163,6 +164,15 @@ class Sample extends \Magento\Framework\Model\AbstractExtensibleModel implements
         return $this->getData(self::KEY_SAMPLE_FILE);
     }
 
+    /**
+     * {@inheritdoc}
+     * @codeCoverageIgnore
+     */
+    public function getSampleFileContent()
+    {
+        return $this->getData(self::KEY_SAMPLE_FILE_CONTENT);
+    }
+
     /**
      * {@inheritdoc}
      * @codeCoverageIgnore
@@ -214,6 +224,17 @@ class Sample extends \Magento\Framework\Model\AbstractExtensibleModel implements
         return $this->setData(self::KEY_SAMPLE_FILE, $sampleFile);
     }
 
+    /**
+     * Set sample file content
+     *
+     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent
+     * @return $this
+     */
+    public function setSampleFileContent(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFileContent = null)
+    {
+        return $this->setData(self::KEY_SAMPLE_FILE_CONTENT, $sampleFileContent);
+    }
+
     /**
      * Set sample URL
      *
diff --git a/app/code/Magento/Downloadable/Model/Sample/Content.php b/app/code/Magento/Downloadable/Model/Sample/Content.php
deleted file mode 100644
index 0fcf613353966548cfbf6ec83e2ca47837ff9f54..0000000000000000000000000000000000000000
--- a/app/code/Magento/Downloadable/Model/Sample/Content.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Downloadable\Model\Sample;
-
-use Magento\Downloadable\Api\Data\SampleContentInterface;
-
-/**
- * @codeCoverageIgnore
- */
-class Content extends \Magento\Framework\Model\AbstractExtensibleModel implements SampleContentInterface
-{
-    const TITLE = 'title';
-    const SORT_ORDER = 'sort_order';
-    const SAMPLE_FILE = 'sample_file';
-    const SAMPLE_URL = 'sample_url';
-    const SAMPLE_TYPE = 'sample_type';
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getTitle()
-    {
-        return $this->getData(self::TITLE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSampleType()
-    {
-        return $this->getData(self::SAMPLE_TYPE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSampleFile()
-    {
-        return $this->getData(self::SAMPLE_FILE);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSortOrder()
-    {
-        return $this->getData(self::SORT_ORDER);
-    }
-
-    /**
-     * {@inheritdoc}
-     * @codeCoverageIgnore
-     */
-    public function getSampleUrl()
-    {
-        return $this->getData(self::SAMPLE_URL);
-    }
-
-    /**
-     * Set sample title
-     *
-     * @param string $title
-     * @return $this
-     */
-    public function setTitle($title)
-    {
-        return $this->setData(self::TITLE, $title);
-    }
-
-    /**
-     * Set sample type ('url' or 'file')
-     *
-     * @param string $sampleType
-     * @return $this
-     */
-    public function setSampleType($sampleType)
-    {
-        return $this->setData(self::SAMPLE_TYPE, $sampleType);
-    }
-
-    /**
-     * Set sample file content
-     *
-     * @param \Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile
-     * @return $this
-     */
-    public function setSampleFile(\Magento\Downloadable\Api\Data\File\ContentInterface $sampleFile = null)
-    {
-        return $this->setData(self::SAMPLE_FILE, $sampleFile);
-    }
-
-    /**
-     * Set sample sort order
-     *
-     * @param int $sortOrder
-     * @return $this
-     */
-    public function setSortOrder($sortOrder)
-    {
-        return $this->setData(self::SORT_ORDER, $sortOrder);
-    }
-
-    /**
-     * Set sample URL
-     *
-     * @param string $sampleUrl
-     * @return $this
-     */
-    public function setSampleUrl($sampleUrl)
-    {
-        return $this->setData(self::SAMPLE_URL, $sampleUrl);
-    }
-
-    /**
-     * {@inheritdoc}
-     *
-     * @return \Magento\Downloadable\Api\Data\SampleContentExtensionInterface|null
-     */
-    public function getExtensionAttributes()
-    {
-        return $this->_getExtensionAttributes();
-    }
-
-    /**
-     * {@inheritdoc}
-     *
-     * @param \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes
-     * @return $this
-     */
-    public function setExtensionAttributes(
-        \Magento\Downloadable\Api\Data\SampleContentExtensionInterface $extensionAttributes
-    ) {
-        return $this->_setExtensionAttributes($extensionAttributes);
-    }
-}
diff --git a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php
index 294bd783d463560a5846eada1cc400f5e6a97ce7..83f44d1a5c1b07c20229193db238f53d93cfad6f 100644
--- a/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php
+++ b/app/code/Magento/Downloadable/Model/Sample/ContentValidator.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Downloadable\Model\Sample;
 
-use Magento\Downloadable\Api\Data\SampleContentInterface;
+use Magento\Downloadable\Api\Data\SampleInterface;
 use Magento\Downloadable\Model\File\ContentValidator as FileContentValidator;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Url\Validator as UrlValidator;
@@ -37,38 +37,41 @@ class ContentValidator
     /**
      * Check if sample content is valid
      *
-     * @param SampleContentInterface $sampleContent
+     * @param SampleInterface $sample
+     * @param bool $validateSampleContent
      * @return bool
      * @throws InputException
      */
-    public function isValid(SampleContentInterface $sampleContent)
+    public function isValid(SampleInterface $sample, $validateSampleContent = true)
     {
-        if (!is_int($sampleContent->getSortOrder()) || $sampleContent->getSortOrder() < 0) {
+        if (!is_int($sample->getSortOrder()) || $sample->getSortOrder() < 0) {
             throw new InputException(__('Sort order must be a positive integer.'));
         }
 
-        $this->validateSampleResource($sampleContent);
+        if ($validateSampleContent) {
+            $this->validateSampleResource($sample);
+        }
         return true;
     }
 
     /**
      * Validate sample resource (file or URL)
      *
-     * @param SampleContentInterface $sampleContent
+     * @param SampleInterface $sample
      * @throws InputException
      * @return void
      */
-    protected function validateSampleResource(SampleContentInterface $sampleContent)
+    protected function validateSampleResource(SampleInterface $sample)
     {
-        $sampleFile = $sampleContent->getSampleFile();
-        if ($sampleContent->getSampleType() == 'file'
+        $sampleFile = $sample->getSampleFileContent();
+        if ($sample->getSampleType() == 'file'
             && (!$sampleFile || !$this->fileContentValidator->isValid($sampleFile))
         ) {
             throw new InputException(__('Provided file content must be valid base64 encoded data.'));
         }
 
-        if ($sampleContent->getSampleType() == 'url'
-            && !$this->urlValidator->isValid($sampleContent->getSampleUrl())
+        if ($sample->getSampleType() == 'url'
+            && !$this->urlValidator->isValid($sample->getSampleUrl())
         ) {
             throw new InputException(__('Sample URL must have valid format.'));
         }
diff --git a/app/code/Magento/Downloadable/Model/SampleRepository.php b/app/code/Magento/Downloadable/Model/SampleRepository.php
index b97702bd9adaf8c143dbf0f62823c460121fe02c..f357e6a5d8541ce450fab6ee85cdb782dd8fcc4e 100644
--- a/app/code/Magento/Downloadable/Model/SampleRepository.php
+++ b/app/code/Magento/Downloadable/Model/SampleRepository.php
@@ -8,12 +8,17 @@ namespace Magento\Downloadable\Model;
 use Magento\Catalog\Api\ProductRepositoryInterface;
 use Magento\Downloadable\Model\SampleFactory;
 use Magento\Downloadable\Api\Data\File\ContentUploaderInterface;
-use Magento\Downloadable\Api\Data\SampleContentInterface;
+use Magento\Downloadable\Api\Data\SampleInterface;
 use Magento\Downloadable\Model\Sample\ContentValidator;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Framework\Json\EncoderInterface;
 
+/**
+ * Class SampleRepository
+ * @package Magento\Downloadable\Model
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
 class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInterface
 {
     /**
@@ -21,6 +26,11 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte
      */
     protected $productRepository;
 
+    /**
+     * @var \Magento\Downloadable\Model\Product\Type
+     */
+    protected $downloadableType;
+
     /**
      * @var ContentValidator
      */
@@ -38,6 +48,7 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte
 
     /**
      * @param ProductRepositoryInterface $productRepository
+     * @param \Magento\Downloadable\Model\Product\Type $downloadableType
      * @param ContentValidator $contentValidator
      * @param ContentUploaderInterface $fileContentUploader
      * @param EncoderInterface $jsonEncoder
@@ -45,12 +56,14 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte
      */
     public function __construct(
         ProductRepositoryInterface $productRepository,
+        \Magento\Downloadable\Model\Product\Type $downloadableType,
         ContentValidator $contentValidator,
         ContentUploaderInterface $fileContentUploader,
         EncoderInterface $jsonEncoder,
         SampleFactory $sampleFactory
     ) {
         $this->productRepository = $productRepository;
+        $this->downloadableType = $downloadableType;
         $this->contentValidator = $contentValidator;
         $this->fileContentUploader = $fileContentUploader;
         $this->jsonEncoder = $jsonEncoder;
@@ -58,106 +71,153 @@ class SampleRepository implements \Magento\Downloadable\Api\SampleRepositoryInte
     }
 
     /**
-     * {@inheritdoc}
+     * Update downloadable sample of the given product
+     *
+     * @param string $sku
+     * @param \Magento\Downloadable\Api\Data\SampleInterface $sample
+     * @param bool $isGlobalScopeContent
+     * @return int
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     public function save(
-        $productSku,
-        SampleContentInterface $sampleContent,
-        $sampleId = null,
+        $sku,
+        SampleInterface $sample,
         $isGlobalScopeContent = false
     ) {
-        $product = $this->productRepository->get($productSku, true);
+        $product = $this->productRepository->get($sku, true);
 
+        $sampleId = $sample->getId();
         if ($sampleId) {
-
-            /** @var $sample \Magento\Downloadable\Model\Sample */
-            $sample = $this->sampleFactory->create()->load($sampleId);
-
-            if (!$sample->getId()) {
-                throw new NoSuchEntityException(__('There is no downloadable sample with provided ID.'));
-            }
-
-            if ($sample->getProductId() != $product->getId()) {
-                throw new InputException(__('Provided downloadable sample is not related to given product.'));
-            }
-            if (!$this->contentValidator->isValid($sampleContent)) {
-                throw new InputException(__('Provided sample information is invalid.'));
-            }
-            if ($isGlobalScopeContent) {
-                $product->setStoreId(0);
-            }
-
-            $title = $sampleContent->getTitle();
-            if (empty($title)) {
-                if ($isGlobalScopeContent) {
-                    throw new InputException(__('Sample title cannot be empty.'));
-                }
-                // use title from GLOBAL scope
-                $sample->setTitle(null);
-            } else {
-                $sample->setTitle($sampleContent->getTitle());
-            }
-
-            $sample->setProductId($product->getId())
-                ->setStoreId($product->getStoreId())
-                ->setSortOrder($sampleContent->getSortOrder())
-                ->save();
-
-            return $sample->getId();
+            return $this->updateSample($product, $sample, $isGlobalScopeContent);
         } else {
-
             if ($product->getTypeId() !== \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE) {
                 throw new InputException(__('Product type of the product must be \'downloadable\'.'));
             }
-            if (!$this->contentValidator->isValid($sampleContent)) {
+            if (!$this->contentValidator->isValid($sample)) {
                 throw new InputException(__('Provided sample information is invalid.'));
             }
 
-            if (!in_array($sampleContent->getSampleType(), ['url', 'file'])) {
+            if (!in_array($sample->getSampleType(), ['url', 'file'])) {
                 throw new InputException(__('Invalid sample type.'));
             }
 
-            $title = $sampleContent->getTitle();
+            $title = $sample->getTitle();
             if (empty($title)) {
                 throw new InputException(__('Sample title cannot be empty.'));
             }
 
-            $sampleData = [
-                'sample_id' => 0,
-                'is_delete' => 0,
-                'type' => $sampleContent->getSampleType(),
-                'sort_order' => $sampleContent->getSortOrder(),
-                'title' => $sampleContent->getTitle(),
-            ];
+            return $this->saveSample($product, $sample, $isGlobalScopeContent);
+        }
+    }
 
-            if ($sampleContent->getSampleType() == 'file') {
-                $sampleData['file'] = $this->jsonEncoder->encode(
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param SampleInterface $sample
+     * @param bool $isGlobalScopeContent
+     * @return int
+     */
+    protected function saveSample(
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        SampleInterface $sample,
+        $isGlobalScopeContent
+    ) {
+        $sampleData = [
+            'sample_id' => $sample->getid() === null ? 0 : $sample->getid(),
+            'is_delete' => 0,
+            'type' => $sample->getSampleType(),
+            'sort_order' => $sample->getSortOrder(),
+            'title' => $sample->getTitle(),
+        ];
+
+        if ($sample->getSampleType() == 'file' && $sample->getSampleFile() === null) {
+            $sampleData['file'] = $this->jsonEncoder->encode(
+                [
+                    $this->fileContentUploader->upload($sample->getSampleFileContent(), 'sample'),
+                ]
+            );
+        } elseif ($sample->getSampleType() === 'url') {
+            $sampleData['sample_url'] = $sample->getSampleUrl();
+        } else {
+            //existing file
+            $sampleData['file'] = $this->jsonEncoder->encode(
+                [
                     [
-                        $this->fileContentUploader->upload($sampleContent->getSampleFile(), 'sample'),
-                    ]
-                );
-            } else {
-                $sampleData['sample_url'] = $sampleContent->getSampleUrl();
-            }
+                        'file' => $sample->getSampleFile(),
+                        'status' => 'old',
+                    ],
+                ]
+            );
+        }
+
+        $downloadableData = ['sample' => [$sampleData]];
+        $product->setDownloadableData($downloadableData);
+        if ($isGlobalScopeContent) {
+            $product->setStoreId(0);
+        }
+        $this->downloadableType->save($product);
+        return $product->getLastAddedSampleId();
+    }
+
+    /**
+     * @param \Magento\Catalog\Api\Data\ProductInterface $product
+     * @param SampleInterface $sample
+     * @param bool $isGlobalScopeContent
+     * @return int
+     * @throws InputException
+     * @throws NoSuchEntityException
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    protected function updateSample(
+        \Magento\Catalog\Api\Data\ProductInterface $product,
+        SampleInterface $sample,
+        $isGlobalScopeContent
+    ) {
+        $sampleId = $sample->getId();
+        /** @var $existingSample \Magento\Downloadable\Model\Sample */
+        $existingSample = $this->sampleFactory->create()->load($sampleId);
 
-            $downloadableData = ['sample' => [$sampleData]];
-            $product->setDownloadableData($downloadableData);
+        if (!$existingSample->getId()) {
+            throw new NoSuchEntityException(__('There is no downloadable sample with provided ID.'));
+        }
+
+        if ($existingSample->getProductId() != $product->getId()) {
+            throw new InputException(__('Provided downloadable sample is not related to given product.'));
+        }
+
+        $validateFileContent = $sample->getSampleFileContent() === null ? false : true;
+        if (!$this->contentValidator->isValid($sample, $validateFileContent)) {
+            throw new InputException(__('Provided sample information is invalid.'));
+        }
+        if ($isGlobalScopeContent) {
+            $product->setStoreId(0);
+        }
+
+        $title = $sample->getTitle();
+        if (empty($title)) {
             if ($isGlobalScopeContent) {
-                $product->setStoreId(0);
+                throw new InputException(__('Sample title cannot be empty.'));
             }
-            $product->save();
-            return $product->getLastAddedSampleId();
+            // use title from GLOBAL scope
+            $existingSample->setTitle(null);
+        } else {
+            $existingSample->setTitle($sample->getTitle());
+        }
+
+        if ($sample->getSampleType() === 'file' && $sample->getSampleFileContent() === null) {
+            $sample->setSampleFile($existingSample->getSampleFile());
         }
+        $this->saveSample($product, $sample, $isGlobalScopeContent);
+        return $existingSample->getId();
     }
 
     /**
      * {@inheritdoc}
      */
-    public function delete($sampleId)
+    public function delete($id)
     {
         /** @var $sample \Magento\Downloadable\Model\Sample */
-        $sample = $this->sampleFactory->create()->load($sampleId);
+        $sample = $this->sampleFactory->create()->load($id);
         if (!$sample->getId()) {
             throw new NoSuchEntityException(__('There is no downloadable sample with provided ID.'));
         }
diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php
index 6e7a6555514b345535e784fc4b2e3abf8839c195..685d116b2b700cd10a286a22b604b435597ad3bb 100644
--- a/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php
+++ b/app/code/Magento/Downloadable/Test/Unit/Model/Link/ContentValidatorTest.php
@@ -52,12 +52,14 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
         );
         $this->linkFileMock = $this->getMock('\Magento\Downloadable\Api\Data\File\ContentInterface');
         $this->sampleFileMock = $this->getMock('\Magento\Downloadable\Api\Data\File\ContentInterface');
-        $this->validator = new \Magento\Downloadable\Model\Link\ContentValidator($this->fileValidatorMock, $this->urlValidatorMock);
+        $this->validator = new ContentValidator($this->fileValidatorMock, $this->urlValidatorMock);
     }
 
     public function testIsValid()
     {
-        $linkContentData = [
+        $linkFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface');
+        $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface');
+        $linkData = [
             'title' => 'Title',
             'sort_order' => 1,
             'price' => 10.1,
@@ -65,11 +67,53 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
             'number_of_downloads' => 100,
             'link_type' => 'file',
             'sample_type' => 'file',
+            'link_file_content' => $linkFileContentMock,
+            'sample_file_content' => $sampleFileContentMock,
         ];
         $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
         $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
-        $contentMock = $this->getLinkContentMock($linkContentData);
-        $this->assertTrue($this->validator->isValid($contentMock));
+        $linkMock = $this->getLinkMock($linkData);
+        $this->assertTrue($this->validator->isValid($linkMock));
+    }
+
+    public function testIsValidSkipLinkContent()
+    {
+        $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface');
+        $linkData = [
+            'title' => 'Title',
+            'sort_order' => 1,
+            'price' => 10.1,
+            'shareable' => true,
+            'number_of_downloads' => 100,
+            'link_type' => 'url',
+            'link_url' => 'http://example.com',
+            'sample_type' => 'file',
+            'sample_file_content' => $sampleFileContentMock,
+        ];
+        $this->fileValidatorMock->expects($this->once())->method('isValid')->will($this->returnValue(true));
+        $this->urlValidatorMock->expects($this->never())->method('isValid')->will($this->returnValue(true));
+        $linkMock = $this->getLinkMock($linkData);
+        $this->assertTrue($this->validator->isValid($linkMock, false));
+    }
+
+    public function testIsValidSkipSampleContent()
+    {
+        $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface');
+        $linkData = [
+            'title' => 'Title',
+            'sort_order' => 1,
+            'price' => 10.1,
+            'shareable' => true,
+            'number_of_downloads' => 100,
+            'link_type' => 'url',
+            'link_url' => 'http://example.com',
+            'sample_type' => 'file',
+            'sample_file_content' => $sampleFileContentMock,
+        ];
+        $this->fileValidatorMock->expects($this->never())->method('isValid')->will($this->returnValue(true));
+        $this->urlValidatorMock->expects($this->once())->method('isValid')->will($this->returnValue(true));
+        $linkMock = $this->getLinkMock($linkData);
+        $this->assertTrue($this->validator->isValid($linkMock, true, false));
     }
 
     /**
@@ -91,7 +135,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
         ];
         $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
         $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
-        $contentMock = $this->getLinkContentMock($linkContentData);
+        $contentMock = $this->getLinkMock($linkContentData);
         $this->validator->isValid($contentMock);
     }
 
@@ -126,7 +170,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
         ];
         $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
         $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
-        $contentMock = $this->getLinkContentMock($linkContentData);
+        $contentMock = $this->getLinkMock($linkContentData);
         $this->validator->isValid($contentMock);
     }
 
@@ -160,7 +204,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
         ];
         $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
         $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
-        $contentMock = $this->getLinkContentMock($linkContentData);
+        $contentMock = $this->getLinkMock($linkContentData);
         $this->validator->isValid($contentMock);
     }
 
@@ -177,51 +221,58 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @param array $linkContentData
+     * @param array $linkData
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function getLinkContentMock(array $linkContentData)
+    protected function getLinkMock(array $linkData)
     {
-        $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkContentInterface');
-        $contentMock->expects($this->any())->method('getTitle')->will($this->returnValue(
-            $linkContentData['title']
+        $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $linkMock->expects($this->any())->method('getTitle')->will($this->returnValue(
+            $linkData['title']
         ));
-        $contentMock->expects($this->any())->method('getPrice')->will($this->returnValue(
-            $linkContentData['price']
+        $linkMock->expects($this->any())->method('getPrice')->will($this->returnValue(
+            $linkData['price']
         ));
-        $contentMock->expects($this->any())->method('getSortOrder')->will($this->returnValue(
-            $linkContentData['sort_order']
+        $linkMock->expects($this->any())->method('getSortOrder')->will($this->returnValue(
+            $linkData['sort_order']
         ));
-        $contentMock->expects($this->any())->method('isShareable')->will($this->returnValue(
-            $linkContentData['shareable']
+        $linkMock->expects($this->any())->method('isShareable')->will($this->returnValue(
+            $linkData['shareable']
         ));
-        $contentMock->expects($this->any())->method('getNumberOfDownloads')->will($this->returnValue(
-            $linkContentData['number_of_downloads']
+        $linkMock->expects($this->any())->method('getNumberOfDownloads')->will($this->returnValue(
+            $linkData['number_of_downloads']
         ));
-        $contentMock->expects($this->any())->method('getLinkType')->will($this->returnValue(
-            $linkContentData['link_type']
+        $linkMock->expects($this->any())->method('getLinkType')->will($this->returnValue(
+            $linkData['link_type']
         ));
-        $contentMock->expects($this->any())->method('getLinkFile')->will($this->returnValue(
+        $linkMock->expects($this->any())->method('getLinkFile')->will($this->returnValue(
             $this->linkFileMock
         ));
-        if (isset($linkContentData['link_url'])) {
-            $contentMock->expects($this->any())->method('getLinkUrl')->will($this->returnValue(
-                $linkContentData['link_url']
+        if (isset($linkData['link_url'])) {
+            $linkMock->expects($this->any())->method('getLinkUrl')->will($this->returnValue(
+                $linkData['link_url']
             ));
         }
-        if (isset($linkContentData['sample_url'])) {
-            $contentMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue(
-                $linkContentData['sample_url']
+        if (isset($linkData['sample_url'])) {
+            $linkMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue(
+                $linkData['sample_url']
             ));
         }
-        if (isset($linkContentData['sample_type'])) {
-            $contentMock->expects($this->any())->method('getSampleType')->will($this->returnValue(
-                $linkContentData['sample_type']
+        if (isset($linkData['sample_type'])) {
+            $linkMock->expects($this->any())->method('getSampleType')->will($this->returnValue(
+                $linkData['sample_type']
             ));
         }
-        $contentMock->expects($this->any())->method('getSampleFile')->will($this->returnValue(
+        if (isset($linkData['link_file_content'])) {
+            $linkMock->expects($this->any())->method('getLinkFileContent')->willReturn($linkData['link_file_content']);
+        }
+        if (isset($linkData['sample_file_content'])) {
+            $linkMock->expects($this->any())->method('getSampleFileContent')
+                ->willReturn($linkData['sample_file_content']);
+        }
+        $linkMock->expects($this->any())->method('getSampleFile')->will($this->returnValue(
             $this->sampleFileMock
         ));
-        return $contentMock;
+        return $linkMock;
     }
 }
diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php
index 06c849f969cd47da2759747c05254ef143d2b759..d4ec184847edcbdbca8feb4e8a08c04f1b664766 100644
--- a/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php
+++ b/app/code/Magento/Downloadable/Test/Unit/Model/LinkRepositoryTest.php
@@ -128,69 +128,80 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @param array $linkContentData
+     * @param array $linkData
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function getLinkContentMock(array $linkContentData)
+    protected function getLinkMock(array $linkData)
     {
-        $contentMock = $this->getMock(
-            '\Magento\Downloadable\Api\Data\LinkContentInterface',
+        $linkMock = $this->getMock(
+            '\Magento\Downloadable\Api\Data\LinkInterface',
             [],
             [],
             '',
             false
         );
 
-        $contentMock->expects($this->any())->method('getPrice')->will(
+        if (isset($linkData['id'])) {
+            $linkMock->expects($this->any())->method('getId')->willReturn($linkData['id']);
+        }
+
+        $linkMock->expects($this->any())->method('getPrice')->will(
             $this->returnValue(
-                $linkContentData['price']
+                $linkData['price']
             )
         );
-        $contentMock->expects($this->any())->method('getTitle')->will(
+        $linkMock->expects($this->any())->method('getTitle')->will(
             $this->returnValue(
-                $linkContentData['title']
+                $linkData['title']
             )
         );
-        $contentMock->expects($this->any())->method('getSortOrder')->will(
+        $linkMock->expects($this->any())->method('getSortOrder')->will(
             $this->returnValue(
-                $linkContentData['sort_order']
+                $linkData['sort_order']
             )
         );
-        $contentMock->expects($this->any())->method('getNumberOfDownloads')->will(
+        $linkMock->expects($this->any())->method('getNumberOfDownloads')->will(
             $this->returnValue(
-                $linkContentData['number_of_downloads']
+                $linkData['number_of_downloads']
             )
         );
-        $contentMock->expects($this->any())->method('isShareable')->will(
+        $linkMock->expects($this->any())->method('getIsShareable')->will(
             $this->returnValue(
-                $linkContentData['shareable']
+                $linkData['is_shareable']
             )
         );
-        if (isset($linkContentData['link_type'])) {
-            $contentMock->expects($this->any())->method('getLinkType')->will(
+        if (isset($linkData['link_type'])) {
+            $linkMock->expects($this->any())->method('getLinkType')->will(
                 $this->returnValue(
-                    $linkContentData['link_type']
+                    $linkData['link_type']
                 )
             );
         }
-        if (isset($linkContentData['link_url'])) {
-            $contentMock->expects($this->any())->method('getLinkUrl')->will(
+        if (isset($linkData['link_url'])) {
+            $linkMock->expects($this->any())->method('getLinkUrl')->will(
                 $this->returnValue(
-                    $linkContentData['link_url']
+                    $linkData['link_url']
                 )
             );
         }
-        return $contentMock;
+        if (isset($linkData['link_file'])) {
+            $linkMock->expects($this->any())->method('getLinkFile')->will(
+                $this->returnValue(
+                    $linkData['link_file']
+                )
+            );
+        }
+        return $linkMock;
     }
 
     public function testCreate()
     {
         $productSku = 'simple';
-        $linkContentData = [
+        $linkData = [
             'title' => 'Title',
             'sort_order' => 1,
             'price' => 10.1,
-            'shareable' => true,
+            'is_shareable' => true,
             'number_of_downloads' => 100,
             'link_type' => 'url',
             'link_url' => 'http://example.com/',
@@ -198,8 +209,8 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
         $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable'));
-        $linkContentMock = $this->getLinkContentMock($linkContentData);
-        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock)
+        $linkMock = $this->getLinkMock($linkData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock)
             ->will($this->returnValue(true));
 
         $this->productMock->expects($this->once())->method('setDownloadableData')->with(
@@ -208,19 +219,20 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
                     [
                         'link_id' => 0,
                         'is_delete' => 0,
-                        'type' => $linkContentData['link_type'],
-                        'sort_order' => $linkContentData['sort_order'],
-                        'title' => $linkContentData['title'],
-                        'price' => $linkContentData['price'],
-                        'number_of_downloads' => $linkContentData['number_of_downloads'],
-                        'is_shareable' => $linkContentData['shareable'],
-                        'link_url' => $linkContentData['link_url'],
+                        'type' => $linkData['link_type'],
+                        'sort_order' => $linkData['sort_order'],
+                        'title' => $linkData['title'],
+                        'price' => $linkData['price'],
+                        'number_of_downloads' => $linkData['number_of_downloads'],
+                        'is_shareable' => $linkData['is_shareable'],
+                        'link_url' => $linkData['link_url'],
                     ],
                 ],
             ]
         );
-        $this->productMock->expects($this->once())->method('save');
-        $this->service->save($productSku, $linkContentMock, null);
+        $this->productTypeMock->expects($this->once())->method('save')
+            ->with($this->productMock);
+        $this->service->save($productSku, $linkMock);
     }
 
     /**
@@ -230,12 +242,12 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
     public function testCreateThrowsExceptionIfTitleIsEmpty()
     {
         $productSku = 'simple';
-        $linkContentData = [
+        $linkData = [
             'title' => '',
             'sort_order' => 1,
             'price' => 10.1,
             'number_of_downloads' => 100,
-            'shareable' => true,
+            'is_shareable' => true,
             'link_type' => 'url',
             'link_url' => 'http://example.com/',
         ];
@@ -243,13 +255,13 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable'));
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
-        $linkContentMock = $this->getLinkContentMock($linkContentData);
-        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock)
+        $linkMock = $this->getLinkMock($linkData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock)
             ->will($this->returnValue(true));
 
         $this->productMock->expects($this->never())->method('save');
 
-        $this->service->save($productSku, $linkContentMock, null);
+        $this->service->save($productSku, $linkMock);
     }
 
     public function testUpdate()
@@ -258,12 +270,15 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
         $linkId = 1;
         $productSku = 'simple';
         $productId = 1;
-        $linkContentData = [
+        $linkData = [
+            'id' => $linkId,
             'title' => 'Updated Title',
             'sort_order' => 1,
             'price' => 10.1,
-            'shareable' => true,
+            'is_shareable' => true,
             'number_of_downloads' => 100,
+            'link_type' => 'url',
+            'link_url' => 'http://example.com/',
         ];
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
@@ -271,54 +286,126 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
         $storeMock = $this->getMock('\Magento\Store\Model\Store', [], [], '', false);
         $storeMock->expects($this->any())->method('getWebsiteId')->will($this->returnValue($websiteId));
         $this->productMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock));
-        $linkMock = $this->getMock(
+        $existingLinkMock = $this->getMock(
             '\Magento\Downloadable\Model\Link',
             [
                 '__wakeup',
-                'setTitle',
-                'setPrice',
-                'setSortOrder',
-                'setIsShareable',
-                'setNumberOfDownloads',
                 'getId',
-                'setProductId',
-                'setStoreId',
-                'setWebsiteId',
-                'setProductWebsiteIds',
                 'load',
-                'save',
                 'getProductId'
             ],
             [],
             '',
             false
         );
-        $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($linkMock));
-        $linkContentMock = $this->getLinkContentMock($linkContentData);
-        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock)
+        $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock));
+        $linkMock = $this->getLinkMock($linkData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock)
             ->will($this->returnValue(true));
 
-        $linkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId));
-        $linkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
-        $linkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setTitle')->with($linkContentData['title'])
-            ->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setSortOrder')->with($linkContentData['sort_order'])
-            ->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setPrice')->with($linkContentData['price'])
-            ->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setIsShareable')->with($linkContentData['shareable'])
-            ->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setNumberOfDownloads')->with($linkContentData['number_of_downloads'])
-            ->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setProductId')->with($productId)
-            ->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setStoreId')->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setWebsiteId')->with($websiteId)->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('setProductWebsiteIds')->will($this->returnSelf());
-        $linkMock->expects($this->once())->method('save')->will($this->returnSelf());
-
-        $this->assertEquals($linkId, $this->service->save($productSku, $linkContentMock, $linkId));
+        $existingLinkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId));
+        $existingLinkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
+        $existingLinkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf());
+
+        $this->productMock->expects($this->once())->method('setDownloadableData')->with(
+            [
+                'link' => [
+                    [
+                        'link_id' => $linkId,
+                        'is_delete' => 0,
+                        'type' => $linkData['link_type'],
+                        'sort_order' => $linkData['sort_order'],
+                        'title' => $linkData['title'],
+                        'price' => $linkData['price'],
+                        'number_of_downloads' => $linkData['number_of_downloads'],
+                        'is_shareable' => $linkData['is_shareable'],
+                        'link_url' => $linkData['link_url'],
+                    ],
+                ],
+            ]
+        );
+        $this->productTypeMock->expects($this->once())->method('save')
+            ->with($this->productMock);
+
+        $this->assertEquals($linkId, $this->service->save($productSku, $linkMock));
+    }
+
+    public function testUpdateWithExistingFile()
+    {
+        $websiteId = 1;
+        $linkId = 1;
+        $productSku = 'simple';
+        $productId = 1;
+        $linkFile = '/l/i/link.jpg';
+        $encodedFiles = "something";
+        $linkData = [
+            'id' => $linkId,
+            'title' => 'Updated Title',
+            'sort_order' => 1,
+            'price' => 10.1,
+            'is_shareable' => true,
+            'number_of_downloads' => 100,
+            'link_type' => 'file',
+            'link_file' => $linkFile,
+        ];
+        $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
+            ->will($this->returnValue($this->productMock));
+        $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId));
+        $storeMock = $this->getMock('\Magento\Store\Model\Store', [], [], '', false);
+        $storeMock->expects($this->any())->method('getWebsiteId')->will($this->returnValue($websiteId));
+        $this->productMock->expects($this->any())->method('getStore')->will($this->returnValue($storeMock));
+        $existingLinkMock = $this->getMock(
+            '\Magento\Downloadable\Model\Link',
+            [
+                '__wakeup',
+                'getId',
+                'load',
+                'getProductId'
+            ],
+            [],
+            '',
+            false
+        );
+        $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock));
+        $linkMock = $this->getLinkMock($linkData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkMock)
+            ->will($this->returnValue(true));
+
+        $existingLinkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId));
+        $existingLinkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
+        $existingLinkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf());
+
+        $this->jsonEncoderMock->expects($this->once())
+            ->method('encode')
+            ->with(
+                [
+                    [
+                        'file' => $linkFile,
+                        'status' => 'old'
+                    ]
+                ]
+            )->willReturn($encodedFiles);
+        $this->productMock->expects($this->once())->method('setDownloadableData')->with(
+            [
+                'link' => [
+                    [
+                        'link_id' => $linkId,
+                        'is_delete' => 0,
+                        'type' => $linkData['link_type'],
+                        'sort_order' => $linkData['sort_order'],
+                        'title' => $linkData['title'],
+                        'price' => $linkData['price'],
+                        'number_of_downloads' => $linkData['number_of_downloads'],
+                        'is_shareable' => $linkData['is_shareable'],
+                        'file' => $encodedFiles,
+                    ],
+                ],
+            ]
+        );
+        $this->productTypeMock->expects($this->once())->method('save')
+            ->with($this->productMock);
+
+        $this->assertEquals($linkId, $this->service->save($productSku, $linkMock));
     }
 
     /**
@@ -330,34 +417,34 @@ class LinkRepositoryTest extends \PHPUnit_Framework_TestCase
         $linkId = 1;
         $productSku = 'simple';
         $productId = 1;
-        $linkContentData = [
+        $linkData = [
+            'id' => $linkId,
             'title' => '',
             'sort_order' => 1,
             'price' => 10.1,
             'number_of_downloads' => 100,
-            'shareable' => true,
+            'is_shareable' => true,
         ];
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
         $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId));
-        $linkMock = $this->getMock(
+        $existingLinkMock = $this->getMock(
             '\Magento\Downloadable\Model\Link',
             ['__wakeup', 'getId', 'load', 'save', 'getProductId'],
             [],
             '',
             false
         );
-        $linkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId));
-        $linkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
-        $linkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf());
-        $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($linkMock));
-        $linkContentMock = $this->getLinkContentMock($linkContentData);
+        $existingLinkMock->expects($this->any())->method('getId')->will($this->returnValue($linkId));
+        $existingLinkMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
+        $existingLinkMock->expects($this->once())->method('load')->with($linkId)->will($this->returnSelf());
+        $this->linkFactoryMock->expects($this->once())->method('create')->will($this->returnValue($existingLinkMock));
+        $linkContentMock = $this->getLinkMock($linkData);
         $this->contentValidatorMock->expects($this->any())->method('isValid')->with($linkContentMock)
             ->will($this->returnValue(true));
 
-        $linkMock->expects($this->never())->method('save');
-
-        $this->service->save($productSku, $linkContentMock, $linkId, true);
+        $this->productTypeMock->expects($this->never())->method('save');
+        $this->service->save($productSku, $linkContentMock, true);
     }
 
     public function testDelete()
diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php
index 99fb7ed07618d23e136ce710af5ddb93010b97aa..f372aaa2451400888c72491ba1607e751d69dc0a 100644
--- a/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php
+++ b/app/code/Magento/Downloadable/Test/Unit/Model/ObserverTest.php
@@ -497,6 +497,33 @@ class ObserverTest extends \PHPUnit_Framework_TestCase
         $this->assertInstanceOf('\Magento\Downloadable\Model\Observer', $result);
     }
 
+    public function testSaveDownloadableOrderItemNotDownloadableItem()
+    {
+        $itemId = 100;
+        $itemMock = $this->getMockBuilder('\Magento\Sales\Model\Order\Item')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $itemMock->expects($this->any())
+            ->method('getId')
+            ->willReturn($itemId);
+        $itemMock->expects($this->any())
+            ->method('getProductType')
+            ->willReturn('simple');
+        $itemMock->expects($this->never())
+            ->method('getProduct');
+        $event = new \Magento\Framework\Object(
+            [
+                'item' => $itemMock,
+            ]
+        );
+        $observer = new \Magento\Framework\Object(
+            [
+                'event' => $event
+            ]
+        );
+        $this->observer->saveDownloadableOrderItem($observer);
+    }
+
     /**
      * @param $id
      * @param int $statusId
diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AfterProductLoadTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AfterProductLoadTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..36f21e6fb5f28382cca4f4bec106d5772d125ec2
--- /dev/null
+++ b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AfterProductLoadTest.php
@@ -0,0 +1,220 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+// @codingStandardsIgnoreFile
+
+namespace Magento\Downloadable\Test\Unit\Model\Plugin;
+
+class AfterProductLoadTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Downloadable\Model\Plugin\AfterProductLoad
+     */
+    protected $model;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $linkRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionFactory;
+
+    protected function setUp()
+    {
+        $this->linkRepositoryMock = $this->getMock('\Magento\Downloadable\Api\LinkRepositoryInterface');
+        $this->productExtensionFactory = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtensionFactory')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->model = new \Magento\Downloadable\Model\Plugin\AfterProductLoad(
+            $this->linkRepositoryMock,
+            $this->productExtensionFactory
+        );
+        $this->productMock = $this->getMockBuilder('\Magento\Catalog\Model\Product')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->productExtensionMock = $this->getMockBuilder('\Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['setDownloadableProductLinks', 'setDownloadableProductSamples'])->getMock();
+    }
+
+    public function testAfterLoad()
+    {
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+
+        $this->productExtensionFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($this->productExtensionMock);
+
+        $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getLinksByProduct')
+            ->with($this->productMock)
+            ->willReturn([$linkMock]);
+        $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getSamplesByProduct')
+            ->with($this->productMock)
+            ->willReturn([$sampleMock]);
+        $this->productExtensionMock->expects($this->once())
+            ->method('setDownloadableProductLinks')
+            ->with([$linkMock])
+            ->willReturnSelf();
+        $this->productExtensionMock->expects($this->once())
+            ->method('setDownloadableProductSamples')
+            ->with([$sampleMock])
+            ->willReturnSelf();
+        $this->productMock->expects($this->once())
+            ->method('setExtensionAttributes')
+            ->with($this->productExtensionMock)
+            ->willReturnSelf();
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->model->afterLoad($this->productMock)
+        );
+    }
+
+    public function testAfterLoadWithExistingExtensionAttributes()
+    {
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+
+        $this->productExtensionFactory->expects($this->never())
+            ->method('create');
+
+        $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getLinksByProduct')
+            ->with($this->productMock)
+            ->willReturn([$linkMock]);
+        $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getSamplesByProduct')
+            ->with($this->productMock)
+            ->willReturn([$sampleMock]);
+        $this->productExtensionMock->expects($this->once())
+            ->method('setDownloadableProductLinks')
+            ->with([$linkMock])
+            ->willReturnSelf();
+        $this->productExtensionMock->expects($this->once())
+            ->method('setDownloadableProductSamples')
+            ->with([$sampleMock])
+            ->willReturnSelf();
+        $this->productMock->expects($this->once())
+            ->method('setExtensionAttributes')
+            ->with($this->productExtensionMock)
+            ->willReturnSelf();
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->model->afterLoad($this->productMock)
+        );
+    }
+
+    public function testAfterLoadOnlyLinks()
+    {
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+
+        $this->productExtensionFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($this->productExtensionMock);
+
+        $linkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getLinksByProduct')
+            ->with($this->productMock)
+            ->willReturn([$linkMock]);
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getSamplesByProduct')
+            ->with($this->productMock)
+            ->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('setDownloadableProductLinks')
+            ->with([$linkMock])
+            ->willReturnSelf();
+        $this->productExtensionMock->expects($this->never())
+            ->method('setDownloadableProductSamples');
+        $this->productMock->expects($this->once())
+            ->method('setExtensionAttributes')
+            ->with($this->productExtensionMock)
+            ->willReturnSelf();
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->model->afterLoad($this->productMock)
+        );
+    }
+
+    public function testAfterLoadOnlySamples()
+    {
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+
+        $this->productExtensionFactory->expects($this->once())
+            ->method('create')
+            ->willReturn($this->productExtensionMock);
+
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getLinksByProduct')
+            ->with($this->productMock)
+            ->willReturn(null);
+        $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $this->linkRepositoryMock->expects($this->once())
+            ->method('getSamplesByProduct')
+            ->with($this->productMock)
+            ->willReturn([$sampleMock]);
+        $this->productExtensionMock->expects($this->never())
+            ->method('setDownloadableProductLinks');
+        $this->productExtensionMock->expects($this->once())
+            ->method('setDownloadableProductSamples')
+            ->with([$sampleMock])
+            ->willReturnSelf();
+        $this->productMock->expects($this->once())
+            ->method('setExtensionAttributes')
+            ->with($this->productExtensionMock)
+            ->willReturnSelf();
+
+        $this->assertEquals(
+            $this->productMock,
+            $this->model->afterLoad($this->productMock)
+        );
+    }
+
+    public function testAfterLoadIfProductTypeNotDownloadable()
+    {
+        $this->productMock->expects($this->once())
+            ->method('getTypeId')
+            ->willReturn(\Magento\Catalog\Model\Product\Type::TYPE_SIMPLE);
+        $this->productMock->expects($this->never())->method('getExtensionAttributes');
+        $this->productMock->expects($this->never())->method('setExtensionAttributes');
+        $this->assertEquals(
+            $this->productMock,
+            $this->model->afterLoad($this->productMock)
+        );
+    }
+}
diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..e33241defa659ee03c309c4ca74fe85b237a9b07
--- /dev/null
+++ b/app/code/Magento/Downloadable/Test/Unit/Model/Plugin/AroundProductRepositorySaveTest.php
@@ -0,0 +1,328 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+// @codingStandardsIgnoreFile
+
+namespace Magento\Downloadable\Test\Unit\Model\Plugin;
+
+use \Magento\Downloadable\Model\Plugin\AroundProductRepositorySave;
+
+class AroundProductRepositorySaveTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var AroundProductRepositorySave
+     */
+    protected $model;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $linkRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $sampleRepositoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $savedProductMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productExtensionMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $existingProductExtensionMock;
+
+    /**
+     * @var \Closure
+     */
+    protected $closureMock;
+
+    protected function setUp()
+    {
+        $this->productRepositoryMock = $this->getMock('Magento\Catalog\Api\ProductRepositoryInterface');
+        $this->linkRepositoryMock = $this->getMock('Magento\Downloadable\Api\LinkRepositoryInterface');
+        $this->sampleRepositoryMock = $this->getMock('Magento\Downloadable\Api\SampleRepositoryInterface');
+        $this->productMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
+        $this->savedProductMock = $this->getMock('\Magento\Catalog\Api\Data\ProductInterface');
+        $this->closureMock = function () {
+            return $this->savedProductMock;
+        };
+        $this->model = new AroundProductRepositorySave(
+            $this->linkRepositoryMock,
+            $this->sampleRepositoryMock        );
+        $this->productExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['getDownloadableProductLinks', 'getDownloadableProductSamples'])->getMock();
+        $this->existingProductExtensionMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension')
+            ->setMethods(['getDownloadableProductLinks', 'getDownloadableProductSamples'])
+            ->getMock();
+    }
+
+    public function testAroundSaveWhenProductIsSimple()
+    {
+        $this->productMock->expects($this->once())->method('getTypeId')->willReturn('simple');
+        $this->productMock->expects($this->never())->method('getExtensionAttributes');
+
+        $this->assertEquals(
+            $this->savedProductMock,
+            $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    public function testAroundSaveWhenProductHasNoExtensionAttributes()
+    {
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn(null);
+
+        $this->savedProductMock->expects($this->never())->method('getExtensionAttributes');
+        $this->linkRepositoryMock->expects($this->never())->method('save');
+
+        $this->assertEquals(
+            $this->savedProductMock,
+            $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    /**
+     * Input has two links and two samples, one existing and one new
+     * Existing product has two links and two samples, one will be updated and one will be deleted
+     */
+    public function testAroundSave()
+    {
+        $productSku = "downloadable_product";
+        $existingLinkId = '2';
+        $existingSampleId = '5';
+        $toBeDeletedLinkId = '3';
+        $toBeDeletedSampleId = '4';
+
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $updateLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $updateLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId);
+        $newLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $newLinkMock->expects($this->once())->method('getId')->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getDownloadableProductLinks')
+            ->willReturn([$newLinkMock, $updateLinkMock]);
+
+        $updateSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $updateSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId);
+        $newSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $newSampleMock->expects($this->once())->method('getId')->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getDownloadableProductSamples')
+            ->willReturn([$updateSampleMock, $newSampleMock]);
+
+        $existingLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $existingLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId);
+        $toBeDeletedLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $toBeDeletedLinkMock->expects($this->once())->method('getId')->willReturn($toBeDeletedLinkId);
+
+        $existingSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $existingSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId);
+        $toBeDeletedSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $toBeDeletedSampleMock->expects($this->once())->method('getId')->willReturn($toBeDeletedSampleId);
+
+        $this->savedProductMock->expects($this->any())->method('getSku')->willReturn($productSku);
+        $this->savedProductMock->expects($this->exactly(2))->method('getExtensionAttributes')
+            ->willReturn($this->existingProductExtensionMock);
+        $this->existingProductExtensionMock->expects($this->once())
+            ->method('getDownloadableProductLinks')
+            ->willReturn([$existingLinkMock, $toBeDeletedLinkMock]);
+        $this->existingProductExtensionMock->expects($this->once())
+            ->method('getDownloadableProductSamples')
+            ->willReturn([$existingSampleMock, $toBeDeletedSampleMock]);
+
+        $this->linkRepositoryMock->expects($this->at(0))
+            ->method('save')
+            ->with($productSku, $updateLinkMock);
+        $this->linkRepositoryMock->expects($this->at(1))
+            ->method('save')
+            ->with($productSku, $newLinkMock);
+        $this->linkRepositoryMock->expects($this->at(2))
+            ->method('delete')
+            ->with($toBeDeletedLinkId);
+
+        $this->sampleRepositoryMock->expects($this->at(0))
+            ->method('save')
+            ->with($productSku, $updateSampleMock);
+        $this->sampleRepositoryMock->expects($this->at(1))
+            ->method('save')
+            ->with($productSku, $newSampleMock);
+        $this->sampleRepositoryMock->expects($this->at(2))
+            ->method('delete')
+            ->with($toBeDeletedSampleId);
+
+        $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
+            ->disableOriginalConstructor()->getMock();
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku, false, null, true)
+            ->willReturn($newProductMock);
+
+        $this->assertEquals(
+            $newProductMock,
+            $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    /**
+     * Input has two links and no samples, one existing and one new
+     * Existing product has two links, one will be updated and one will be deleted
+     */
+    public function testAroundSaveWithOnlyLinks()
+    {
+        $productSku = "downloadable_product";
+        $existingLinkId = '2';
+        $toBeDeletedLinkId = '3';
+
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $updateLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $updateLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId);
+        $newLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $newLinkMock->expects($this->once())->method('getId')->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getDownloadableProductLinks')
+            ->willReturn([$newLinkMock, $updateLinkMock]);
+
+        $this->productExtensionMock->expects($this->once())
+            ->method('getDownloadableProductSamples')
+            ->willReturn(null);
+
+        $existingLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $existingLinkMock->expects($this->once())->method('getId')->willReturn($existingLinkId);
+        $toBeDeletedLinkMock = $this->getMock('\Magento\Downloadable\Api\Data\LinkInterface');
+        $toBeDeletedLinkMock->expects($this->once())->method('getId')->willReturn($toBeDeletedLinkId);
+
+        $this->savedProductMock->expects($this->any())->method('getSku')->willReturn($productSku);
+        $this->savedProductMock->expects($this->once())->method('getExtensionAttributes')
+            ->willReturn($this->existingProductExtensionMock);
+        $this->existingProductExtensionMock->expects($this->once())
+            ->method('getDownloadableProductLinks')
+            ->willReturn([$existingLinkMock, $toBeDeletedLinkMock]);
+        $this->existingProductExtensionMock->expects($this->never())
+            ->method('getDownloadableProductSamples');
+
+        $this->linkRepositoryMock->expects($this->at(0))
+            ->method('save')
+            ->with($productSku, $updateLinkMock);
+        $this->linkRepositoryMock->expects($this->at(1))
+            ->method('save')
+            ->with($productSku, $newLinkMock);
+        $this->linkRepositoryMock->expects($this->at(2))
+            ->method('delete')
+            ->with($toBeDeletedLinkId);
+
+        $this->sampleRepositoryMock->expects($this->never())
+            ->method('save');
+
+        $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
+            ->disableOriginalConstructor()->getMock();
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku, false, null, true)
+            ->willReturn($newProductMock);
+
+        $this->assertEquals(
+            $newProductMock,
+            $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+
+    /**
+     * Input has two samples, one existing and one new
+     * Existing product has two samples, one will be updated and one will be deleted
+     */
+    public function testAroundSaveWithOnlySamples()
+    {
+        $productSku = "downloadable_product";
+        $existingSampleId = '5';
+        $toBeDeletedSampleId = '4';
+
+        $this->productMock->expects($this->once())->method('getTypeId')
+            ->willReturn(\Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE);
+        $this->productMock->expects($this->once())
+            ->method('getExtensionAttributes')
+            ->willReturn($this->productExtensionMock);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getDownloadableProductLinks')
+            ->willReturn(null);
+
+        $updateSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $updateSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId);
+        $newSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $newSampleMock->expects($this->once())->method('getId')->willReturn(null);
+        $this->productExtensionMock->expects($this->once())
+            ->method('getDownloadableProductSamples')
+            ->willReturn([$updateSampleMock, $newSampleMock]);
+
+        $existingSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $existingSampleMock->expects($this->once())->method('getId')->willReturn($existingSampleId);
+        $toBeDeletedSampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
+        $toBeDeletedSampleMock->expects($this->once())->method('getId')->willReturn($toBeDeletedSampleId);
+
+        $this->savedProductMock->expects($this->any())->method('getSku')->willReturn($productSku);
+        $this->savedProductMock->expects($this->once())->method('getExtensionAttributes')
+            ->willReturn($this->existingProductExtensionMock);
+        $this->existingProductExtensionMock->expects($this->never())
+            ->method('getDownloadableProductLinks');
+        $this->existingProductExtensionMock->expects($this->once())
+            ->method('getDownloadableProductSamples')
+            ->willReturn([$existingSampleMock, $toBeDeletedSampleMock]);
+
+        $this->linkRepositoryMock->expects($this->never())
+            ->method('save');
+
+        $this->sampleRepositoryMock->expects($this->at(0))
+            ->method('save')
+            ->with($productSku, $updateSampleMock);
+        $this->sampleRepositoryMock->expects($this->at(1))
+            ->method('save')
+            ->with($productSku, $newSampleMock);
+        $this->sampleRepositoryMock->expects($this->at(2))
+            ->method('delete')
+            ->with($toBeDeletedSampleId);
+
+        $newProductMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductInterface')
+            ->disableOriginalConstructor()->getMock();
+        $this->productRepositoryMock->expects($this->once())
+            ->method('get')
+            ->with($productSku, false, null, true)
+            ->willReturn($newProductMock);
+
+        $this->assertEquals(
+            $newProductMock,
+            $this->model->aroundSave($this->productRepositoryMock, $this->closureMock, $this->productMock)
+        );
+    }
+}
diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php
index 8a94c4b8ba1279711a75d133a7697b5cf49738c5..517563a68cd8b58ee2baba39bf71ca479dd55005 100644
--- a/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php
+++ b/app/code/Magento/Downloadable/Test/Unit/Model/Sample/ContentValidatorTest.php
@@ -51,15 +51,17 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
             false
         );
         $this->sampleFileMock = $this->getMock('\Magento\Downloadable\Api\Data\File\ContentInterface');
-        $this->validator = new \Magento\Downloadable\Model\Sample\ContentValidator($this->fileValidatorMock, $this->urlValidatorMock);
+        $this->validator = new ContentValidator($this->fileValidatorMock, $this->urlValidatorMock);
     }
 
     public function testIsValid()
     {
+        $sampleFileContentMock = $this->getMock('Magento\Downloadable\Api\Data\File\ContentInterface');
         $sampleContentData = [
             'title' => 'Title',
             'sort_order' => 1,
             'sample_type' => 'file',
+            'sample_file_content' => $sampleFileContentMock,
         ];
         $this->fileValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
         $this->urlValidatorMock->expects($this->any())->method('isValid')->will($this->returnValue(true));
@@ -103,7 +105,7 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
      */
     protected function getSampleContentMock(array $sampleContentData)
     {
-        $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleContentInterface');
+        $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
         $contentMock->expects($this->any())->method('getTitle')->will($this->returnValue(
             $sampleContentData['title']
         ));
@@ -119,6 +121,10 @@ class ContentValidatorTest extends \PHPUnit_Framework_TestCase
                 $sampleContentData['sample_url']
             ));
         }
+        if (isset($sampleContentData['sample_file_content'])) {
+            $contentMock->expects($this->any())->method('getSampleFileContent')
+                ->willReturn($sampleContentData['sample_file_content']);
+        }
         $contentMock->expects($this->any())->method('getSampleFile')->will($this->returnValue(
             $this->sampleFileMock
         ));
diff --git a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php
index 4027405e14f5c27eaf84dc245574e21d4a8a156a..7e25af50c3f6b771308ffc0eed2f79b227bd35b6 100644
--- a/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php
+++ b/app/code/Magento/Downloadable/Test/Unit/Model/SampleRepositoryTest.php
@@ -15,6 +15,11 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
      */
     protected $repositoryMock;
 
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productTypeMock;
+
     /**
      * @var \PHPUnit_Framework_MockObject_MockObject
      */
@@ -55,6 +60,7 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
             false
         );
         $this->repositoryMock = $this->getMock('\Magento\Catalog\Model\ProductRepository', [], [], '', false);
+        $this->productTypeMock = $this->getMock('\Magento\Downloadable\Model\Product\Type', [], [], '', false);
         $this->contentValidatorMock = $this->getMock(
             'Magento\Downloadable\Model\Sample\ContentValidator',
             [],
@@ -78,6 +84,7 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
 
         $this->service = new \Magento\Downloadable\Model\SampleRepository(
             $this->repositoryMock,
+            $this->productTypeMock,
             $this->contentValidatorMock,
             $this->contentUploaderMock,
             $this->jsonEncoderMock,
@@ -86,35 +93,43 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
-     * @param array $sampleContentData
+     * @param array $sampleData
      * @return \PHPUnit_Framework_MockObject_MockObject
      */
-    protected function getSampleContentMock(array $sampleContentData)
+    protected function getSampleMock(array $sampleData)
     {
-        $contentMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleContentInterface');
+        $sampleMock = $this->getMock('\Magento\Downloadable\Api\Data\SampleInterface');
 
-        $contentMock->expects($this->any())->method('getTitle')->will($this->returnValue($sampleContentData['title']));
-        $contentMock->expects($this->any())->method('getSortOrder')->will($this->returnValue(
-            $sampleContentData['sort_order']
+        if (isset($sampleData['id'])) {
+            $sampleMock->expects($this->any())->method('getId')->willReturn($sampleData['id']);
+        }
+        $sampleMock->expects($this->any())->method('getTitle')->will($this->returnValue($sampleData['title']));
+        $sampleMock->expects($this->any())->method('getSortOrder')->will($this->returnValue(
+            $sampleData['sort_order']
         ));
 
-        if (isset($sampleContentData['sample_type'])) {
-            $contentMock->expects($this->any())->method('getSampleType')->will($this->returnValue(
-                $sampleContentData['sample_type']
+        if (isset($sampleData['sample_type'])) {
+            $sampleMock->expects($this->any())->method('getSampleType')->will($this->returnValue(
+                $sampleData['sample_type']
+            ));
+        }
+        if (isset($sampleData['sample_url'])) {
+            $sampleMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue(
+                $sampleData['sample_url']
             ));
         }
-        if (isset($sampleContentData['sample_url'])) {
-            $contentMock->expects($this->any())->method('getSampleUrl')->will($this->returnValue(
-                $sampleContentData['sample_url']
+        if (isset($sampleData['sample_file'])) {
+            $sampleMock->expects($this->any())->method('getSampleFile')->will($this->returnValue(
+                $sampleData['sample_file']
             ));
         }
-        return $contentMock;
+        return $sampleMock;
     }
 
     public function testCreate()
     {
         $productSku = 'simple';
-        $sampleContentData = [
+        $sampleData = [
             'title' => 'Title',
             'sort_order' => 1,
             'sample_type' => 'url',
@@ -123,8 +138,8 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
         $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable'));
-        $sampleContentMock = $this->getSampleContentMock($sampleContentData);
-        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock)
+        $sampleMock = $this->getSampleMock($sampleData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock)
             ->will($this->returnValue(true));
 
         $this->productMock->expects($this->once())->method('setDownloadableData')->with([
@@ -132,15 +147,15 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
                 [
                     'sample_id' => 0,
                     'is_delete' => 0,
-                    'type' => $sampleContentData['sample_type'],
-                    'sort_order' => $sampleContentData['sort_order'],
-                    'title' => $sampleContentData['title'],
-                    'sample_url' => $sampleContentData['sample_url'],
+                    'type' => $sampleData['sample_type'],
+                    'sort_order' => $sampleData['sort_order'],
+                    'title' => $sampleData['title'],
+                    'sample_url' => $sampleData['sample_url'],
                 ],
             ],
         ]);
-        $this->productMock->expects($this->once())->method('save');
-        $this->service->save($productSku, $sampleContentMock, null);
+        $this->productTypeMock->expects($this->once())->method('save')->with($this->productMock);
+        $this->service->save($productSku, $sampleMock);
     }
 
     /**
@@ -150,7 +165,7 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
     public function testCreateThrowsExceptionIfTitleIsEmpty()
     {
         $productSku = 'simple';
-        $sampleContentData = [
+        $sampleData = [
             'title' => '',
             'sort_order' => 1,
             'sample_type' => 'url',
@@ -160,13 +175,13 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
         $this->productMock->expects($this->any())->method('getTypeId')->will($this->returnValue('downloadable'));
-        $sampleContentMock = $this->getSampleContentMock($sampleContentData);
-        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock)
+        $sampleMock = $this->getSampleMock($sampleData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock)
             ->will($this->returnValue(true));
 
-        $this->productMock->expects($this->never())->method('save');
+        $this->productTypeMock->expects($this->never())->method('save');
 
-        $this->service->save($productSku, $sampleContentMock, null);
+        $this->service->save($productSku, $sampleMock);
     }
 
     public function testUpdate()
@@ -174,39 +189,109 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
         $sampleId = 1;
         $productId = 1;
         $productSku = 'simple';
-        $sampleContentData = [
+        $sampleData = [
+            'id' => $sampleId,
             'title' => 'Updated Title',
             'sort_order' => 1,
+            'sample_type' => 'url',
+            'sample_url' => 'http://example.com/',
         ];
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
         $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId));
-        $sampleMock = $this->getMock(
+        $existingSampleMock = $this->getMock(
             '\Magento\Downloadable\Model\Sample',
-            ['__wakeup', 'setTitle', 'setSortOrder', 'getId', 'setProductId', 'setStoreId',
-                'load', 'save', 'getProductId'],
+            ['__wakeup', 'getId', 'load', 'getProductId'],
             [],
             '',
             false
         );
-        $this->sampleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($sampleMock));
-        $sampleContentMock = $this->getSampleContentMock($sampleContentData);
-        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock)
+        $this->sampleFactoryMock->expects($this->once())->method('create')
+            ->will($this->returnValue($existingSampleMock));
+        $sampleMock = $this->getSampleMock($sampleData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock)
             ->will($this->returnValue(true));
 
-        $sampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId));
-        $sampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
-        $sampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf());
-        $sampleMock->expects($this->once())->method('setTitle')->with($sampleContentData['title'])
-            ->will($this->returnSelf());
-        $sampleMock->expects($this->once())->method('setSortOrder')->with($sampleContentData['sort_order'])
-            ->will($this->returnSelf());
-        $sampleMock->expects($this->once())->method('setProductId')->with($productId)
-            ->will($this->returnSelf());
-        $sampleMock->expects($this->once())->method('setStoreId')->will($this->returnSelf());
-        $sampleMock->expects($this->once())->method('save')->will($this->returnSelf());
-
-        $this->assertEquals($sampleId, $this->service->save($productSku, $sampleContentMock, $sampleId));
+        $existingSampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId));
+        $existingSampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
+        $existingSampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf());
+
+        $this->productMock->expects($this->once())->method('setDownloadableData')->with([
+            'sample' => [
+                [
+                    'sample_id' => $sampleId,
+                    'is_delete' => 0,
+                    'type' => $sampleData['sample_type'],
+                    'sort_order' => $sampleData['sort_order'],
+                    'title' => $sampleData['title'],
+                    'sample_url' => $sampleData['sample_url'],
+                ],
+            ],
+        ]);
+        $this->productTypeMock->expects($this->once())->method('save')->with($this->productMock);
+
+        $this->assertEquals($sampleId, $this->service->save($productSku, $sampleMock));
+    }
+
+    public function testUpdateWithExistingFile()
+    {
+        $sampleId = 1;
+        $productId = 1;
+        $productSku = 'simple';
+        $sampleFile = '/s/a/sample.jpg';
+        $encodedFile = 'something';
+        $sampleData = [
+            'id' => $sampleId,
+            'title' => 'Updated Title',
+            'sort_order' => 1,
+            'sample_type' => 'file',
+            'sample_file' => $sampleFile,
+        ];
+        $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
+            ->will($this->returnValue($this->productMock));
+        $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId));
+        $existingSampleMock = $this->getMock(
+            '\Magento\Downloadable\Model\Sample',
+            ['__wakeup', 'getId', 'load', 'getProductId'],
+            [],
+            '',
+            false
+        );
+        $this->sampleFactoryMock->expects($this->once())->method('create')
+            ->will($this->returnValue($existingSampleMock));
+        $sampleMock = $this->getSampleMock($sampleData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock)
+            ->will($this->returnValue(true));
+
+        $existingSampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId));
+        $existingSampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
+        $existingSampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf());
+
+        $this->jsonEncoderMock->expects($this->once())
+            ->method('encode')
+            ->with(
+                [
+                    [
+                        'file' => $sampleFile,
+                        'status' => 'old',
+                    ]
+                ]
+            )->willReturn($encodedFile);
+        $this->productMock->expects($this->once())->method('setDownloadableData')->with([
+            'sample' => [
+                [
+                    'sample_id' => $sampleId,
+                    'is_delete' => 0,
+                    'type' => $sampleData['sample_type'],
+                    'sort_order' => $sampleData['sort_order'],
+                    'title' => $sampleData['title'],
+                    'file' => $encodedFile,
+                ],
+            ],
+        ]);
+        $this->productTypeMock->expects($this->once())->method('save')->with($this->productMock);
+
+        $this->assertEquals($sampleId, $this->service->save($productSku, $sampleMock));
     }
 
     /**
@@ -218,31 +303,33 @@ class SampleRepositoryTest extends \PHPUnit_Framework_TestCase
         $sampleId = 1;
         $productSku = 'simple';
         $productId = 1;
-        $sampleContentData = [
+        $sampleData = [
+            'id' => $sampleId,
             'title' => '',
             'sort_order' => 1,
         ];
         $this->repositoryMock->expects($this->any())->method('get')->with($productSku, true)
             ->will($this->returnValue($this->productMock));
         $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($productId));
-        $sampleMock = $this->getMock(
+        $existingSampleMock = $this->getMock(
             '\Magento\Downloadable\Model\Sample',
             ['__wakeup', 'getId', 'load', 'save', 'getProductId'],
             [],
             '',
             false
         );
-        $sampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId));
-        $sampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf());
-        $sampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
-        $this->sampleFactoryMock->expects($this->once())->method('create')->will($this->returnValue($sampleMock));
-        $sampleContentMock = $this->getSampleContentMock($sampleContentData);
-        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleContentMock)
+        $existingSampleMock->expects($this->any())->method('getId')->will($this->returnValue($sampleId));
+        $existingSampleMock->expects($this->once())->method('load')->with($sampleId)->will($this->returnSelf());
+        $existingSampleMock->expects($this->any())->method('getProductId')->will($this->returnValue($productId));
+        $this->sampleFactoryMock->expects($this->once())->method('create')
+            ->will($this->returnValue($existingSampleMock));
+        $sampleMock = $this->getSampleMock($sampleData);
+        $this->contentValidatorMock->expects($this->any())->method('isValid')->with($sampleMock)
             ->will($this->returnValue(true));
 
-        $sampleMock->expects($this->never())->method('save');
+        $this->productTypeMock->expects($this->never())->method('save');
 
-        $this->service->save($productSku, $sampleContentMock, $sampleId, true);
+        $this->service->save($productSku, $sampleMock, true);
     }
 
     public function testDelete()
diff --git a/app/code/Magento/Downloadable/etc/di.xml b/app/code/Magento/Downloadable/etc/di.xml
index 9b492b051acc779329be9e761c9ede86ce6752e7..acb63729a578108f11e7b5f9d59c60d3d1e00745 100644
--- a/app/code/Magento/Downloadable/etc/di.xml
+++ b/app/code/Magento/Downloadable/etc/di.xml
@@ -58,12 +58,16 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Catalog\Model\Product">
+        <plugin name="downloadableAfterLoad" type="\Magento\Downloadable\Model\Plugin\AfterProductLoad"/>
+    </type>
+    <type name="Magento\Catalog\Api\ProductRepositoryInterface">
+        <plugin name="downloadableAroundSave" type="\Magento\Downloadable\Model\Plugin\AroundProductRepositorySave"/>
+    </type>
     <preference for="\Magento\Downloadable\Api\LinkRepositoryInterface" type="\Magento\Downloadable\Model\LinkRepository" />
     <preference for="\Magento\Downloadable\Api\SampleRepositoryInterface" type="\Magento\Downloadable\Model\SampleRepository" />
     <preference for="\Magento\Downloadable\Api\Data\LinkInterface" type="\Magento\Downloadable\Model\Link" />
     <preference for="\Magento\Downloadable\Api\Data\SampleInterface" type="\Magento\Downloadable\Model\Sample" />
-    <preference for="\Magento\Downloadable\Api\Data\SampleContentInterface" type="\Magento\Downloadable\Model\Sample\Content" />
-    <preference for="\Magento\Downloadable\Api\Data\LinkContentInterface" type="\Magento\Downloadable\Model\Link\Content" />
     <preference for="\Magento\Downloadable\Api\Data\File\ContentInterface" type="\Magento\Downloadable\Model\File\Content" />
     <preference for="\Magento\Downloadable\Api\Data\File\ContentUploaderInterface" type="\Magento\Downloadable\Model\File\ContentUploader" />
     <preference for="\Magento\Downloadable\Model\Product\TypeHandler\TypeHandlerInterface" type="\Magento\Downloadable\Model\Product\TypeHandler\TypeHandler" />
diff --git a/app/code/Magento/Downloadable/etc/service_data_attributes.xml b/app/code/Magento/Downloadable/etc/service_data_attributes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..80cc1cb0abf9ff457b97d12eddc9b3b6ee519784
--- /dev/null
+++ b/app/code/Magento/Downloadable/etc/service_data_attributes.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
+        <attribute code="downloadable_product_links" type="Magento\Downloadable\Api\Data\LinkInterface[]" />
+        <attribute code="downloadable_product_samples" type="Magento\Downloadable\Api\Data\SampleInterface[]" />
+    </extension_attributes>
+</config>
diff --git a/app/code/Magento/Downloadable/etc/webapi.xml b/app/code/Magento/Downloadable/etc/webapi.xml
index 0291fe5322f8328380ab63b58dfd7b8b8322dbe2..7f597119f9e74db7142d3baa866ec3c697c67e45 100644
--- a/app/code/Magento/Downloadable/etc/webapi.xml
+++ b/app/code/Magento/Downloadable/etc/webapi.xml
@@ -25,13 +25,13 @@
             <resource ref="Magento_Downloadable::downloadable" />
         </resources>
     </route>
-    <route url="/V1/products/:sku/downloadable-links/:linkId" method="PUT">
+    <route url="/V1/products/:sku/downloadable-links/:id" method="PUT">
         <service class="Magento\Downloadable\Api\LinkRepositoryInterface" method="save"/>
         <resources>
             <resource ref="Magento_Downloadable::downloadable" />
         </resources>
     </route>
-    <route url="/V1/products/downloadable-links/:linkId" method="DELETE">
+    <route url="/V1/products/downloadable-links/:id" method="DELETE">
         <service class="Magento\Downloadable\Api\LinkRepositoryInterface" method="delete"/>
         <resources>
             <resource ref="Magento_Downloadable::downloadable" />
@@ -43,13 +43,13 @@
             <resource ref="Magento_Downloadable::downloadable" />
         </resources>
     </route>
-    <route url="/V1/products/:sku/downloadable-links/samples/:sampleId" method="PUT">
+    <route url="/V1/products/:sku/downloadable-links/samples/:id" method="PUT">
     <service class="Magento\Downloadable\Api\SampleRepositoryInterface" method="save"/>
     <resources>
         <resource ref="Magento_Downloadable::downloadable" />
     </resources>
 </route>
-    <route url="/V1/products/downloadable-links/samples/:sampleId" method="DELETE">
+    <route url="/V1/products/downloadable-links/samples/:id" method="DELETE">
         <service class="Magento\Downloadable\Api\SampleRepositoryInterface" method="delete"/>
         <resources>
             <resource ref="Magento_Downloadable::downloadable" />
diff --git a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
index ac8c5805f4b72f8727d09a4a4cbc71a0f05c2258..c8fa8214eab58921da003c19e9a5c55b4a3d66c1 100755
--- a/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
+++ b/app/code/Magento/Eav/Model/Entity/AbstractEntity.php
@@ -31,14 +31,14 @@ abstract class AbstractEntity extends \Magento\Framework\Model\Resource\Abstract
     /**
      * Read connection
      *
-     * @var \Magento\Framework\DB\Adapter\Pdo\Mysql
+     * @var \Magento\Framework\DB\Adapter\Pdo\Mysql | string
      */
     protected $_read;
 
     /**
      * Write connection
      *
-     * @var \Magento\Framework\DB\Adapter\Pdo\Mysql
+     * @var \Magento\Framework\DB\Adapter\Pdo\Mysql | string
      */
     protected $_write;
 
diff --git a/app/code/Magento/Eav/etc/di.xml b/app/code/Magento/Eav/etc/di.xml
index 3dfebf456c7ff4ae26f6f8a118431872c7c2f67e..00524114f784dd3bc859a8395a9c1d23012433cc 100644
--- a/app/code/Magento/Eav/etc/di.xml
+++ b/app/code/Magento/Eav/etc/di.xml
@@ -57,4 +57,9 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Eav\Model\Entity\Attribute">
+        <arguments>
+            <argument name="reservedAttributeList" xsi:type="object">Magento\Catalog\Model\Product\ReservedAttributeList\Proxy</argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Email/Model/BackendTemplate.php b/app/code/Magento/Email/Model/BackendTemplate.php
index be5b232564a67e139724e96f6d7a51a89702ed9c..c8bc6b34398e0c492f9123cfaeedc8182da23299 100644
--- a/app/code/Magento/Email/Model/BackendTemplate.php
+++ b/app/code/Magento/Email/Model/BackendTemplate.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Email\Model;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 /**
  * Adminhtml email template model
  *
@@ -78,7 +80,7 @@ class BackendTemplate extends Template
             return [];
         }
 
-        $configData = $this->_scopeConfig->getValue(null, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT);
+        $configData = $this->_scopeConfig->getValue(null, ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
         $paths = $this->_findEmailTemplateUsages($templateCode, $configData, '');
         return $paths;
     }
diff --git a/app/code/Magento/GiftMessage/etc/data_object.xml b/app/code/Magento/GiftMessage/etc/service_data_attributes.xml
similarity index 64%
rename from app/code/Magento/GiftMessage/etc/data_object.xml
rename to app/code/Magento/GiftMessage/etc/service_data_attributes.xml
index a1255cc8a161a5a8d28090697088a87ede8920e7..f02c0a717e169224d0b7d450e861f38d7176dc7f 100644
--- a/app/code/Magento/GiftMessage/etc/data_object.xml
+++ b/app/code/Magento/GiftMessage/etc/service_data_attributes.xml
@@ -5,11 +5,11 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Sales\Api\Data\OrderInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
         <attribute code="gift_message" type="Magento\GiftMessage\Api\Data\MessageInterface" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Sales\Api\Data\OrderItemInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Sales\Api\Data\OrderItemInterface">
         <attribute code="gift_message" type="Magento\GiftMessage\Api\Data\MessageInterface" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/app/code/Magento/GoogleAdwords/etc/di.xml b/app/code/Magento/GoogleAdwords/etc/di.xml
new file mode 100644
index 0000000000000000000000000000000000000000..293db157d7c38cca198df46a541016f71c97dd54
--- /dev/null
+++ b/app/code/Magento/GoogleAdwords/etc/di.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
+    <type name="Magento\GoogleAdwords\Model\Observer">
+        <arguments>
+            <argument name="collection" xsi:type="object">Magento\Sales\Model\Resource\Order\Collection\Proxy</argument>
+        </arguments>
+    </type>
+</config>
diff --git a/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php b/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php
index 089f14216bdadb05a1ee8c4b4530526b46ad892a..d20e9cc0cc88c9215df5aa15b0b69cd32ccb5061 100644
--- a/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php
+++ b/app/code/Magento/GoogleShopping/Model/Attribute/Tax.php
@@ -8,6 +8,7 @@ namespace Magento\GoogleShopping\Model\Attribute;
 use Magento\Framework\Parse\Zip;
 use Magento\Store\Model\Store;
 use Magento\Tax\Api\Data\TaxClassKeyInterface;
+use Magento\Tax\Model\TaxClass\Key;
 
 /**
  * Tax attribute model
@@ -179,8 +180,8 @@ class Tax extends \Magento\GoogleShopping\Model\Attribute\DefaultAttribute
                         'code' => $product->getSku(),
                         'type' => 'product',
                         'tax_class_key' => [
-                            TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
-                            TaxClassKeyInterface::KEY_VALUE => $product->getTaxClassId(),
+                            Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
+                            Key::KEY_VALUE => $product->getTaxClassId(),
                         ],
                         'unit_price' => $product->getPrice(),
                         'quantity' => 1,
@@ -204,8 +205,8 @@ class Tax extends \Magento\GoogleShopping\Model\Attribute\DefaultAttribute
                         'billing_address' => $billingAddressDataArray,
                         'shipping_address' => $shippingAddressDataArray,
                         'customer_tax_class_key' => [
-                            TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
-                            TaxClassKeyInterface::KEY_VALUE => $defaultCustomerTaxClassId,
+                            Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
+                            Key::KEY_VALUE => $defaultCustomerTaxClassId,
                         ],
                         'items' => [
                             $quoteDetailsItemDataArray,
diff --git a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php
index d856527779116c8c6795c79d704fc90a892bfcd2..5ee51f1ecbfb45622fd6f8c28955de99da4c2874 100644
--- a/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php
+++ b/app/code/Magento/GroupedProduct/Model/Product/Type/Grouped.php
@@ -480,7 +480,7 @@ class Grouped extends \Magento\Catalog\Model\Product\Type\AbstractType
      */
     public function beforeSave($product)
     {
-        if ($product->hasData('product_options')) {
+        if ($product->hasData('product_options') && !empty($product->getData('product_options'))) {
             throw new \Exception('Custom options for grouped product type are not supported');
         }
         return parent::beforeSave($product);
diff --git a/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php b/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..04d7f729f8c86ad5a0c85435bfa266584cf3fbb1
--- /dev/null
+++ b/app/code/Magento/GroupedProduct/Test/Unit/Model/ProductTest.php
@@ -0,0 +1,430 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+// @codingStandardsIgnoreFile
+
+namespace Magento\GroupedProduct\Test\Unit\Model;
+
+use \Magento\Catalog\Model\Product;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+
+/**
+ * Product Test
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @SuppressWarnings(PHPMD.TooManyFields)
+ *
+ */
+class ProductTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ObjectManagerHelper
+     */
+    protected $objectManagerHelper;
+
+    /**
+     * @var \Magento\Catalog\Model\Product
+     */
+    protected $model;
+
+    /**
+     * @var \Magento\Framework\Module\Manager|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $moduleManager;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $stockItemFactoryMock;
+
+    /**
+     * @var \Magento\Indexer\Model\IndexerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $categoryIndexerMock;
+
+    /**
+     * @var \Magento\Catalog\Model\Indexer\Product\Flat\Processor|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productFlatProcessor;
+
+    /**
+     * @var \Magento\Catalog\Model\Indexer\Product\Price\Processor|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productPriceProcessor;
+
+    /**
+     * @var Product\Type|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productTypeInstanceMock;
+
+    /**
+     * @var Product\Option|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $optionInstanceMock;
+
+    /**
+     * @var \Magento\Framework\Pricing\PriceInfo\Base|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $_priceInfoMock;
+
+    /**
+     * @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $store;
+
+    /**
+     * @var \Magento\Catalog\Model\Resource\Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $resource;
+
+    /**
+     * @var \Magento\Framework\Registry|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $registry;
+
+    /**
+     * @var \Magento\Catalog\Model\Category|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $category;
+
+    /**
+     * @var \Magento\Store\Model\Website|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $website;
+
+    /**
+     * @var \Magento\Indexer\Model\IndexerRegistry|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $indexerRegistryMock;
+
+    /**
+     * @var \Magento\Catalog\Api\CategoryRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $categoryRepository;
+
+    /**
+     * @var \Magento\Catalog\Helper\Product|\PHPUnit_Framework_MockObject_MockObject
+     */
+    private $_catalogProduct;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Image\Cache|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $imageCache;
+
+    /**
+     * @var \Magento\Catalog\Model\Product\Image\CacheFactory|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $imageCacheFactory;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $mediaGalleryEntryFactoryMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $productLinkFactory;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $dataObjectHelperMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $metadataServiceMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $attributeValueFactory;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $linkTypeProviderMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $entityCollectionProviderMock;
+
+    /**
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    public function setUp()
+    {
+        $this->categoryIndexerMock = $this->getMockForAbstractClass('\Magento\Indexer\Model\IndexerInterface');
+
+        $this->moduleManager = $this->getMock(
+            'Magento\Framework\Module\Manager',
+            ['isEnabled'],
+            [],
+            '',
+            false
+        );
+        $this->stockItemFactoryMock = $this->getMock(
+            'Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory',
+            ['create'],
+            [],
+            '',
+            false
+        );
+        $this->dataObjectHelperMock = $this->getMockBuilder('\Magento\Framework\Api\DataObjectHelper')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->productFlatProcessor = $this->getMock(
+            'Magento\Catalog\Model\Indexer\Product\Flat\Processor',
+            [],
+            [],
+            '',
+            false
+        );
+
+        $this->_priceInfoMock = $this->getMock('Magento\Framework\Pricing\PriceInfo\Base', [], [], '', false);
+        $this->productTypeInstanceMock = $this->getMock('Magento\Catalog\Model\Product\Type', [], [], '', false);
+        $this->productPriceProcessor = $this->getMock(
+            'Magento\Catalog\Model\Indexer\Product\Price\Processor',
+            [],
+            [],
+            '',
+            false
+        );
+
+        $stateMock = $this->getMock('Magento\FrameworkApp\State', ['getAreaCode'], [], '', false);
+        $stateMock->expects($this->any())
+            ->method('getAreaCode')
+            ->will($this->returnValue(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE));
+
+        $eventManagerMock = $this->getMock('Magento\Framework\Event\ManagerInterface');
+        $actionValidatorMock = $this->getMock(
+            '\Magento\Framework\Model\ActionValidator\RemoveAction',
+            [],
+            [],
+            '',
+            false
+        );
+        $actionValidatorMock->expects($this->any())->method('isAllowed')->will($this->returnValue(true));
+        $cacheInterfaceMock = $this->getMock('Magento\Framework\App\CacheInterface');
+
+        $contextMock = $this->getMock(
+            '\Magento\Framework\Model\Context',
+            ['getEventDispatcher', 'getCacheManager', 'getAppState', 'getActionValidator'], [], '', false
+        );
+        $contextMock->expects($this->any())->method('getAppState')->will($this->returnValue($stateMock));
+        $contextMock->expects($this->any())->method('getEventDispatcher')->will($this->returnValue($eventManagerMock));
+        $contextMock->expects($this->any())
+            ->method('getCacheManager')
+            ->will($this->returnValue($cacheInterfaceMock));
+        $contextMock->expects($this->any())
+            ->method('getActionValidator')
+            ->will($this->returnValue($actionValidatorMock));
+
+        $this->optionInstanceMock = $this->getMockBuilder('Magento\Catalog\Model\Product\Option')
+            ->setMethods(['setProduct', 'saveOptions', '__wakeup', '__sleep'])
+            ->disableOriginalConstructor()->getMock();
+
+        $this->resource = $this->getMockBuilder('Magento\Catalog\Model\Resource\Product')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->registry = $this->getMockBuilder('Magento\Framework\Registry')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->category = $this->getMockBuilder('Magento\Catalog\Model\Category')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->store = $this->getMockBuilder('Magento\Store\Model\Store')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->website = $this->getMockBuilder('\Magento\Store\Model\Website')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $storeManager = $this->getMockBuilder('Magento\Store\Model\StoreManagerInterface')
+            ->disableOriginalConstructor()
+            ->getMockForAbstractClass();
+        $storeManager->expects($this->any())
+            ->method('getStore')
+            ->will($this->returnValue($this->store));
+        $storeManager->expects($this->any())
+            ->method('getWebsite')
+            ->will($this->returnValue($this->website));
+        $this->indexerRegistryMock = $this->getMock('Magento\Indexer\Model\IndexerRegistry', ['get'], [], '', false);
+        $this->categoryRepository = $this->getMock('Magento\Catalog\Api\CategoryRepositoryInterface');
+
+        $this->_catalogProduct = $this->getMock(
+            'Magento\Catalog\Helper\Product',
+            ['isDataForProductCategoryIndexerWasChanged'],
+            [],
+            '',
+            false
+        );
+
+        $this->imageCache = $this->getMockBuilder('Magento\Catalog\Model\Product\Image\Cache')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->imageCacheFactory = $this->getMockBuilder('Magento\Catalog\Model\Product\Image\CacheFactory')
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->productLinkFactory = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductLinkInterfaceFactory')
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->mediaGalleryEntryFactoryMock =
+            $this->getMockBuilder('Magento\Catalog\Api\Data\ProductAttributeMediaGalleryEntryInterfaceFactory')
+                ->setMethods(['create'])
+                ->disableOriginalConstructor()
+                ->getMock();
+
+        $this->metadataServiceMock = $this->getMock('\Magento\Catalog\Api\ProductAttributeRepositoryInterface');
+        $this->attributeValueFactory = $this->getMockBuilder('Magento\Framework\Api\AttributeValueFactory')
+            ->disableOriginalConstructor()->getMock();
+        $this->linkTypeProviderMock = $this->getMock('Magento\Catalog\Model\Product\LinkTypeProvider',
+            ['getLinkTypes'], [], '', false);
+        $this->entityCollectionProviderMock = $this->getMock('Magento\Catalog\Model\ProductLink\CollectionProvider',
+            ['getCollection'], [], '', false);
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->model = $this->objectManagerHelper->getObject(
+            'Magento\Catalog\Model\Product',
+            [
+                'context' => $contextMock,
+                'catalogProductType' => $this->productTypeInstanceMock,
+                'productFlatIndexerProcessor' => $this->productFlatProcessor,
+                'productPriceIndexerProcessor' => $this->productPriceProcessor,
+                'catalogProductOption' => $this->optionInstanceMock,
+                'storeManager' => $storeManager,
+                'resource' => $this->resource,
+                'registry' => $this->registry,
+                'moduleManager' => $this->moduleManager,
+                'stockItemFactory' => $this->stockItemFactoryMock,
+                'dataObjectHelper' => $this->dataObjectHelperMock,
+                'indexerRegistry' => $this->indexerRegistryMock,
+                'categoryRepository' => $this->categoryRepository,
+                'catalogProduct' => $this->_catalogProduct,
+                'imageCacheFactory' => $this->imageCacheFactory,
+                'productLinkFactory' => $this->productLinkFactory,
+                'mediaGalleryEntryFactory' => $this->mediaGalleryEntryFactoryMock,
+                'metadataService' => $this->metadataServiceMock,
+                'customAttributeFactory' => $this->attributeValueFactory,
+                'entityCollectionProvider' => $this->entityCollectionProviderMock,
+                'linkTypeProvider' => $this->linkTypeProviderMock,
+                'data' => ['id' => 1]
+            ]
+        );
+
+    }
+
+    /**
+     *  Test for getProductLinks() with associated product links
+     */
+    public function testGetProductLinks()
+    {
+        $this->markTestIncomplete('Skipped due to https://jira.corp.x.com/browse/MAGETWO-36926');
+        $linkTypes = ['related' => 1, 'upsell' => 4, 'crosssell' => 5, 'associated' => 3];
+        $this->linkTypeProviderMock->expects($this->once())
+            ->method('getLinkTypes')
+            ->willReturn($linkTypes);
+
+        $inputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputRelatedLink->setProductSku("Simple Product 1");
+        $inputRelatedLink->setLinkType("related");
+        $inputRelatedLink->setData("sku", "Simple Product 2");
+        $inputRelatedLink->setData("type", "simple");
+        $inputRelatedLink->setPosition(0);
+
+        $customData = ["attribute_code" => "qty", "value" => 1];
+        $inputGroupLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $inputGroupLink->setProductSku("Simple Product 1");
+        $inputGroupLink->setLinkType("associated");
+        $inputGroupLink->setData("sku", "Simple Product 2");
+        $inputGroupLink->setData("type", "simple");
+        $inputGroupLink->setPosition(0);
+        $inputGroupLink["custom_attributes"] = [$customData];
+
+        $outputRelatedLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $outputRelatedLink->setProductSku("Simple Product 1");
+        $outputRelatedLink->setLinkType("related");
+        $outputRelatedLink->setLinkedProductSku("Simple Product 2");
+        $outputRelatedLink->setLinkedProductType("simple");
+        $outputRelatedLink->setPosition(0);
+
+        $groupExtension = $this->objectManagerHelper->getObject('Magento\Catalog\Api\Data\ProductLinkExtension');
+        $reflectionOfExtension = new \ReflectionClass('Magento\Catalog\Api\Data\ProductLinkExtension');
+        $method = $reflectionOfExtension->getMethod('setData');
+        $method->setAccessible(true);
+        $method->invokeArgs($groupExtension, array('qty', 1));
+
+        $outputGroupLink = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $outputGroupLink->setProductSku("Simple Product 1");
+        $outputGroupLink->setLinkType("associated");
+        $outputGroupLink->setLinkedProductSku("Simple Product 2");
+        $outputGroupLink->setLinkedProductType("simple");
+        $outputGroupLink->setPosition(0);
+        $outputGroupLink->setExtensionAttributes($groupExtension);
+
+        $this->entityCollectionProviderMock->expects($this->at(0))
+            ->method('getCollection')
+            ->with($this->model, 'related')
+            ->willReturn([$inputRelatedLink]);
+        $this->entityCollectionProviderMock->expects($this->at(1))
+            ->method('getCollection')
+            ->with($this->model, 'upsell')
+            ->willReturn([]);
+        $this->entityCollectionProviderMock->expects($this->at(2))
+            ->method('getCollection')
+            ->with($this->model, 'crosssell')
+            ->willReturn([]);
+        $this->entityCollectionProviderMock->expects($this->at(3))
+            ->method('getCollection')
+            ->with($this->model, 'associated')
+            ->willReturn([$inputGroupLink]);
+
+        $expectedOutput = [$outputRelatedLink, $outputGroupLink];
+        $typeInstanceMock = $this->getMock(
+            'Magento\ConfigurableProduct\Model\Product\Type\Simple', ["getSku"], [], '', false);
+        $typeInstanceMock
+            ->expects($this->atLeastOnce())
+            ->method('getSku')
+            ->willReturn("Simple Product 1");
+        $this->model->setTypeInstance($typeInstanceMock);
+
+        $productLink1 = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $productLink2 = $this->objectManagerHelper->getObject('Magento\Catalog\Model\ProductLink\Link');
+        $this->productLinkFactory->expects($this->at(0))
+            ->method('create')
+            ->willReturn($productLink1);
+        $this->productLinkFactory->expects($this->at(1))
+            ->method('create')
+            ->willReturn($productLink2);
+
+        $extension = $this->objectManagerHelper->getObject('Magento\Catalog\Api\Data\ProductLinkExtension');
+        $productLink2->setExtensionAttributes($extension);
+
+        $links = $this->model->getProductLinks();
+        // Match the links
+        $matches = 0;
+        foreach ($links as $link) {
+            foreach ($expectedOutput as $expected) {
+                if ($expected->getData() == $link->getData()) {
+                    $matches++;
+                }
+            }
+        }
+        $this->assertEquals($matches, 2);
+    }
+}
diff --git a/app/code/Magento/GroupedProduct/etc/data_object.xml b/app/code/Magento/GroupedProduct/etc/service_data_attributes.xml
similarity index 66%
rename from app/code/Magento/GroupedProduct/etc/data_object.xml
rename to app/code/Magento/GroupedProduct/etc/service_data_attributes.xml
index d6a76ebaa081f2e742c78e66d70dc504c460bdde..0618cc9286cfd8ccb0cab46f6a505b05a39c4ee2 100644
--- a/app/code/Magento/GroupedProduct/etc/data_object.xml
+++ b/app/code/Magento/GroupedProduct/etc/service_data_attributes.xml
@@ -5,8 +5,8 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Catalog\Api\Data\ProductLinkInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductLinkInterface">
         <attribute code="qty" type="float" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/app/code/Magento/Newsletter/Model/Session.php b/app/code/Magento/Newsletter/Model/Session.php
index 18d680df142601fb39eab95181cee59a79cca4cb..2ffb0b2f25236066922a7510267f10135c1a518f 100644
--- a/app/code/Magento/Newsletter/Model/Session.php
+++ b/app/code/Magento/Newsletter/Model/Session.php
@@ -8,7 +8,7 @@ namespace Magento\Newsletter\Model;
 /**
  * Newsletter session model
  */
-class Session extends \Magento\Framework\Session\Generic
+class Session extends \Magento\Framework\Session\SessionManager
 {
     /**
      * Set error message
diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php
index f2885610281947d30f55abdccb526e6a270c6ac4..5fa2c3aa052638f3841cefcc5ddfe6bf1ef4358a 100644
--- a/app/code/Magento/Newsletter/Model/Subscriber.php
+++ b/app/code/Magento/Newsletter/Model/Subscriber.php
@@ -442,7 +442,6 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel
         $this->setStatusChanged(true);
 
         try {
-            $this->save();
             if ($isConfirmNeed === true
                 && $isOwnSubscribes === false
             ) {
@@ -450,6 +449,7 @@ class Subscriber extends \Magento\Framework\Model\AbstractModel
             } else {
                 $this->sendConfirmationSuccessEmail();
             }
+            $this->save();
             return $this->getStatus();
         } catch (\Exception $e) {
             throw new \Exception($e->getMessage());
diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd8380299a52ee06f8d95a9ddea229ae2649160e
--- /dev/null
+++ b/app/code/Magento/Newsletter/Test/Unit/Model/SubscriberTest.php
@@ -0,0 +1,164 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Newsletter\Test\Unit\Model;
+
+class SubscriberTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Newsletter\Helper\Data|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $newsletterData;
+
+    /**
+     * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $scopeConfig;
+
+    /**
+     * @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $transportBuilder;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManager;
+
+    /**
+     * @var \Magento\Customer\Model\Session|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $customerSession;
+
+    /**
+     * @var \Magento\Customer\Api\CustomerRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $customerRepository;
+
+    /**
+     * @var \Magento\Customer\Api\AccountManagementInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $customerAccountManagement;
+
+    /**
+     * @var \Magento\Framework\Translate\Inline\StateInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $inlineTranslation;
+
+    /**
+     * @var \Magento\Newsletter\Model\Resource\Subscriber|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $resource;
+
+    /**
+     * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
+     */
+    protected $objectManager;
+
+    /**
+     * @var \Magento\Newsletter\Model\Subscriber
+     */
+    protected $subscriber;
+
+    public function setUp()
+    {
+        $this->newsletterData = $this->getMock('Magento\Newsletter\Helper\Data', [], [], '', false);
+        $this->scopeConfig = $this->getMock('Magento\Framework\App\Config\ScopeConfigInterface');
+        $this->transportBuilder = $this->getMock(
+            'Magento\Framework\Mail\Template\TransportBuilder',
+            [
+                'setTemplateIdentifier',
+                'setTemplateOptions',
+                'setTemplateVars',
+                'setFrom',
+                'addTo',
+                'getTransport'
+            ],
+            [],
+            '',
+            false
+        );
+        $this->storeManager = $this->getMock('Magento\Store\Model\StoreManagerInterface');
+        $this->customerSession = $this->getMock(
+            'Magento\Customer\Model\Session',
+            [
+                'isLoggedIn',
+                'getCustomerDataObject',
+                'getCustomerId'
+            ],
+            [],
+            '',
+            false
+        );
+        $this->customerRepository = $this->getMock('Magento\Customer\Api\CustomerRepositoryInterface');
+        $this->customerAccountManagement = $this->getMock('Magento\Customer\Api\AccountManagementInterface');
+        $this->inlineTranslation = $this->getMock('Magento\Framework\Translate\Inline\StateInterface');
+        $this->resource = $this->getMock(
+            'Magento\Newsletter\Model\Resource\Subscriber',
+            [
+                'loadByEmail',
+                'getIdFieldName',
+                'save'
+            ],
+            [],
+            '',
+            false
+        );
+        $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+        $this->subscriber = $this->objectManager->getObject(
+            'Magento\Newsletter\Model\Subscriber',
+            [
+                'newsletterData' => $this->newsletterData,
+                'scopeConfig' => $this->scopeConfig,
+                'transportBuilder' => $this->transportBuilder,
+                'storeManager' => $this->storeManager,
+                'customerSession' => $this->customerSession,
+                'customerRepository' => $this->customerRepository,
+                'customerAccountManagement' => $this->customerAccountManagement,
+                'inlineTranslation' => $this->inlineTranslation,
+                'resource' => $this->resource
+            ]
+        );
+    }
+
+    public function testSubscribe()
+    {
+        $email = 'subscriber_email@magento.com';
+        $this->resource->expects($this->any())->method('loadByEmail')->willReturn(
+            [
+                'subscriber_status' => 3,
+                'subscriber_email' => $email,
+                'name' => 'subscriber_name'
+            ]
+        );
+        $this->resource->expects($this->any())->method('getIdFieldName')->willReturn('id_field');
+        $this->scopeConfig->expects($this->any())->method('getValue')->willReturn(true);
+        $this->customerSession->expects($this->any())->method('isLoggedIn')->willReturn(true);
+        $customerDataModel = $this->getMock('\Magento\Customer\Api\Data\CustomerInterface');
+        $this->customerSession->expects($this->any())->method('getCustomerDataObject')->willReturn($customerDataModel);
+        $this->customerSession->expects($this->any())->method('getCustomerId')->willReturn(1);
+        $customerDataModel->expects($this->any())->method('getEmail')->willReturn($email);
+        $this->customerRepository->expects($this->any())->method('getById')->willReturn($customerDataModel);
+        $customerDataModel->expects($this->any())->method('getStoreId')->willReturn(1);
+        $customerDataModel->expects($this->any())->method('getId')->willReturn(1);
+        $this->transportBuilder->expects($this->any())->method('setTemplateIdentifier')->willReturnSelf();
+        $this->transportBuilder->expects($this->any())->method('setTemplateOptions')->willReturnSelf();
+        $this->transportBuilder->expects($this->any())->method('setTemplateVars')->willReturnSelf();
+        $this->transportBuilder->expects($this->any())->method('setFrom')->willReturnSelf();
+        $this->transportBuilder->expects($this->any())->method('addTo')->willReturnSelf();
+        $storeModel = $this->getMock('\Magento\Store\Model\Store', ['getId'], [], '', false);
+        $this->scopeConfig->expects($this->any())->method('getValue')->willReturn('owner_email@magento.com');
+        $this->storeManager->expects($this->any())->method('getStore')->willReturn($storeModel);
+        $storeModel->expects($this->any())->method('getId')->willReturn(1);
+        $transport = $this->getMock('\Magento\Framework\Mail\TransportInterface');
+        $this->transportBuilder->expects($this->any())->method('getTransport')->willReturn($transport);
+        $transport->expects($this->any())->method('sendMessage')->willReturnSelf();
+        $inlineTranslation = $this->getMock('Magento\Framework\Translate\Inline\StateInterface');
+        $inlineTranslation->expects($this->any())->method('resume')->willReturnSelf();
+        $this->resource->expects($this->atLeastOnce())->method('save')->willReturnSelf();
+        $this->assertEquals(1, $this->subscriber->subscribe($email));
+    }
+}
diff --git a/app/code/Magento/Quote/etc/di.xml b/app/code/Magento/Quote/etc/di.xml
index 4eeb1ff2b61a7e865d24c28af7411c599b7c3f05..0b46fc59fb7dcbead0e93d40563787ce7f6af5b7 100644
--- a/app/code/Magento/Quote/etc/di.xml
+++ b/app/code/Magento/Quote/etc/di.xml
@@ -25,7 +25,6 @@
     <preference for="Magento\Quote\Api\Data\TotalsInterface" type="\Magento\Quote\Model\Cart\Totals" />
     <preference for="Magento\Quote\Api\Data\TotalsItemInterface" type="\Magento\Quote\Model\Quote\Cart\Totals\Item" />
     <preference for="Magento\Quote\Api\Data\CurrencyInterface" type="\Magento\Quote\Model\Cart\Currency" />
-
     <preference for="Magento\Quote\Api\GuestCartManagementInterface" type="Magento\Quote\Model\GuestCart\GuestCartManagement" />
     <preference for="Magento\Quote\Api\GuestCartRepositoryInterface" type="Magento\Quote\Model\GuestCart\GuestCartRepository" />
     <preference for="Magento\Quote\Api\GuestCartItemRepositoryInterface" type="Magento\Quote\Model\GuestCart\GuestCartItemRepository" />
@@ -35,7 +34,6 @@
     <preference for="Magento\Quote\Api\GuestShippingAddressManagementInterface" type="Magento\Quote\Model\GuestCart\GuestShippingAddressManagement" />
     <preference for="Magento\Quote\Api\GuestShippingMethodManagementInterface" type="Magento\Quote\Model\GuestCart\GuestShippingMethodManagement" />
     <preference for="Magento\Quote\Api\GuestBillingAddressManagementInterface" type="Magento\Quote\Model\GuestCart\GuestBillingAddressManagement" />
-
     <type name="Magento\Webapi\Controller\Rest\ParamsOverrider">
         <arguments>
             <argument name="paramOverriders" xsi:type="array">
@@ -43,4 +41,14 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Quote\Model\QuoteRepository">
+        <arguments>
+            <argument name="quoteCollection" xsi:type="object">Magento\Quote\Model\Resource\Quote\Collection\Proxy</argument>
+        </arguments>
+    </type>
+    <type name="Magento\Quote\Model\Quote\Address">
+        <arguments>
+            <argument name="addressConfig" xsi:type="object">Magento\Customer\Model\Address\Config\Proxy</argument>
+        </arguments>
+    </type>
 </config>
diff --git a/app/code/Magento/Review/Block/Form.php b/app/code/Magento/Review/Block/Form.php
index 9682f444c4100126155f6daa0ecde88b656ce156..cf506a09857d50ca75e60af14307933802b9678b 100644
--- a/app/code/Magento/Review/Block/Form.php
+++ b/app/code/Magento/Review/Block/Form.php
@@ -163,7 +163,11 @@ class Form extends \Magento\Framework\View\Element\Template
      */
     public function getProductInfo()
     {
-        return $this->productRepository->getById($this->getProductId());
+        return $this->productRepository->getById(
+            $this->getProductId(),
+            false,
+            $this->_storeManager->getStore()->getId()
+        );
     }
 
     /**
diff --git a/app/code/Magento/Review/Model/Resource/Review/Collection.php b/app/code/Magento/Review/Model/Resource/Review/Collection.php
index 7bd2de244b7adb3bdff739d5782cb681e832c41b..d32a6b04ac03043b9fc8a2c596ece7d84af58eaf 100644
--- a/app/code/Magento/Review/Model/Resource/Review/Collection.php
+++ b/app/code/Magento/Review/Model/Resource/Review/Collection.php
@@ -17,35 +17,35 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
      *
      * @var string
      */
-    protected $_reviewTable;
+    protected $_reviewTable = null;
 
     /**
      * Review detail table
      *
      * @var string
      */
-    protected $_reviewDetailTable;
+    protected $_reviewDetailTable = null;
 
     /**
      * Review status table
      *
      * @var string
      */
-    protected $_reviewStatusTable;
+    protected $_reviewStatusTable = null;
 
     /**
      * Review entity table
      *
      * @var string
      */
-    protected $_reviewEntityTable;
+    protected $_reviewEntityTable = null;
 
     /**
      * Review store table
      *
      * @var string
      */
-    protected $_reviewStoreTable;
+    protected $_reviewStoreTable = null;
 
     /**
      * Add store data flag
@@ -111,11 +111,6 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
     protected function _construct()
     {
         $this->_init('Magento\Review\Model\Review', 'Magento\Review\Model\Resource\Review');
-        $this->_reviewTable = $this->getTable('review');
-        $this->_reviewDetailTable = $this->getTable('review_detail');
-        $this->_reviewStatusTable = $this->getTable('review_status');
-        $this->_reviewEntityTable = $this->getTable('review_entity');
-        $this->_reviewStoreTable = $this->getTable('review_store');
     }
 
     /**
@@ -127,7 +122,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
     {
         parent::_initSelect();
         $this->getSelect()->join(
-            ['detail' => $this->_reviewDetailTable],
+            ['detail' => $this->getReviewDetailTable()],
             'main_table.review_id = detail.review_id',
             ['detail_id', 'title', 'detail', 'nickname', 'customer_id']
         );
@@ -156,7 +151,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
     {
         $inCond = $this->getConnection()->prepareSqlCondition('store.store_id', ['in' => $storeId]);
         $this->getSelect()->join(
-            ['store' => $this->_reviewStoreTable],
+            ['store' => $this->getReviewStoreTable()],
             'main_table.review_id=store.review_id',
             []
         );
@@ -184,18 +179,19 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
      */
     public function addEntityFilter($entity, $pkValue)
     {
+        $reviewEntityTable = $this->getReviewEntityTable();
         if (is_numeric($entity)) {
             $this->addFilter('entity', $this->getConnection()->quoteInto('main_table.entity_id=?', $entity), 'string');
         } elseif (is_string($entity)) {
             $this->_select->join(
-                $this->_reviewEntityTable,
-                'main_table.entity_id=' . $this->_reviewEntityTable . '.entity_id',
+                $reviewEntityTable,
+                'main_table.entity_id=' . $reviewEntityTable . '.entity_id',
                 ['entity_code']
             );
 
             $this->addFilter(
                 'entity',
-                $this->getConnection()->quoteInto($this->_reviewEntityTable . '.entity_code=?', $entity),
+                $this->getConnection()->quoteInto($reviewEntityTable . '.entity_code=?', $entity),
                 'string'
             );
         }
@@ -268,7 +264,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
     public function addReviewsTotalCount()
     {
         $this->_select->joinLeft(
-            ['r' => $this->_reviewTable],
+            ['r' => $this->getReviewTable()],
             'main_table.entity_pk_value = r.entity_pk_value',
             ['total_reviews' => new \Zend_Db_Expr('COUNT(r.review_id)')]
         )->group(
@@ -311,7 +307,7 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
         $storesToReviews = [];
         if (count($reviewsIds) > 0) {
             $inCond = $adapter->prepareSqlCondition('review_id', ['in' => $reviewsIds]);
-            $select = $adapter->select()->from($this->_reviewStoreTable)->where($inCond);
+            $select = $adapter->select()->from($this->getReviewStoreTable())->where($inCond);
             $result = $adapter->fetchAll($select);
             foreach ($result as $row) {
                 if (!isset($storesToReviews[$row['review_id']])) {
@@ -329,4 +325,69 @@ class Collection extends \Magento\Framework\Model\Resource\Db\Collection\Abstrac
             }
         }
     }
+
+    /**
+     * Get review table
+     *
+     * @return string
+     */
+    protected function getReviewTable()
+    {
+        if ($this->_reviewTable === null) {
+            $this->_reviewTable = $this->getTable('review');
+        }
+        return $this->_reviewTable;
+    }
+
+    /**
+     * Get review detail table
+     *
+     * @return string
+     */
+    protected function getReviewDetailTable()
+    {
+        if ($this->_reviewDetailTable === null) {
+            $this->_reviewDetailTable = $this->getTable('review_detail');
+        }
+        return $this->_reviewDetailTable;
+    }
+
+    /**
+     * Get review status table
+     *
+     * @return string
+     */
+    protected function getReviewStatusTable()
+    {
+        if ($this->_reviewStatusTable === null) {
+            $this->_reviewStatusTable = $this->getTable('review_status');
+        }
+        return $this->_reviewStatusTable;
+    }
+
+    /**
+     * Get review entity table
+     *
+     * @return string
+     */
+    protected function getReviewEntityTable()
+    {
+        if ($this->_reviewEntityTable === null) {
+            $this->_reviewEntityTable = $this->getTable('review_entity');
+        }
+        return $this->_reviewEntityTable;
+    }
+
+    /**
+     * Get review store table
+     *
+     * @return string
+     */
+    protected function getReviewStoreTable()
+    {
+        if ($this->_reviewStoreTable === null) {
+            $this->_reviewStoreTable = $this->getTable('review_store');
+        }
+        return $this->_reviewStoreTable;
+    }
 }
diff --git a/app/code/Magento/Review/Test/Unit/Block/FormTest.php b/app/code/Magento/Review/Test/Unit/Block/FormTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..32a59cf8334c59766d49955916821c587a1bb32f
--- /dev/null
+++ b/app/code/Magento/Review/Test/Unit/Block/FormTest.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Review\Test\Unit\Block;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper;
+
+class FormTest extends \PHPUnit_Framework_TestCase
+{
+    /** @var \Magento\Review\Block\Form */
+    protected $object;
+
+    /** @var ObjectManagerHelper */
+    protected $objectManagerHelper;
+
+    /**
+     * @var \Magento\Framework\App\RequestInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $requestMock;
+
+    /** @var \Magento\Framework\View\Element\Template\Context|\PHPUnit_Framework_MockObject_MockObject */
+    protected $context;
+
+    /**
+     * @var \Magento\Review\Helper\Data|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $reviewDataMock;
+
+    /** @var \Magento\Catalog\Api\ProductRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject */
+    protected $productRepository;
+
+    /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */
+    protected $storeManager;
+
+    protected function setUp()
+    {
+        $this->storeManager = $this->getMock('\Magento\Store\Model\StoreManagerInterface');
+        $this->requestMock = $this->getMock('\Magento\Framework\App\RequestInterface');
+        $this->reviewDataMock = $this->getMockBuilder('\Magento\Review\Helper\Data')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->reviewDataMock->expects($this->once())
+            ->method('getIsGuestAllowToWrite')
+            ->willReturn(true);
+
+        $this->context = $this->getMock('Magento\Framework\View\Element\Template\Context', [], [], '', false);
+        $this->context->expects(
+            $this->any()
+        )->method(
+            'getStoreManager'
+        )->will(
+            $this->returnValue($this->storeManager)
+        );
+        $this->context->expects($this->any())
+            ->method('getRequest')
+            ->willReturn($this->requestMock);
+        $this->productRepository = $this->getMock('\Magento\Catalog\Api\ProductRepositoryInterface');
+
+        $this->objectManagerHelper = new ObjectManagerHelper($this);
+        $this->object = $this->objectManagerHelper->getObject(
+            'Magento\Review\Block\Form',
+            [
+                'context' => $this->context,
+                'reviewData' => $this->reviewDataMock,
+                'productRepository' => $this->productRepository,
+            ]
+        );
+    }
+
+    public function testGetProductInfo()
+    {
+        $productId = 3;
+        $storeId = 1;
+
+        $this->storeManager->expects(
+            $this->any()
+        )->method(
+            'getStore'
+        )->will(
+            $this->returnValue(new \Magento\Framework\Object(['id' => $storeId]))
+        );
+
+        $this->requestMock->expects($this->once())
+            ->method('getParam')
+            ->with('id', false)
+            ->willReturn($productId);
+
+        $productMock = $this->getMock('Magento\Catalog\Api\Data\ProductInterface');
+        $this->productRepository->expects($this->once())
+            ->method('getById')
+            ->with($productId, false, $storeId)
+            ->willReturn($productMock);
+
+        $this->assertSame($productMock, $this->object->getProductInfo());
+    }
+}
diff --git a/app/code/Magento/Review/Test/Unit/Model/Resource/Review/CollectionTest.php b/app/code/Magento/Review/Test/Unit/Model/Resource/Review/CollectionTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..67e67cdf8381745aa356f7b85129945f2d563d33
--- /dev/null
+++ b/app/code/Magento/Review/Test/Unit/Model/Resource/Review/CollectionTest.php
@@ -0,0 +1,188 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Review\Test\Unit\Model\Resource\Review;
+
+class CollectionTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Review\Model\Resource\Review\Collection
+     */
+    protected $model;
+
+    /**
+     * @var \Zend_Db_Select | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $selectMock;
+
+    /**
+     * @var \Magento\Store\Model\StoreManagerInterface | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $storeManagerMock;
+
+    /**
+     * @var \Magento\Framework\Model\Resource\Db\AbstractDb | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $resourceMock;
+
+    /**
+     * @var \Zend_Db_Adapter_Abstract | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $readerAdapterMock;
+
+    /**
+     * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
+     */
+    protected $objectManager;
+
+    public function setUp()
+    {
+        $store = $this->getMock('\Magento\Store\Model\Store', ['getId'], [], '', false);
+        $store->expects($this->any())->method('getId')->will($this->returnValue(1));
+        $this->storeManagerMock = $this->getMock('Magento\Store\Model\StoreManagerInterface');
+        $this->storeManagerMock->expects($this->any())->method('getStore')->will($this->returnValue($store));
+        $this->objectManager = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this));
+        $this->resourceMock = $this->getMockBuilder('Magento\Framework\Model\Resource\Db\AbstractDb')
+            ->disableOriginalConstructor()
+            ->setMethods(['getReadConnection', 'getMainTable', 'getTable'])
+            ->getMockForAbstractClass();
+        $this->readerAdapterMock = $this->getMockBuilder('\Zend_Db_Adapter_Abstract')
+            ->disableOriginalConstructor()
+            ->setMethods(['select', 'prepareSqlCondition', 'quoteInto'])
+            ->getMockForAbstractClass();
+        $this->selectMock = $this->getMockBuilder('\Zend_Db_Select')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->readerAdapterMock->expects($this->any())
+            ->method('select')
+            ->willReturn($this->selectMock);
+        $this->resourceMock->expects($this->any())
+            ->method('getReadConnection')
+            ->willReturn($this->readerAdapterMock);
+        $this->resourceMock->expects($this->any())
+            ->method('getMainTable')
+            ->willReturn('maintable');
+        $this->resourceMock->expects($this->any())
+            ->method('getTable')
+            ->willReturnCallback(function ($table) {
+                return $table;
+            });
+        $this->model = $this->objectManager->getObject(
+            '\Magento\Review\Model\Resource\Review\Collection',
+            [
+                'storeManager' => $this->storeManagerMock,
+                'resource' => $this->resourceMock,
+            ]
+        );
+
+    }
+
+    public function testInitSelect()
+    {
+        $this->selectMock->expects($this->once())
+            ->method('join')
+            ->with(
+                ['detail' => 'review_detail'],
+                'main_table.review_id = detail.review_id',
+                ['detail_id', 'title', 'detail', 'nickname', 'customer_id']
+            );
+        $this->objectManager->getObject(
+            '\Magento\Review\Model\Resource\Review\Collection',
+            [
+                'storeManager' => $this->storeManagerMock,
+                'resource' => $this->resourceMock,
+            ]
+        );
+    }
+
+    public function testAddStoreFilter()
+    {
+        $this->readerAdapterMock->expects($this->once())
+            ->method('prepareSqlCondition');
+        $this->selectMock->expects($this->once())
+            ->method('join')
+            ->with(
+                ['store' => 'review_store'],
+                'main_table.review_id=store.review_id',
+                []
+            );
+        $this->model->addStoreFilter(1);
+    }
+
+    /**
+     * @param int|string $entity
+     * @param int $pkValue
+     * @param string $quoteIntoArguments1
+     * @param string $quoteIntoArguments2
+     * @param string $quoteIntoReturn1
+     * @param string $quoteIntoReturn2
+     * @param int $callNum
+     * @dataProvider addEntityFilterDataProvider
+     */
+    public function testAddEntityFilter(
+        $entity,
+        $pkValue,
+        $quoteIntoArguments1,
+        $quoteIntoArguments2,
+        $quoteIntoReturn1,
+        $quoteIntoReturn2,
+        $callNum
+    ) {
+        $this->readerAdapterMock->expects($this->at(0))
+            ->method('quoteInto')
+            ->with($quoteIntoArguments1[0], $quoteIntoArguments1[1])
+            ->willReturn($quoteIntoReturn1);
+        $this->readerAdapterMock->expects($this->at(1))
+            ->method('quoteInto')
+            ->with($quoteIntoArguments2[0], $quoteIntoArguments2[1])
+            ->willReturn($quoteIntoReturn2);
+        $this->selectMock->expects($this->exactly($callNum))
+            ->method('join')
+            ->with(
+                'review_entity',
+                'main_table.entity_id=' . 'review_entity' . '.entity_id',
+                ['entity_code']
+            );
+        $this->model->addEntityFilter($entity, $pkValue);
+    }
+
+    public function addEntityFilterDataProvider()
+    {
+        return [
+            [
+                1,
+                2,
+                ['main_table.entity_id=?', 1],
+                ['main_table.entity_pk_value=?', 2],
+                'quoteIntoReturn1',
+                'quoteIntoReturn2',
+                0
+            ],
+            [
+                'entity',
+                2,
+                ['review_entity.entity_code=?', 'entity'],
+                ['main_table.entity_pk_value=?', 2],
+                'quoteIntoReturn1',
+                'quoteIntoReturn2',
+                1
+            ]
+        ];
+    }
+
+    public function testAddReviewsTotalCount()
+    {
+        $this->selectMock->expects($this->once())
+            ->method('joinLeft')
+            ->with(
+                ['r' => 'review'],
+                'main_table.entity_pk_value = r.entity_pk_value',
+                ['total_reviews' => new \Zend_Db_Expr('COUNT(r.review_id)')]
+            )->willReturnSelf();
+        $this->selectMock->expects($this->once())
+            ->method('group');
+        $this->model->addReviewsTotalCount();
+    }
+}
diff --git a/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php b/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php
index 28afae3c0f9528343770c22dfe283b82324a7c5a..3a4a5c23033f48a2b2fcc41eb519330cb3e955fd 100644
--- a/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php
+++ b/app/code/Magento/Sales/Api/Data/CreditmemoCommentInterface.php
@@ -57,6 +57,14 @@ interface CreditmemoCommentInterface extends \Magento\Framework\Api\ExtensibleDa
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the credit memo created-at timestamp.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the credit memo ID.
      *
diff --git a/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php b/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php
index 205d5c37b3f1033132c9679a240e036223a2e5e8..ce5a46b9838b447159bcf96b87bbd8753e46ed5e 100644
--- a/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php
+++ b/app/code/Magento/Sales/Api/Data/CreditmemoInterface.php
@@ -383,6 +383,15 @@ interface CreditmemoInterface extends \Magento\Framework\Api\ExtensibleDataInter
      * @return string Credit memo created-at timestamp.
      */
     public function getCreatedAt();
+
+    /**
+     * Sets the credit memo created-at timestamp.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the credit memo status.
      *
diff --git a/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php
index 10c86a870231cd4cd0c24a165c1cc5cf022bcdaf..ec6fce4016f6a0d397858b3ebf5848cacdc3e047 100644
--- a/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php
+++ b/app/code/Magento/Sales/Api/Data/InvoiceCommentInterface.php
@@ -55,6 +55,14 @@ interface InvoiceCommentInterface extends \Magento\Framework\Api\ExtensibleDataI
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the invoice.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the ID for the invoice.
      *
diff --git a/app/code/Magento/Sales/Api/Data/InvoiceInterface.php b/app/code/Magento/Sales/Api/Data/InvoiceInterface.php
index e17decc98e0bbd364313f43a2d7373cbfd7990cc..fcbe20b83f28c96b939277d524a04dec9f923b7d 100644
--- a/app/code/Magento/Sales/Api/Data/InvoiceInterface.php
+++ b/app/code/Magento/Sales/Api/Data/InvoiceInterface.php
@@ -319,6 +319,14 @@ interface InvoiceInterface extends \Magento\Framework\Api\ExtensibleDataInterfac
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the invoice.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the discount amount for the invoice.
      *
diff --git a/app/code/Magento/Sales/Api/Data/OrderInterface.php b/app/code/Magento/Sales/Api/Data/OrderInterface.php
index 7bd62fbe1c3228b2241690b981a7f0ab8c2f84eb..42ccf6aee23c151088f27b7d6e714237a75ce845 100644
--- a/app/code/Magento/Sales/Api/Data/OrderInterface.php
+++ b/app/code/Magento/Sales/Api/Data/OrderInterface.php
@@ -914,6 +914,14 @@ interface OrderInterface extends \Magento\Framework\Api\ExtensibleDataInterface
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the order.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the customer date-of-birth (DOB) for the order.
      *
diff --git a/app/code/Magento/Sales/Api/Data/OrderItemInterface.php b/app/code/Magento/Sales/Api/Data/OrderItemInterface.php
index 6e59b03a98370feb88a6d6522c9fb84560e12bf3..e10e196380670f54fd11963a281738c50226d20c 100644
--- a/app/code/Magento/Sales/Api/Data/OrderItemInterface.php
+++ b/app/code/Magento/Sales/Api/Data/OrderItemInterface.php
@@ -578,6 +578,14 @@ interface OrderItemInterface extends \Magento\Framework\Api\ExtensibleDataInterf
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the order item.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the description for the order item.
      *
diff --git a/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php b/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php
index 4ef7daf322d37f0ca1b26f0ab7501e7ad5a79fcf..3a27e326c3b88866c1cb03ec4c84227391e46416 100644
--- a/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php
+++ b/app/code/Magento/Sales/Api/Data/OrderStatusHistoryInterface.php
@@ -64,6 +64,14 @@ interface OrderStatusHistoryInterface extends \Magento\Framework\Api\ExtensibleD
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the order status history.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the ID for the order status history.
      *
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php
index 213b7c2e5e4931bd9f9295aee3b537f817891a68..e6bcd71928463f0bd09446f60bc303a1a31f4864 100644
--- a/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php
+++ b/app/code/Magento/Sales/Api/Data/ShipmentCommentInterface.php
@@ -55,6 +55,14 @@ interface ShipmentCommentInterface extends \Magento\Framework\Api\ExtensibleData
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the shipment comment.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the ID for the shipment comment.
      *
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentInterface.php
index 41c676ea2081219e35d7e7d6561206f4f5bf5c24..67b3b60b05266ef8febff35e6df0952414ebdea1 100644
--- a/app/code/Magento/Sales/Api/Data/ShipmentInterface.php
+++ b/app/code/Magento/Sales/Api/Data/ShipmentInterface.php
@@ -103,6 +103,14 @@ interface ShipmentInterface extends \Magento\Framework\Api\ExtensibleDataInterfa
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the shipment.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the customer ID for the shipment.
      *
diff --git a/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php b/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php
index 7ace1ead4114db6c8c81c9a3921d56f1e9195727..e9bfa3915c4074096f9d5023e5a5c13514a92267 100644
--- a/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php
+++ b/app/code/Magento/Sales/Api/Data/ShipmentTrackInterface.php
@@ -76,6 +76,14 @@ interface ShipmentTrackInterface extends \Magento\Framework\Api\ExtensibleDataIn
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the shipment package.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets the description for the shipment package.
      *
diff --git a/app/code/Magento/Sales/Api/Data/TransactionInterface.php b/app/code/Magento/Sales/Api/Data/TransactionInterface.php
index 224d9765582f0e3381846f159814103e72111351..92d431c6e0b5754ed8222c5deb9f8fb02f28ce05 100644
--- a/app/code/Magento/Sales/Api/Data/TransactionInterface.php
+++ b/app/code/Magento/Sales/Api/Data/TransactionInterface.php
@@ -75,6 +75,14 @@ interface TransactionInterface extends \Magento\Framework\Api\ExtensibleDataInte
      */
     public function getTransactionId();
 
+    /**
+     * Sets the transaction ID for the transaction.
+     *
+     * @param int $id
+     * @return $this
+     */
+    public function setTransactionId($id);
+
     /**
      * Gets the parent ID for the transaction.
      *
@@ -138,6 +146,14 @@ interface TransactionInterface extends \Magento\Framework\Api\ExtensibleDataInte
      */
     public function getCreatedAt();
 
+    /**
+     * Sets the created-at timestamp for the transaction.
+     *
+     * @param string $createdAt timestamp
+     * @return $this
+     */
+    public function setCreatedAt($createdAt);
+
     /**
      * Gets an array of child transactions for the transaction.
      *
diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/EditInterface.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/EditInterface.php
deleted file mode 100644
index 79fefe2a18515d4f143f561aba1c9a2f9e3f4cde..0000000000000000000000000000000000000000
--- a/app/code/Magento/Sales/Controller/Adminhtml/Order/EditInterface.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-/**
- *
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Sales\Controller\Adminhtml\Order;
-
-interface EditInterface extends \Magento\Framework\App\ActionInterface
-{
-}
diff --git a/app/code/Magento/Sales/Model/Order.php b/app/code/Magento/Sales/Model/Order.php
index 02077b3e30163a4794f260175e74ec667ed17592..43f4b95497720ed8df3e7f74a4cc03e31255b980 100644
--- a/app/code/Magento/Sales/Model/Order.php
+++ b/app/code/Magento/Sales/Model/Order.php
@@ -35,7 +35,6 @@ use Magento\Sales\Model\Resource\Order\Status\History\Collection as HistoryColle
  * @method \Magento\Sales\Model\Resource\Order getResource()
  * @method int getGiftMessageId()
  * @method \Magento\Sales\Model\Order setGiftMessageId(int $value)
- * @method \Magento\Sales\Model\Order setCreatedAt(string $value)
  * @method bool hasBillingAddressId()
  * @method \Magento\Sales\Model\Order unsBillingAddressId()
  * @method bool hasShippingAddressId()
@@ -2481,6 +2480,14 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
         return $this->getData(OrderInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(OrderInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns customer_dob
      *
diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo.php b/app/code/Magento/Sales/Model/Order/Creditmemo.php
index 9e77f5afd8635e1b6421a1915684a4c0a858cbd0..7fbc58d77171b57ccf708e81a9a4ad77a827293b 100644
--- a/app/code/Magento/Sales/Model/Order/Creditmemo.php
+++ b/app/code/Magento/Sales/Model/Order/Creditmemo.php
@@ -20,7 +20,6 @@ use Magento\Sales\Model\EntityInterface;
  *
  * @method \Magento\Sales\Model\Resource\Order\Creditmemo _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Creditmemo getResource()
- * @method \Magento\Sales\Model\Order\Creditmemo setCreatedAt(string $value)
  * @method \Magento\Sales\Model\Order\Invoice setSendEmail(bool $value)
  * @method \Magento\Sales\Model\Order\Invoice setCustomerNote(string $value)
  * @method string getCustomerNote()
@@ -1034,6 +1033,14 @@ class Creditmemo extends AbstractModel implements EntityInterface, CreditmemoInt
         return $this->getData(CreditmemoInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(CreditmemoInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns creditmemo_status
      *
diff --git a/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php b/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php
index a488162c322611e892d33bc90cd60be5dac7cc6f..2dac94c26b818bc2eab23acc83aea3079166a9d5 100644
--- a/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php
+++ b/app/code/Magento/Sales/Model/Order/Creditmemo/Comment.php
@@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel;
 /**
  * @method \Magento\Sales\Model\Resource\Order\Creditmemo\Comment _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Creditmemo\Comment getResource()
- * @method \Magento\Sales\Model\Order\Creditmemo\Comment setCreatedAt(string $value)
  */
 class Comment extends AbstractModel implements CreditmemoCommentInterface
 {
@@ -126,6 +125,14 @@ class Comment extends AbstractModel implements CreditmemoCommentInterface
         return $this->getData(CreditmemoCommentInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(CreditmemoCommentInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns is_customer_notified
      *
diff --git a/app/code/Magento/Sales/Model/Order/Invoice.php b/app/code/Magento/Sales/Model/Order/Invoice.php
index 8317b7041d61f3e747af2b644050854b3b55174d..036aec685121162933181a05e6c66af57af6d58c 100644
--- a/app/code/Magento/Sales/Model/Order/Invoice.php
+++ b/app/code/Magento/Sales/Model/Order/Invoice.php
@@ -11,7 +11,6 @@ use Magento\Sales\Model\AbstractModel;
 use Magento\Sales\Model\EntityInterface;
 
 /**
- * @method \Magento\Sales\Model\Order\Invoice setCreatedAt(string $value)
  * @method \Magento\Sales\Model\Order\Invoice setSendEmail(bool $value)
  * @method \Magento\Sales\Model\Order\Invoice setCustomerNote(string $value)
  * @method string getCustomerNote()
@@ -969,6 +968,14 @@ class Invoice extends AbstractModel implements EntityInterface, InvoiceInterface
         return $this->getData(InvoiceInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(InvoiceInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns discount_amount
      *
diff --git a/app/code/Magento/Sales/Model/Order/Invoice/Comment.php b/app/code/Magento/Sales/Model/Order/Invoice/Comment.php
index 074e77b3638e656906f3da22e3727cea61423e98..e7b99dea1593ed699d4f76e6f745256f780e8075 100644
--- a/app/code/Magento/Sales/Model/Order/Invoice/Comment.php
+++ b/app/code/Magento/Sales/Model/Order/Invoice/Comment.php
@@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel;
 /**
  * @method \Magento\Sales\Model\Resource\Order\Invoice\Comment _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Invoice\Comment getResource()
- * @method \Magento\Sales\Model\Order\Invoice\Comment setCreatedAt(string $value)
  */
 class Comment extends AbstractModel implements InvoiceCommentInterface
 {
@@ -126,6 +125,14 @@ class Comment extends AbstractModel implements InvoiceCommentInterface
         return $this->getData(InvoiceCommentInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(InvoiceCommentInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns is_customer_notified
      *
diff --git a/app/code/Magento/Sales/Model/Order/Item.php b/app/code/Magento/Sales/Model/Order/Item.php
index b23a34296c690d5584dc2a6e314401669981bd3a..602cde0a32e3ccbbc03195aa4933e4fdb81b3079 100644
--- a/app/code/Magento/Sales/Model/Order/Item.php
+++ b/app/code/Magento/Sales/Model/Order/Item.php
@@ -14,7 +14,6 @@ use Magento\Sales\Api\Data\OrderItemInterface;
  *
  * @method \Magento\Sales\Model\Resource\Order\Item _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Item getResource()
- * @method \Magento\Sales\Model\Order\Item setCreatedAt(string $value)
  * @method int getGiftMessageId()
  * @method \Magento\Sales\Model\Order\Item setGiftMessageId(int $value)
  * @method int getGiftMessageAvailable()
@@ -949,6 +948,14 @@ class Item extends AbstractModel implements OrderItemInterface
         return $this->getData(OrderItemInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(OrderItemInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns description
      *
diff --git a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php
index 3c0d281f3e27cd8630d0471e6e8f505c29f6b10c..16cc9b45365adec1da8225d0c7ef6cd27ba813f1 100644
--- a/app/code/Magento/Sales/Model/Order/Payment/Transaction.php
+++ b/app/code/Magento/Sales/Model/Order/Payment/Transaction.php
@@ -19,7 +19,6 @@ use Magento\Sales\Model\AbstractModel;
  *
  * @method \Magento\Sales\Model\Resource\Order\Payment\Transaction _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Payment\Transaction getResource()
- * @method \Magento\Sales\Model\Order\Payment\Transaction setCreatedAt(string $value)
 
  * @author      Magento Core Team <core@magentocommerce.com>
  * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
@@ -922,6 +921,14 @@ class Transaction extends AbstractModel implements TransactionInterface
         return $this->getData(TransactionInterface::TRANSACTION_ID);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setTransactionId($id)
+    {
+        return $this->setData(TransactionInterface::TRANSACTION_ID, $id);
+    }
+
     /**
      * Returns method
      *
@@ -1002,8 +1009,9 @@ class Transaction extends AbstractModel implements TransactionInterface
         return $this->getData(TransactionInterface::IS_CLOSED);
     }
 
+    //@codeCoverageIgnoreStart
     /**
-     * Returns created_at
+     * Gets the created-at timestamp for the transaction.
      *
      * @return string
      */
@@ -1012,7 +1020,14 @@ class Transaction extends AbstractModel implements TransactionInterface
         return $this->getData(TransactionInterface::CREATED_AT);
     }
 
-    //@codeCoverageIgnoreStart
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(TransactionInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * {@inheritdoc}
      */
diff --git a/app/code/Magento/Sales/Model/Order/Shipment.php b/app/code/Magento/Sales/Model/Order/Shipment.php
index 3472e9663ad337ceb64c7399b06a60e389883708..6763a4895dfe30891530cdeb2c25a61e47047c14 100644
--- a/app/code/Magento/Sales/Model/Order/Shipment.php
+++ b/app/code/Magento/Sales/Model/Order/Shipment.php
@@ -15,7 +15,6 @@ use Magento\Sales\Model\EntityInterface;
  *
  * @method \Magento\Sales\Model\Resource\Order\Shipment _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Shipment getResource()
- * @method \Magento\Sales\Model\Order\Shipment setCreatedAt(string $value)
  * @method \Magento\Sales\Model\Order\Invoice setSendEmail(bool $value)
  * @method \Magento\Sales\Model\Order\Invoice setCustomerNote(string $value)
  * @method string getCustomerNote()
@@ -626,6 +625,14 @@ class Shipment extends AbstractModel implements EntityInterface, ShipmentInterfa
         return $this->getData(ShipmentInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(ShipmentInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns customer_id
      *
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Comment.php b/app/code/Magento/Sales/Model/Order/Shipment/Comment.php
index 43361d79d59c4a4b6e403a7b0c7677ba1dbc1784..d1e9a5ac880bce77a0cef548f077e6e09ce754e7 100644
--- a/app/code/Magento/Sales/Model/Order/Shipment/Comment.php
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Comment.php
@@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel;
 /**
  * @method \Magento\Sales\Model\Resource\Order\Shipment\Comment _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Shipment\Comment getResource()
- * @method \Magento\Sales\Model\Order\Shipment\Comment setCreatedAt(string $value)
  */
 class Comment extends AbstractModel implements ShipmentCommentInterface
 {
@@ -126,6 +125,14 @@ class Comment extends AbstractModel implements ShipmentCommentInterface
         return $this->getData(ShipmentCommentInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(ShipmentCommentInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns is_customer_notified
      *
diff --git a/app/code/Magento/Sales/Model/Order/Shipment/Track.php b/app/code/Magento/Sales/Model/Order/Shipment/Track.php
index 325c9837bee51bc906c55afdc3d172c9da8966f3..f5b15181cb8fcb89a5ddb301b28a4290573010ec 100644
--- a/app/code/Magento/Sales/Model/Order/Shipment/Track.php
+++ b/app/code/Magento/Sales/Model/Order/Shipment/Track.php
@@ -12,7 +12,6 @@ use Magento\Sales\Model\AbstractModel;
 /**
  * @method \Magento\Sales\Model\Resource\Order\Shipment\Track _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Shipment\Track getResource()
- * @method \Magento\Sales\Model\Order\Shipment\Track setCreatedAt(string $value)
  *
  * @author      Magento Core Team <core@magentocommerce.com>
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -232,6 +231,14 @@ class Track extends AbstractModel implements ShipmentTrackInterface
         return $this->getData(ShipmentTrackInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(ShipmentTrackInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns description
      *
diff --git a/app/code/Magento/Sales/Model/Order/Status/History.php b/app/code/Magento/Sales/Model/Order/Status/History.php
index edd828956c8241f11fcc8d929f4618a36d75e858..b442ebba05f7df7af8691e42949e79c174b0cb74 100644
--- a/app/code/Magento/Sales/Model/Order/Status/History.php
+++ b/app/code/Magento/Sales/Model/Order/Status/History.php
@@ -14,7 +14,6 @@ use Magento\Sales\Model\AbstractModel;
  *
  * @method \Magento\Sales\Model\Resource\Order\Status\History _getResource()
  * @method \Magento\Sales\Model\Resource\Order\Status\History getResource()
- * @method \Magento\Sales\Model\Order\Status\History setCreatedAt(string $value)
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class History extends AbstractModel implements OrderStatusHistoryInterface
@@ -196,6 +195,14 @@ class History extends AbstractModel implements OrderStatusHistoryInterface
         return $this->getData(OrderStatusHistoryInterface::CREATED_AT);
     }
 
+    /**
+     * {@inheritdoc}
+     */
+    public function setCreatedAt($createdAt)
+    {
+        return $this->setData(OrderStatusHistoryInterface::CREATED_AT, $createdAt);
+    }
+
     /**
      * Returns entity_id
      *
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php
index 69f3f6e0df310d27dc7c7c6774ad5a62ca1e82cb..9359af03ee68dfcb020c7e7ed1a9923bd4d456ec 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Email/AsyncSendingTest.php
@@ -63,7 +63,7 @@ class AsyncSendingTest extends \PHPUnit_Framework_TestCase
     public function testAfterSave($value, $oldValue, $eventName)
     {
         $path = 'sales_email/general/async_sending';
-        $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT;
+        $scope = \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT;
 
         $this->object->setData(['value' => $value, 'path' => $path, 'scope' => $scope]);
 
diff --git a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php
index 972cfae38e2ae4273235a2c7a7d1ac260b4f45a3..729c19940ccff8b5f464eeeb895bc74e5aeafa16 100644
--- a/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php
+++ b/app/code/Magento/Sales/Test/Unit/Model/Config/Backend/Grid/AsyncIndexingTest.php
@@ -63,7 +63,7 @@ class AsyncIndexingTest extends \PHPUnit_Framework_TestCase
     public function testAfterSave($value, $oldValue, $eventName)
     {
         $path = 'dev/grid/async_indexing';
-        $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT;
+        $scope = \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT;
 
         $this->object->setData(['value' => $value, 'path' => $path, 'scope' => $scope]);
 
diff --git a/app/code/Magento/Sales/etc/data_object.xml b/app/code/Magento/Sales/etc/service_data_attributes.xml
similarity index 78%
rename from app/code/Magento/Sales/etc/data_object.xml
rename to app/code/Magento/Sales/etc/service_data_attributes.xml
index 8ef8080db8a29120f255cedfe0c083f45d85e5e5..1e7f320c18ed6e98b0c979d23e80e6ff48478fa4 100644
--- a/app/code/Magento/Sales/etc/data_object.xml
+++ b/app/code/Magento/Sales/etc/service_data_attributes.xml
@@ -5,10 +5,10 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Sales\Api\Data\OrderInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
         <attribute code="applied_taxes" type="Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface[]" />
         <attribute code="item_applied_taxes" type="Magento\Tax\Api\Data\OrderTaxDetailsItemInterface[]" />
         <attribute code="converting_from_quote" type="boolean" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/app/code/Magento/Search/Model/SearchEngine.php b/app/code/Magento/Search/Model/SearchEngine.php
index c4d5c4787a17af5dbfd7f042da520650493642c3..f80e6bfd8796f2d6e2dae2901f27028964c97b6c 100644
--- a/app/code/Magento/Search/Model/SearchEngine.php
+++ b/app/code/Magento/Search/Model/SearchEngine.php
@@ -17,14 +17,21 @@ class SearchEngine implements SearchEngineInterface
     /**
      * @var AdapterInterface
      */
-    protected $adapter;
+    private $adapter = null;
+
+    /**
+     * Adapter factory
+     *
+     * @var AdapterFactory
+     */
+    private $adapterFactory;
 
     /**
      * @param AdapterFactory $adapterFactory
      */
     public function __construct(AdapterFactory $adapterFactory)
     {
-        $this->adapter = $adapterFactory->create();
+        $this->adapterFactory = $adapterFactory;
     }
 
     /**
@@ -32,6 +39,19 @@ class SearchEngine implements SearchEngineInterface
      */
     public function search(RequestInterface $request)
     {
-        return $this->adapter->query($request);
+        return $this->getAdapter()->query($request);
+    }
+
+    /**
+     * Get adapter
+     *
+     * @return AdapterInterface
+     */
+    protected function getAdapter()
+    {
+        if ($this->adapter === null) {
+            $this->adapter = $this->adapterFactory->create();
+        }
+        return $this->adapter;
     }
 }
diff --git a/app/code/Magento/Shipping/Model/Order/Track.php b/app/code/Magento/Shipping/Model/Order/Track.php
index 81b506a7d10d9a821df999357d74a3c914feb8a9..9763f2bdb6e49035dd9b5d16b0677b635b6dd39b 100644
--- a/app/code/Magento/Shipping/Model/Order/Track.php
+++ b/app/code/Magento/Shipping/Model/Order/Track.php
@@ -19,7 +19,6 @@ use Magento\Framework\Api\AttributeValueFactory;
  * @method string getTitle()
  * @method string getCarrierCode()
  * @method string getCreatedAt()
- * @method \Magento\Sales\Model\Order\Shipment\Track setCreatedAt(string $value)
  * @method string getUpdatedAt()
  * @method \Magento\Sales\Api\Data\ShipmentTrackExtensionInterface getExtensionAttributes()
  */
diff --git a/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php b/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php
index 4f03885e8cd7a910cb35abf05970bd86deb5a790..7bb9981369440c9c8062ffe6308376c8b089a05f 100644
--- a/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php
+++ b/app/code/Magento/Store/Model/Config/Reader/DefaultReader.php
@@ -7,6 +7,9 @@
  */
 namespace Magento\Store\Model\Config\Reader;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\Exception\LocalizedException;
+
 class DefaultReader implements \Magento\Framework\App\Config\Scope\ReaderInterface
 {
     /**
@@ -42,14 +45,21 @@ class DefaultReader implements \Magento\Framework\App\Config\Scope\ReaderInterfa
     /**
      * Read configuration data
      *
+     * @param null|string $scope
+     * @throws LocalizedException Exception is thrown when scope other than default is given
      * @return array
      */
-    public function read()
+    public function read($scope = null)
     {
-        $config = $this->_initialConfig->getData(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT);
+        $scope = $scope === null ? ScopeConfigInterface::SCOPE_TYPE_DEFAULT : $scope;
+        if ($scope !== ScopeConfigInterface::SCOPE_TYPE_DEFAULT) {
+            throw new \Magento\Framework\Exception\LocalizedException(__("Only default scope allowed"));
+        }
+
+        $config = $this->_initialConfig->getData($scope);
 
         $collection = $this->_collectionFactory->create(
-            ['scope' => \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT]
+            ['scope' => $scope]
         );
         $dbDefaultConfig = [];
         foreach ($collection as $item) {
diff --git a/app/code/Magento/Store/Model/Config/Reader/Store.php b/app/code/Magento/Store/Model/Config/Reader/Store.php
index c5456bcb102f9ad2952c17f33ac41f48c1ae777a..151c3625f2b1dbf596d222eaf10b3f7c9e07ce5e 100644
--- a/app/code/Magento/Store/Model/Config/Reader/Store.php
+++ b/app/code/Magento/Store/Model/Config/Reader/Store.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Store\Model\Config\Reader;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\Exception\NoSuchEntityException;
 
 class Store implements \Magento\Framework\App\Config\Scope\ReaderInterface
@@ -66,7 +67,7 @@ class Store implements \Magento\Framework\App\Config\Scope\ReaderInterface
     /**
      * Read configuration by code
      *
-     * @param string $code
+     * @param null|string $code
      * @return array
      * @throws NoSuchEntityException
      */
@@ -74,7 +75,7 @@ class Store implements \Magento\Framework\App\Config\Scope\ReaderInterface
     {
         if (empty($code)) {
             $store = $this->_storeManager->getStore();
-        } elseif (($code == \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT)) {
+        } elseif (($code == ScopeConfigInterface::SCOPE_TYPE_DEFAULT)) {
             $store = $this->_storeManager->getDefaultStoreView();
         } else {
             $store = $this->_storeFactory->create();
diff --git a/app/code/Magento/Store/Model/Config/Reader/Website.php b/app/code/Magento/Store/Model/Config/Reader/Website.php
index 1448b04bf9afbf5e6b49b5e223f53b9ec2a52f69..7971deb6c9713199cfe7c85617ebe767556edb18 100644
--- a/app/code/Magento/Store/Model/Config/Reader/Website.php
+++ b/app/code/Magento/Store/Model/Config/Reader/Website.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Store\Model\Config\Reader;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class Website implements \Magento\Framework\App\Config\Scope\ReaderInterface
 {
     /**
@@ -62,7 +64,7 @@ class Website implements \Magento\Framework\App\Config\Scope\ReaderInterface
     public function read($code = null)
     {
         $config = array_replace_recursive(
-            $this->_scopePool->getScope(\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT)->getSource(),
+            $this->_scopePool->getScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT)->getSource(),
             $this->_initialConfig->getData("websites|{$code}")
         );
 
diff --git a/app/code/Magento/Store/Model/Store.php b/app/code/Magento/Store/Model/Store.php
index 1da41cae73e20593dcdc498b6b4a1bc94bb5a0b6..494d1c129254154208d81e6956d72027003f9dc4 100644
--- a/app/code/Magento/Store/Model/Store.php
+++ b/app/code/Magento/Store/Model/Store.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Store\Model;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\App\Filesystem\DirectoryList;
 use Magento\Framework\App\Http\Context;
 use Magento\Framework\Model\AbstractModel;
@@ -55,7 +56,7 @@ class Store extends AbstractModel implements
     const XML_PATH_UNSECURE_BASE_URL = 'web/unsecure/base_url';
 
     const XML_PATH_SECURE_BASE_URL = 'web/secure/base_url';
-    
+
     const XML_PATH_SECURE_IN_FRONTEND = 'web/secure/use_in_frontend';
 
     const XML_PATH_SECURE_IN_ADMINHTML = 'web/secure/use_in_adminhtml';
@@ -479,7 +480,7 @@ class Store extends AbstractModel implements
     {
         $data = $this->_config->getValue($path, ScopeInterface::SCOPE_STORE, $this->getCode());
         if (!$data) {
-            $data = $this->_config->getValue($path, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT);
+            $data = $this->_config->getValue($path, ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
         }
         return $data === false ? null : $data;
     }
@@ -788,7 +789,7 @@ class Store extends AbstractModel implements
         if ($configValue == self::PRICE_SCOPE_GLOBAL) {
             return $this->_config->getValue(
                 \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
-                \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+                ScopeConfigInterface::SCOPE_TYPE_DEFAULT
             );
         } else {
             return $this->_getConfig(\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE);
diff --git a/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php b/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php
index 9e91f4b43281522ac0a2d3688167488893df43e3..25875ae67dd5973525a0a59cbf7e2c1635c58691 100644
--- a/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php
+++ b/app/code/Magento/Store/Test/Unit/Model/Config/Reader/DefaultReaderTest.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Store\Test\Unit\Model\Config\Reader;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class DefaultReaderTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -46,7 +48,7 @@ class DefaultReaderTest extends \PHPUnit_Framework_TestCase
         )->method(
             'getData'
         )->with(
-            \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+            ScopeConfigInterface::SCOPE_TYPE_DEFAULT
         )->will(
             $this->returnValue(['config' => ['key1' => 'default_value1', 'key2' => 'default_value2']])
         );
diff --git a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php
index e9a6f8ecf7207c03889a4043d02168d18e6e5b72..8d305848c554f900bd521f04490c7b8f18fca6a4 100644
--- a/app/code/Magento/Store/Test/Unit/Model/StoreTest.php
+++ b/app/code/Magento/Store/Test/Unit/Model/StoreTest.php
@@ -8,6 +8,7 @@
 
 namespace Magento\Store\Test\Unit\Model;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Store\Model\ScopeInterface;
 use Magento\Framework\App\Config\ReinitableConfigInterface;
 use Magento\Store\Model\Store;
@@ -399,7 +400,7 @@ class StoreTest extends \PHPUnit_Framework_TestCase
                 ['catalog/price/scope', ScopeInterface::SCOPE_STORE, 'scope_code', $priceScope],
                 [
                     \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
-                    \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+                    ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
                     null,
                     'USD'
                 ],
diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml
index ebbc61f6036c8723c1fe943e07795097b63f1f6d..c1584145c2d3db3ad86924dd7ac6b0099155aa18 100644
--- a/app/code/Magento/Store/etc/di.xml
+++ b/app/code/Magento/Store/etc/di.xml
@@ -79,7 +79,7 @@
         <arguments>
             <argument name="session" xsi:type="object" shared="false">Magento\Framework\Session\Generic\Proxy</argument>
             <argument name="isCustomEntryPoint" xsi:type="init_parameter">Magento\Store\Model\Store::CUSTOM_ENTRY_POINT_PARAM</argument>
-            <argument name="url" xsi:type="object" shared="false">Magento\Framework\UrlInterface</argument>
+            <argument name="url" xsi:type="object" shared="false">Magento\Framework\UrlInterface\Proxy</argument>
         </arguments>
     </type>
     <type name="Magento\Store\Model\StoreManager">
diff --git a/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php b/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php
index 356d39d0bc106519315c3fa76da8321931c116fd..a5865871a5f59f25f9d72d624d277088d5f4ae41 100644
--- a/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php
+++ b/app/code/Magento/Tax/Api/Data/AppliedTaxInterface.php
@@ -7,18 +7,6 @@ namespace Magento\Tax\Api\Data;
 
 interface AppliedTaxInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_TAX_RATE_KEY = 'tax_rate_key';
-
-    const KEY_PERCENT = 'percent';
-
-    const KEY_AMOUNT = 'amount';
-
-    const KEY_RATES = 'rates';
-    /**#@-*/
-
     /**
      * Get tax rate key
      *
diff --git a/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php b/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php
index cee4ab16fd1f0acdde90bd353b3d264e4ba52b11..afd3231335ea011e6daed2e0fa5537849c376362 100644
--- a/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php
+++ b/app/code/Magento/Tax/Api/Data/AppliedTaxRateInterface.php
@@ -8,16 +8,6 @@ namespace Magento\Tax\Api\Data;
 
 interface AppliedTaxRateInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_CODE = 'code';
-
-    const KEY_TITLE = 'title';
-
-    const KEY_PERCENT = 'percent';
-    /**#@-*/
-
     /**
      * Get code
      *
diff --git a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php
index 7123087eacf565f0341178e6f9643c9a98bf7036..603d287eabff739644b9ed7ada2d397b4c59d857 100644
--- a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php
+++ b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsAppliedTaxInterface.php
@@ -9,20 +9,6 @@ namespace Magento\Tax\Api\Data;
 
 interface OrderTaxDetailsAppliedTaxInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_CODE = 'code';
-
-    const KEY_TITLE = 'title';
-
-    const KEY_PERCENT = 'percent';
-
-    const KEY_AMOUNT = 'amount';
-
-    const KEY_BASE_AMOUNT = 'base_amount';
-    /**#@-*/
-
     /**
      * Get code
      *
diff --git a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php
index d68de6f5858aab5c3362c60d62a1d006bc22c42d..d57fb6ba5194faac79dbcddd4e0269adb81c6dae 100644
--- a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php
+++ b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsInterface.php
@@ -9,10 +9,6 @@ namespace Magento\Tax\Api\Data;
 
 interface OrderTaxDetailsInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    const KEY_APPLIED_TAXES = 'applied_taxes';
-
-    const KEY_ITEMS = 'items';
-
     /**
      * Get applied taxes at order level
      *
diff --git a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php
index 905a0aa203594097e5377130679dfb4e5bb18a92..a43d4ccd7dfc4b5f1f95ed1fbf29527bdea40e89 100644
--- a/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php
+++ b/app/code/Magento/Tax/Api/Data/OrderTaxDetailsItemInterface.php
@@ -9,18 +9,6 @@ namespace Magento\Tax\Api\Data;
 
 interface OrderTaxDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_TYPE = 'type';
-
-    const KEY_ITEM_ID = 'item_id';
-
-    const KEY_ASSOCIATED_ITEM_ID = 'associated_item_id';
-
-    const KEY_APPLIED_TAXES = 'applied_taxes';
-    /**#@-*/
-
     /**
      * Get type (shipping, product, weee, gift wrapping, etc)
      *
diff --git a/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php b/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php
index f7d69e1572f86eaecfad263eeeb026ebe8f280bc..b3250a1f7caa09419ffc35169ac0362ffdc2ef2a 100644
--- a/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php
+++ b/app/code/Magento/Tax/Api/Data/QuoteDetailsInterface.php
@@ -9,22 +9,6 @@ namespace Magento\Tax\Api\Data;
 
 interface QuoteDetailsInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_BILLING_ADDRESS = 'billing_address';
-
-    const KEY_SHIPPING_ADDRESS = 'shipping_address';
-
-    const KEY_CUSTOMER_TAX_CLASS_KEY = 'customer_tax_class_key';
-
-    const KEY_ITEMS = 'items';
-
-    const CUSTOMER_TAX_CLASS_ID = 'customer_tax_class_id';
-
-    const KEY_CUSTOMER_ID = 'customer_id';
-    /**#@-*/
-
     /**
      * Get customer billing address
      *
diff --git a/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php b/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php
index ea3e57327f3115581853572e3159628b600c31cd..daffa01d4f87651346965eab83a723e7f806df28 100644
--- a/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php
+++ b/app/code/Magento/Tax/Api/Data/QuoteDetailsItemInterface.php
@@ -7,32 +7,6 @@ namespace Magento\Tax\Api\Data;
 
 interface QuoteDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_CODE = 'code';
-
-    const KEY_TYPE = 'type';
-
-    const KEY_TAX_CLASS_KEY = 'tax_class_key';
-
-    const KEY_UNIT_PRICE = 'unit_price';
-
-    const KEY_QUANTITY = 'quantity';
-
-    const KEY_TAX_INCLUDED = 'tax_included';
-
-    const KEY_SHORT_DESCRIPTION = 'short_description';
-
-    const KEY_DISCOUNT_AMOUNT = 'discount_amount';
-
-    const KEY_PARENT_CODE = 'parent_code';
-
-    const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code';
-
-    const KEY_TAX_CLASS_ID = 'tax_class_id';
-    /**#@-*/
-
     /**
      * Get code (sku or shipping code)
      *
@@ -172,7 +146,7 @@ interface QuoteDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDat
     /**
      * Get associated item code if this item is associated with another item, null otherwise
      *
-     * @return mixed|null
+     * @return int|null
      */
     public function getAssociatedItemCode();
 
diff --git a/app/code/Magento/Tax/Api/Data/TaxClassInterface.php b/app/code/Magento/Tax/Api/Data/TaxClassInterface.php
index 80774ef47c646bed3cb54e3813b7968c37536adc..632219c6ad66986690b0655198feceb4f271b0a5 100644
--- a/app/code/Magento/Tax/Api/Data/TaxClassInterface.php
+++ b/app/code/Magento/Tax/Api/Data/TaxClassInterface.php
@@ -9,15 +9,6 @@ namespace Magento\Tax\Api\Data;
 
 interface TaxClassInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     *
-     * Tax class field key.
-     */
-    const KEY_ID = 'class_id';
-    const KEY_NAME = 'class_name';
-    const KEY_TYPE = 'class_type';
-    /**#@-*/
-
     /**
      * Get tax class ID.
      *
diff --git a/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php b/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php
index 9f0a25a170cbadbd71ad8e1c1e7d7cd6de7d0c27..d6f5d742ad88069a9810c5f815a3a0ac8668f18a 100644
--- a/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php
+++ b/app/code/Magento/Tax/Api/Data/TaxClassKeyInterface.php
@@ -10,19 +10,10 @@ use Magento\Framework\Api\ExtensibleDataInterface;
 
 interface TaxClassKeyInterface extends ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_TYPE = 'type';
-
-    const KEY_VALUE = 'value';
-    /**#@-*/
-
     /**#@+
      * Constants defined for type of tax class key
      */
-    const TYPE_ID = 'id';
-
+    const TYPE_ID   = 'id';
     const TYPE_NAME = 'name';
     /**#@-*/
 
diff --git a/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php b/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php
index f06ba96bc54ca28a6ed4e167d48bbc4e553dcd15..ce0ae30e69e15cf434a0e034be6d913afc9cc245 100644
--- a/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php
+++ b/app/code/Magento/Tax/Api/Data/TaxDetailsInterface.php
@@ -8,21 +8,6 @@ namespace Magento\Tax\Api\Data;
 
 interface TaxDetailsInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_SUBTOTAL = 'subtotal';
-
-    const KEY_TAX_AMOUNT = 'tax_amount';
-
-    const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount';
-
-    const KEY_APPLIED_TAXES = 'applied_taxes';
-
-    const KEY_ITEMS = 'items';
-
-    /**#@-*/
-
     /**
      * Get subtotal
      *
diff --git a/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php b/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php
index fad3229d7076a589363cbabf9f5bcb064984e04f..0550b6614dfc76ef111e29179a96551e476a831b 100644
--- a/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php
+++ b/app/code/Magento/Tax/Api/Data/TaxDetailsItemInterface.php
@@ -8,36 +8,6 @@ namespace Magento\Tax\Api\Data;
 
 interface TaxDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_CODE = 'code';
-
-    const KEY_TYPE = 'type';
-
-    const KEY_TAX_PERCENT = 'tax_percent';
-
-    const KEY_PRICE = 'price';
-
-    const KEY_PRICE_INCL_TAX = 'price_incl_tax';
-
-    const KEY_ROW_TOTAL = 'row_total';
-
-    const KEY_ROW_TOTAL_INCL_TAX = 'row_total_incl_tax';
-
-    const KEY_ROW_TAX = 'row_tax';
-
-    const KEY_TAXABLE_AMOUNT = 'taxable_amount';
-
-    const KEY_DISCOUNT_AMOUNT = 'discount_amount';
-
-    const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount';
-
-    const KEY_APPLIED_TAXES = 'applied_taxes';
-
-    const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code';
-    /**#@-*/
-
     /**
      * Get code (sku or shipping code)
      *
@@ -221,7 +191,7 @@ interface TaxDetailsItemInterface extends \Magento\Framework\Api\ExtensibleDataI
     /**
      * Return associated item code if this item is associated with another item, null otherwise
      *
-     * @return mixed|null
+     * @return int|null
      */
     public function getAssociatedItemCode();
 
diff --git a/app/code/Magento/Tax/Api/Data/TaxRateInterface.php b/app/code/Magento/Tax/Api/Data/TaxRateInterface.php
index 881bb7fa7e013838c558f5514af373dcbe6fceaa..34cf62414862a644f928531ce9fafe996648c32e 100644
--- a/app/code/Magento/Tax/Api/Data/TaxRateInterface.php
+++ b/app/code/Magento/Tax/Api/Data/TaxRateInterface.php
@@ -9,22 +9,6 @@ namespace Magento\Tax\Api\Data;
 
 interface TaxRateInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     * Constants defined for keys of array, makes typos less likely
-     */
-    const KEY_ID = 'id';
-    const KEY_COUNTRY_ID = 'tax_country_id';
-    const KEY_REGION_ID = 'tax_region_id';
-    const KEY_REGION_NAME = 'region_name';
-    const KEY_POSTCODE = 'tax_postcode';
-    const KEY_ZIP_IS_RANGE = 'zip_is_range';
-    const KEY_ZIP_RANGE_FROM = 'zip_from';
-    const KEY_ZIP_RANGE_TO = 'zip_to';
-    const KEY_PERCENTAGE_RATE = 'rate';
-    const KEY_CODE = 'code';
-    const KEY_TITLES = 'titles';
-    /**#@-*/
-
     /**
      * Get id
      *
diff --git a/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php b/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php
index 8d3991070b9565d9527c7c036aca9e45864d15f4..7dcfd2413759636b184d8e6cac0a789d6ecfacee 100644
--- a/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php
+++ b/app/code/Magento/Tax/Api/Data/TaxRateTitleInterface.php
@@ -9,15 +9,6 @@ namespace Magento\Tax\Api\Data;
 
 interface TaxRateTitleInterface extends \Magento\Framework\Api\ExtensibleDataInterface
 {
-    /**#@+
-     *
-     * Tax rate field key.
-     */
-    const KEY_STORE_ID = 'store_id';
-
-    const KEY_VALUE_ID = 'value';
-    /**#@-*/
-
     /**
      * Get store id
      *
diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php
index 08a7803aa85474510a9135d22b7819dd4122c03c..84e32da4ae97439c3fbc7d8e8fccefc364b9f6cc 100644
--- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php
+++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php
@@ -65,6 +65,11 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
      */
     protected $_taxRateCollection;
 
+    /**
+     * @var \Magento\Tax\Model\Calculation\Rate\Converter
+     */
+    protected $_taxRateConverter;
+
     /**
      * @param \Magento\Backend\Block\Template\Context $context
      * @param \Magento\Framework\Registry $registry
@@ -75,6 +80,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
      * @param \Magento\Tax\Helper\Data $taxData
      * @param \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository
      * @param \Magento\Tax\Model\TaxRateCollection $taxRateCollection
+     * @param \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter
      * @param array $data
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      */
@@ -88,6 +94,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
         \Magento\Tax\Helper\Data $taxData,
         \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository,
         \Magento\Tax\Model\TaxRateCollection $taxRateCollection,
+        \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter,
         array $data = []
     ) {
         $this->_regionFactory = $regionFactory;
@@ -96,6 +103,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
         $this->_taxData = $taxData;
         $this->_taxRateRepository = $taxRateRepository;
         $this->_taxRateCollection = $taxRateCollection;
+        $this->_taxRateConverter = $taxRateConverter;
         parent::__construct($context, $registry, $formFactory, $data);
     }
 
@@ -127,7 +135,7 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
         }
 
         $sessionFormValues = (array)$this->_coreRegistry->registry(RegistryConstants::CURRENT_TAX_RATE_FORM_DATA);
-        $formData = isset($taxRateDataObject) ? $this->extractTaxRateData($taxRateDataObject) : [];
+        $formData = isset($taxRateDataObject) ? $this->_taxRateConverter->createArrayFromServiceObject($taxRateDataObject) : [];
         $formData = array_merge($formData, $sessionFormValues);
 
         if (isset($formData['zip_is_range']) && $formData['zip_is_range'] && !isset($formData['tax_postcode'])) {
@@ -286,65 +294,4 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
 
         return parent::_prepareForm();
     }
-
-    /**
-     * Get Tax Rates Collection
-     *
-     * @return mixed
-     */
-    public function getRateCollection()
-    {
-        if ($this->getData('rate_collection') == null) {
-            $items = $this->_taxRateCollection->getItems();
-            $rates = [];
-            foreach ($items as $rate) {
-                $rateData = $rate->getData();
-                if (isset($rateData['titles'])) {
-                    foreach ($rateData['titles'] as $storeId => $value) {
-                        $rateData['title[' . $storeId . ']'] = $value;
-                    }
-                }
-                unset($rateData['titles']);
-                $rates[] = $rateData;
-            }
-
-            $this->setRateCollection($rates);
-        }
-        return $this->getData('rate_collection');
-    }
-
-    /**
-     * Extract tax rate data in a format which is
-     *
-     * @param \Magento\Tax\Api\Data\TaxRateInterface $taxRate
-     * @return array
-     */
-    protected function extractTaxRateData($taxRate)
-    {
-        $formData = [
-            'tax_calculation_rate_id' => $taxRate->getId(),
-            'tax_country_id' => $taxRate->getTaxCountryId(),
-            'tax_region_id' => $taxRate->getTaxRegionId(),
-            'tax_postcode' => $taxRate->getTaxPostcode(),
-            'code' => $taxRate->getCode(),
-            'rate' => $taxRate->getRate(),
-            'zip_is_range' => false,
-        ];
-
-        if ($taxRate->getZipFrom() && $taxRate->getZipTo()) {
-            $formData['zip_is_range'] = true;
-            $formData['zip_from'] = $taxRate->getZipFrom();
-            $formData['zip_to'] = $taxRate->getZipTo();
-        }
-
-        if ($taxRate->getTitles()) {
-            $titleData = [];
-            foreach ($taxRate->getTitles() as $title) {
-                $titleData[] = [$title->getStoreId() => $title->getValue()];
-            }
-            $formData['title'] = $titleData;
-        }
-
-        return $formData;
-    }
 }
diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php
index 0d2b16bee6f0dff1154bba3c025f3a0f9b0b541a..fb434ca6dd3092395c7df36a0f4cd1dfa0fdd222 100644
--- a/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php
+++ b/app/code/Magento/Tax/Block/Adminhtml/Rule/Edit/Form.php
@@ -297,6 +297,16 @@ class Form extends \Magento\Backend\Block\Widget\Form\Generic
         return $this->getUrl('tax/rate/ajaxSave/');
     }
 
+    /**
+     * Retrieve Tax Rate load URL
+     *
+     * @return string
+     */
+    public function getTaxRateLoadUrl()
+    {
+        return $this->getUrl('tax/rate/ajaxLoad/');
+    }
+
     /**
      * Extract tax rule data in a format which is
      *
diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate.php
index 21a165edf65bf48964f43da33811e285fcbdfffe..e25e0a19a87a2752204de23adc90e6106e6c559e 100644
--- a/app/code/Magento/Tax/Controller/Adminhtml/Rate.php
+++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate.php
@@ -21,38 +21,30 @@ class Rate extends \Magento\Backend\App\Action
     protected $_coreRegistry;
 
     /**
-     * @var \Magento\Tax\Api\TaxRateRepositoryInterface
+     * @var \Magento\Tax\Model\Calculation\Rate\Converter
      */
-    protected $_taxRateRepository;
+    protected $_taxRateConverter;
 
     /**
-     * @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory
-     */
-    protected $_taxRateDataObjectFactory;
-
-    /**
-     * @var \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory
+     * @var \Magento\Tax\Api\TaxRateRepositoryInterface
      */
-    protected $_taxRateTitleDataObjectFactory;
+    protected $_taxRateRepository;
 
     /**
      * @param \Magento\Backend\App\Action\Context $context
      * @param \Magento\Framework\Registry $coreRegistry
+     * @param \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter
      * @param \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository
-     * @param \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory
-     * @param \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory
      */
     public function __construct(
         \Magento\Backend\App\Action\Context $context,
         \Magento\Framework\Registry $coreRegistry,
-        \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository,
-        \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory,
-        \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory
+        \Magento\Tax\Model\Calculation\Rate\Converter $taxRateConverter,
+        \Magento\Tax\Api\TaxRateRepositoryInterface $taxRateRepository
     ) {
         $this->_coreRegistry = $coreRegistry;
+        $this->_taxRateConverter = $taxRateConverter;
         $this->_taxRateRepository = $taxRateRepository;
-        $this->_taxRateDataObjectFactory = $taxRateDataObjectFactory;
-        $this->_taxRateTitleDataObjectFactory = $taxRateTitleDataObjectFactory;
         parent::__construct($context);
     }
 
@@ -96,50 +88,4 @@ class Rate extends \Magento\Backend\App\Action
     {
         return $this->_authorization->isAllowed('Magento_Tax::manage_tax');
     }
-
-    /**
-     * Populate a tax rate data object
-     *
-     * @param array $formData
-     * @return \Magento\Tax\Api\Data\TaxRateInterface
-     */
-    protected function populateTaxRateData($formData)
-    {
-        $taxRate = $this->_taxRateDataObjectFactory->create();
-        $taxRate->setId($this->extractFormData($formData, 'tax_calculation_rate_id'))
-            ->setTaxCountryId($this->extractFormData($formData, 'tax_country_id'))
-            ->setTaxRegionId($this->extractFormData($formData, 'tax_region_id'))
-            ->setTaxPostcode($this->extractFormData($formData, 'tax_postcode'))
-            ->setCode($this->extractFormData($formData, 'code'))
-            ->setRate($this->extractFormData($formData, 'rate'));
-        if (isset($formData['zip_is_range']) && $formData['zip_is_range']) {
-            $taxRate->setZipFrom($this->extractFormData($formData, 'zip_from'))
-                ->setZipTo($this->extractFormData($formData, 'zip_to'))->setZipIsRange(1);
-        }
-
-        if (isset($formData['title'])) {
-            $titles = [];
-            foreach ($formData['title'] as $storeId => $value) {
-                $titles[] = $this->_taxRateTitleDataObjectFactory->create()->setStoreId($storeId)->setValue($value);
-            }
-            $taxRate->setTitles($titles);
-        }
-
-        return $taxRate;
-    }
-
-    /**
-     * Determines if an array value is set in the form data array and returns it.
-     *
-     * @param array $formData the form to get data from
-     * @param string $fieldName the key
-     * @return null|string
-     */
-    protected function extractFormData($formData, $fieldName)
-    {
-        if (isset($formData[$fieldName])) {
-            return $formData[$fieldName];
-        }
-        return null;
-    }
 }
diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxLoad.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxLoad.php
new file mode 100755
index 0000000000000000000000000000000000000000..673ea280bab25ed504a240bfcddb25d1e2cbadfe
--- /dev/null
+++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxLoad.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ *
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Tax\Controller\Adminhtml\Rate;
+
+use Magento\Framework\Exception\NoSuchEntityException;
+use Magento\Framework\Controller\ResultFactory;
+
+class AjaxLoad extends \Magento\Tax\Controller\Adminhtml\Rate
+{
+    /**
+     * Json needed for the Ajax Edit Form
+     *
+     * @return void
+     */
+    public function execute()
+    {
+        $rateId = (int)$this->getRequest()->getParam('id');
+        try {
+            /* @var \Magento\Tax\Api\Data\TaxRateInterface */
+            $taxRateDataObject = $this->_taxRateRepository->get($rateId);
+            /* @var array */
+            $resultArray= $this->_taxRateConverter->createArrayFromServiceObject($taxRateDataObject, true);
+
+            $responseContent = [
+                'success' => true,
+                'error_message' => '',
+                'result'=>$resultArray,
+                ];
+        } catch (NoSuchEntityException $e) {
+            $responseContent = [
+                'success' => false,
+                'error_message' => $e->getMessage(),
+            ];
+        } catch (\Exception $e) {
+            $responseContent = [
+                'success' => false,
+                'error_message' => __('An error occurred while loading this tax rate.'),
+            ];
+        }
+
+        /** @var \Magento\Framework\Controller\Result\Json $resultJson */
+        $resultJson = $this->resultFactory->create(ResultFactory::TYPE_JSON);
+        $resultJson->setData($responseContent);
+        return $resultJson;
+    }
+}
diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php
index fd5471314d2d7066ae4462d3276dfcf0c55bb193..32ba0758fbd58b291e036ec525b06c6c3c3242c4 100644
--- a/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php
+++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate/AjaxSave.php
@@ -20,7 +20,7 @@ class AjaxSave extends \Magento\Tax\Controller\Adminhtml\Rate
         try {
             $rateData = $this->_processRateData($this->getRequest()->getPostValue());
             /** @var \Magento\Tax\Api\Data\TaxRateInterface  $taxRate */
-            $taxRate = $this->populateTaxRateData($rateData);
+            $taxRate = $this->_taxRateConverter->populateTaxRateData($rateData);
             $this->_taxRateRepository->save($taxRate);
             $responseContent = [
                 'success' => true,
diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php b/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php
index e56a31a4d348942962cb96a04f4f598bbbdaf4f3..36dbcc284eabb831c206477b1da004c886686429 100644
--- a/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php
+++ b/app/code/Magento/Tax/Controller/Adminhtml/Rate/Save.php
@@ -32,7 +32,7 @@ class Save extends \Magento\Tax\Controller\Adminhtml\Rate
             }
 
             try {
-                $taxData = $this->populateTaxRateData($ratePost);
+                $taxData = $this->_taxRateConverter->populateTaxRateData($ratePost);
                 $this->_taxRateRepository->save($taxData);
 
                 $this->messageManager->addSuccess(__('The tax rate has been saved.'));
diff --git a/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php b/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php
index d84a09825b08fb4391830a6ec8261b24d152557a..9fede0b0238787abcc72a691b7473f588a90434b 100644
--- a/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php
+++ b/app/code/Magento/Tax/Controller/Adminhtml/Tax/IgnoreTaxNotification.php
@@ -6,6 +6,7 @@
  */
 namespace Magento\Tax\Controller\Adminhtml\Tax;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\Controller\ResultFactory;
 
 class IgnoreTaxNotification extends \Magento\Tax\Controller\Adminhtml\Tax
@@ -43,7 +44,7 @@ class IgnoreTaxNotification extends \Magento\Tax\Controller\Adminhtml\Tax
             try {
                 $path = 'tax/notification/ignore_' . $section;
                 $this->_objectManager->get('Magento\Config\Model\Resource\Config')
-                    ->saveConfig($path, 1, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0);
+                    ->saveConfig($path, 1, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0);
             } catch (\Exception $e) {
                 $this->messageManager->addError($e->getMessage());
             }
diff --git a/app/code/Magento/Tax/Model/Calculation/Rate.php b/app/code/Magento/Tax/Model/Calculation/Rate.php
index 18dde7c54f14d54945d1625e034e07ce7833f502..fae528a7d943f8b48b5023d3aaf2ad468a62bb98 100644
--- a/app/code/Magento/Tax/Model/Calculation/Rate.php
+++ b/app/code/Magento/Tax/Model/Calculation/Rate.php
@@ -20,8 +20,24 @@ use Magento\Tax\Api\Data\TaxRateInterface;
  * @method \Magento\Tax\Model\Resource\Calculation\Rate getResource()
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
-class Rate extends \Magento\Framework\Model\AbstractExtensibleModel implements \Magento\Tax\Api\Data\TaxRateInterface
+class Rate extends \Magento\Framework\Model\AbstractExtensibleModel implements TaxRateInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_ID              = 'id';
+    const KEY_COUNTRY_ID      = 'tax_country_id';
+    const KEY_REGION_ID       = 'tax_region_id';
+    const KEY_REGION_NAME     = 'region_name';
+    const KEY_POSTCODE        = 'tax_postcode';
+    const KEY_ZIP_IS_RANGE    = 'zip_is_range';
+    const KEY_ZIP_RANGE_FROM  = 'zip_from';
+    const KEY_ZIP_RANGE_TO    = 'zip_to';
+    const KEY_PERCENTAGE_RATE = 'rate';
+    const KEY_CODE            = 'code';
+    const KEY_TITLES          = 'titles';
+    /**#@-*/
+
     /**
      * List of tax titles
      *
diff --git a/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php b/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php
index 7a407da4de340f4d8a8c82bc2df877786ecdcc3a..5fd8da907a5d8b81a297511758895d0dfa2156bd 100644
--- a/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php
+++ b/app/code/Magento/Tax/Model/Calculation/Rate/Converter.php
@@ -12,6 +12,27 @@ namespace Magento\Tax\Model\Calculation\Rate;
  */
 class Converter
 {
+    /**
+     * @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory
+     */
+    protected $taxRateDataObjectFactory;
+
+    /**
+     * @var \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory
+     */
+    protected $taxRateTitleDataObjectFactory;
+
+    /**
+     * @param \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory
+     * @param \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory,
+     */
+    public function __construct(
+        \Magento\Tax\Api\Data\TaxRateInterfaceFactory $taxRateDataObjectFactory,
+        \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory $taxRateTitleDataObjectFactory
+    ) {
+        $this->taxRateDataObjectFactory = $taxRateDataObjectFactory;
+        $this->taxRateTitleDataObjectFactory = $taxRateTitleDataObjectFactory;
+    }
     /**
      * Convert a tax rate data object to an array of associated titles
      *
@@ -29,4 +50,105 @@ class Converter
         }
         return $titleData;
     }
+
+    /**
+     * Extract tax rate data in a format which is
+     *
+     * @param \Magento\Tax\Api\Data\TaxRateInterface $taxRate
+     * @param Boolean $returnNumericLogic
+     * @return array
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     */
+    public function createArrayFromServiceObject(
+        \Magento\Tax\Api\Data\TaxRateInterface $taxRate,
+        $returnNumericLogic = false
+    ) {
+        $taxRateFormData = [
+            'tax_calculation_rate_id' => $taxRate->getId(),
+            'tax_country_id' => $taxRate->getTaxCountryId(),
+            'tax_region_id' => $taxRate->getTaxRegionId(),
+            'tax_postcode' => $taxRate->getTaxPostcode(),
+            'code' => $taxRate->getCode(),
+            'rate' => $taxRate->getRate(),
+            'zip_is_range' => $returnNumericLogic?0:false,
+        ];
+
+        if ($taxRate->getZipFrom() && $taxRate->getZipTo()) {
+            $taxRateFormData['zip_is_range'] = $returnNumericLogic?1:true;
+            $taxRateFormData['zip_from'] = $taxRate->getZipFrom();
+            $taxRateFormData['zip_to'] = $taxRate->getZipTo();
+        }
+
+        if ($returnNumericLogic) {
+            //format for the ajax on multiple sites titles
+            $titleArray=($this->createTitleArrayFromServiceObject($taxRate));
+            if (is_array($titleArray)) {
+                foreach ($titleArray as $storeId => $title) {
+                    $taxRateFormData['title[' . $storeId . ']']=$title;
+                }
+            }
+        } else {
+            //format for the form array on multiple sites titles
+            $titleArray=($this->createTitleArrayFromServiceObject($taxRate));
+            if (is_array($titleArray)) {
+                $titleData = [];
+                foreach ($titleArray as $storeId => $title) {
+                    $titleData[] = [$storeId => $title];
+                }
+                if (count($titleArray)>0) {
+                    $taxRateFormData['title'] = $titleData;
+                }
+            }
+        }
+
+        return $taxRateFormData;
+    }
+
+
+    /**
+     * Convert an array to a tax rate data object
+     *
+     * @param array $formData
+     * @return \Magento\Tax\Api\Data\TaxRateInterface
+     */
+    public function populateTaxRateData($formData)
+    {
+        $taxRate = $this->taxRateDataObjectFactory->create();
+        $taxRate->setId($this->extractFormData($formData, 'tax_calculation_rate_id'))
+            ->setTaxCountryId($this->extractFormData($formData, 'tax_country_id'))
+            ->setTaxRegionId($this->extractFormData($formData, 'tax_region_id'))
+            ->setTaxPostcode($this->extractFormData($formData, 'tax_postcode'))
+            ->setCode($this->extractFormData($formData, 'code'))
+            ->setRate($this->extractFormData($formData, 'rate'));
+        if (isset($formData['zip_is_range']) && $formData['zip_is_range']) {
+            $taxRate->setZipFrom($this->extractFormData($formData, 'zip_from'))
+                ->setZipTo($this->extractFormData($formData, 'zip_to'))->setZipIsRange(1);
+        }
+
+        if (isset($formData['title'])) {
+            $titles = [];
+            foreach ($formData['title'] as $storeId => $value) {
+                $titles[] = $this->taxRateTitleDataObjectFactory->create()->setStoreId($storeId)->setValue($value);
+            }
+            $taxRate->setTitles($titles);
+        }
+
+        return $taxRate;
+    }
+
+    /**
+     * Determines if an array value is set in the form data array and returns it.
+     *
+     * @param array $formData the form to get data from
+     * @param string $fieldName the key
+     * @return null|string
+     */
+    protected function extractFormData($formData, $fieldName)
+    {
+        if (isset($formData[$fieldName])) {
+            return $formData[$fieldName];
+        }
+        return null;
+    }
 }
diff --git a/app/code/Magento/Tax/Model/Calculation/Rate/Title.php b/app/code/Magento/Tax/Model/Calculation/Rate/Title.php
index a98b220548940e240774250f868c8b1abb8c4f1d..141e62435538b94a97d91671966161b6bf174b7c 100644
--- a/app/code/Magento/Tax/Model/Calculation/Rate/Title.php
+++ b/app/code/Magento/Tax/Model/Calculation/Rate/Title.php
@@ -17,9 +17,16 @@ namespace Magento\Tax\Model\Calculation\Rate;
 
 use Magento\Tax\Api\Data\TaxRateTitleInterface;
 
-class Title extends \Magento\Framework\Model\AbstractExtensibleModel implements
-    \Magento\Tax\Api\Data\TaxRateTitleInterface
+class Title extends \Magento\Framework\Model\AbstractExtensibleModel implements TaxRateTitleInterface
 {
+    /**#@+
+     *
+     * Tax rate field key.
+     */
+    const KEY_STORE_ID = 'store_id';
+    const KEY_VALUE_ID = 'value';
+    /**#@-*/
+
     /**
      * @return void
      */
diff --git a/app/code/Magento/Tax/Model/Calculation/RateRepository.php b/app/code/Magento/Tax/Model/Calculation/RateRepository.php
index a185acd15844412198f779b0efd4dc58e224a7fa..6f099ea27ae35038fac754ea19d97066355c7b47 100644
--- a/app/code/Magento/Tax/Model/Calculation/RateRepository.php
+++ b/app/code/Magento/Tax/Model/Calculation/RateRepository.php
@@ -15,7 +15,7 @@ use Magento\Framework\Api\SortOrder;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Exception\LocalizedException;
 use Magento\Framework\Exception\AlreadyExistsException;
-use Magento\Tax\Api\Data\TaxRateInterface as TaxRateDataObject;
+use Magento\Tax\Model\Calculation\Rate;
 use Magento\Tax\Model\Calculation\Rate\Converter;
 use Magento\Tax\Model\Resource\Calculation\Rate\Collection;
 
@@ -210,7 +210,7 @@ class RateRepository implements \Magento\Tax\Api\TaxRateRepositoryInterface
     protected function translateField($field)
     {
         switch ($field) {
-            case TaxRateDataObject::KEY_REGION_NAME:
+            case Rate::KEY_REGION_NAME:
                 return 'region_table.code';
             default:
                 return "main_table." . $field;
diff --git a/app/code/Magento/Tax/Model/Calculation/Rule.php b/app/code/Magento/Tax/Model/Calculation/Rule.php
index 13d287ce5da36b67ece5d16a634a1ba3a743ca44..dc7e0cfac6f2c4932e6c1515b277483e78e88a7c 100644
--- a/app/code/Magento/Tax/Model/Calculation/Rule.php
+++ b/app/code/Magento/Tax/Model/Calculation/Rule.php
@@ -21,21 +21,14 @@ class Rule extends \Magento\Framework\Model\AbstractExtensibleModel implements T
      *
      * Tax rule field key.
      */
-    const KEY_ID = 'id';
-
-    const KEY_CODE = 'code';
-
+    const KEY_ID       = 'id';
+    const KEY_CODE     = 'code';
     const KEY_PRIORITY = 'priority';
-
     const KEY_POSITION = 'position';
-
     const KEY_CUSTOMER_TAX_CLASS_IDS = 'customer_tax_class_ids';
-
-    const KEY_PRODUCT_TAX_CLASS_IDS = 'product_tax_class_ids';
-
-    const KEY_TAX_RATE_IDS = 'tax_rate_ids';
-
-    const KEY_CALCULATE_SUBTOTAL = 'calculate_subtotal';
+    const KEY_PRODUCT_TAX_CLASS_IDS  = 'product_tax_class_ids';
+    const KEY_TAX_RATE_IDS           = 'tax_rate_ids';
+    const KEY_CALCULATE_SUBTOTAL     = 'calculate_subtotal';
     /**#@-*/
 
     /**
diff --git a/app/code/Magento/Tax/Model/ClassModel.php b/app/code/Magento/Tax/Model/ClassModel.php
index 603fc708d47286e0dffdd25f8724756445f6cac1..610527d58eb625ed32510e99dabbb11d58af3bc8 100644
--- a/app/code/Magento/Tax/Model/ClassModel.php
+++ b/app/code/Magento/Tax/Model/ClassModel.php
@@ -19,6 +19,14 @@ use Magento\Tax\Api\Data\TaxClassInterface;
 class ClassModel extends \Magento\Framework\Model\AbstractExtensibleModel implements
     \Magento\Tax\Api\Data\TaxClassInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_ID   = 'class_id';
+    const KEY_NAME = 'class_name';
+    const KEY_TYPE = 'class_type';
+    /**#@-*/
+
     /**
      * Defines Customer Tax Class string
      */
diff --git a/app/code/Magento/Tax/Model/ClassModelRegistry.php b/app/code/Magento/Tax/Model/ClassModelRegistry.php
index e4667c061776813e7fa833bae372904527505eb5..da438fcbdf3d55ddb4d5127a36b89d402ec2e4cc 100644
--- a/app/code/Magento/Tax/Model/ClassModelRegistry.php
+++ b/app/code/Magento/Tax/Model/ClassModelRegistry.php
@@ -7,7 +7,6 @@
 namespace Magento\Tax\Model;
 
 use Magento\Framework\Exception\NoSuchEntityException;
-use Magento\Tax\Api\Data\TaxClassInterface;
 use Magento\Tax\Model\ClassModel as TaxClassModel;
 use Magento\Tax\Model\ClassModelFactory as TaxClassModelFactory;
 
@@ -67,7 +66,7 @@ class ClassModelRegistry
         $taxClassModel = $this->taxClassModelFactory->create()->load($taxClassId);
         if (!$taxClassModel->getId()) {
             // tax class does not exist
-            throw NoSuchEntityException::singleField(TaxClassInterface::KEY_ID, $taxClassId);
+            throw NoSuchEntityException::singleField(TaxClassModel::KEY_ID, $taxClassId);
         }
         $this->taxClassRegistryById[$taxClassModel->getId()] = $taxClassModel;
         return $taxClassModel;
diff --git a/app/code/Magento/Tax/Model/Config/Notification.php b/app/code/Magento/Tax/Model/Config/Notification.php
index c886452442406056cb369dd15210a1c32b0c9829..f4b395a290856cf26c36fae743ac7a3ce9797c9b 100644
--- a/app/code/Magento/Tax/Model/Config/Notification.php
+++ b/app/code/Magento/Tax/Model/Config/Notification.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Tax\Model\Config;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 /**
  * Tax Config Notification
  */
@@ -59,7 +61,7 @@ class Notification extends \Magento\Framework\App\Config\Value
      */
     protected function _resetNotificationFlag($path)
     {
-        $this->resourceConfig->saveConfig($path, 0, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0);
+        $this->resourceConfig->saveConfig($path, 0, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0);
         return $this;
     }
 }
diff --git a/app/code/Magento/Tax/Model/Rate/Source.php b/app/code/Magento/Tax/Model/Rate/Source.php
index 8e4342ca28f161887bfc6aecf3e1d1f6ccb41b44..0d999d6698b6ba76630b3d81a815c8649e466cb5 100644
--- a/app/code/Magento/Tax/Model/Rate/Source.php
+++ b/app/code/Magento/Tax/Model/Rate/Source.php
@@ -8,8 +8,8 @@ namespace Magento\Tax\Model\Rate;
 
 use Magento\Framework\Api\SearchCriteriaBuilder;
 use Magento\Framework\Convert\Object as Converter;
-use Magento\Tax\Api\Data\TaxRateInterface as TaxRate;
 use Magento\Tax\Api\TaxRateRepositoryInterface;
+use Magento\Tax\Model\Calculation\Rate;
 
 /**
  * Tax rate source model.
@@ -57,8 +57,8 @@ class Source implements \Magento\Framework\Data\OptionSourceInterface
             $searchResults = $this->taxRateRepository->getList($searchCriteria);
             $this->options = $this->converter->toOptionArray(
                 $searchResults->getItems(),
-                TaxRate::KEY_ID,
-                TaxRate::KEY_CODE
+                Rate::KEY_ID,
+                Rate::KEY_CODE
             );
         }
         return $this->options;
diff --git a/app/code/Magento/Tax/Model/Sales/Order/Details.php b/app/code/Magento/Tax/Model/Sales/Order/Details.php
index 5161ab9fe6f7397f08cb88972033a8a6be6f6ef4..0294aff0f68eeef1f3e903a3ecbc85246d4fd3c9 100644
--- a/app/code/Magento/Tax/Model/Sales/Order/Details.php
+++ b/app/code/Magento/Tax/Model/Sales/Order/Details.php
@@ -13,6 +13,13 @@ namespace Magento\Tax\Model\Sales\Order;
 class Details extends \Magento\Framework\Model\AbstractExtensibleModel implements
     \Magento\Tax\Api\Data\OrderTaxDetailsInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_APPLIED_TAXES = 'applied_taxes';
+    const KEY_ITEMS         = 'items';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
diff --git a/app/code/Magento/Tax/Model/Sales/Order/Tax.php b/app/code/Magento/Tax/Model/Sales/Order/Tax.php
index 29ce7e404f5696bcff549d3b276f6b14f6aea1d2..3b67f9222800f2f55585bff22409966003799d2e 100644
--- a/app/code/Magento/Tax/Model/Sales/Order/Tax.php
+++ b/app/code/Magento/Tax/Model/Sales/Order/Tax.php
@@ -25,6 +25,16 @@ namespace Magento\Tax\Model\Sales\Order;
 class Tax extends \Magento\Framework\Model\AbstractExtensibleModel implements
     \Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_CODE        = 'code';
+    const KEY_TITLE       = 'title';
+    const KEY_PERCENT     = 'percent';
+    const KEY_AMOUNT      = 'amount';
+    const KEY_BASE_AMOUNT = 'base_amount';
+    /**#@-*/
+
     /**
      * @return void
      */
diff --git a/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php b/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php
index a6b6148ed0c9eaca83d07b74c4d32f53ce3a1e74..8ecadf3eec08696f763a1f72ea1bbfa256dcd575 100644
--- a/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php
+++ b/app/code/Magento/Tax/Model/Sales/Order/Tax/Item.php
@@ -11,6 +11,15 @@ namespace Magento\Tax\Model\Sales\Order\Tax;
 class Item extends \Magento\Framework\Model\AbstractExtensibleModel implements
     \Magento\Tax\Api\Data\OrderTaxDetailsItemInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_TYPE               = 'type';
+    const KEY_ITEM_ID            = 'item_id';
+    const KEY_ASSOCIATED_ITEM_ID = 'associated_item_id';
+    const KEY_APPLIED_TAXES      = 'applied_taxes';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
diff --git a/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php b/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php
index c665dfd00e8f8a65f487faacb618c1475991b10b..fed06b4dd1983da04c39ff4625bf22c0626a4707 100644
--- a/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php
+++ b/app/code/Magento/Tax/Model/Sales/Order/TaxManagement.php
@@ -10,7 +10,8 @@ namespace Magento\Tax\Model\Sales\Order;
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterfaceFactory as TaxDetailsDataObjectFactory;
 use Magento\Tax\Api\Data\OrderTaxDetailsAppliedTaxInterface as AppliedTax;
-use Magento\Tax\Api\Data\OrderTaxDetailsItemInterface as Item;
+use Magento\Tax\Model\Sales\Order\Tax;
+use Magento\Tax\Model\Sales\Order\Tax\Item;
 
 class TaxManagement implements \Magento\Tax\Api\OrderTaxManagementInterface
 {
@@ -83,7 +84,7 @@ class TaxManagement implements \Magento\Tax\Api\OrderTaxManagementInterface
      * Aggregate item applied taxes to get order applied taxes
      *
      * @param TaxDetailsDataObjectFactory $appliedTaxDataObjectFactory
-     * @param Item[] $items
+     * @param \Magento\Tax\Api\Data\OrderTaxDetailsItemInterface[] $items
      * @return AppliedTax[]
      */
     protected function aggregateAppliedTaxes(TaxDetailsDataObjectFactory $appliedTaxDataObjectFactory, $items)
@@ -96,25 +97,25 @@ class TaxManagement implements \Magento\Tax\Api\OrderTaxManagementInterface
                 $code = $itemAppliedTax->getCode();
                 if (!isset($orderAppliedTaxesData[$code])) {
                     $orderAppliedTaxesData[$code] = [
-                        AppliedTax::KEY_CODE => $code,
-                        AppliedTax::KEY_TITLE => $itemAppliedTax->getTitle(),
-                        AppliedTax::KEY_PERCENT => $itemAppliedTax->getPercent(),
-                        AppliedTax::KEY_AMOUNT => $itemAppliedTax->getAmount(),
-                        AppliedTax::KEY_BASE_AMOUNT => $itemAppliedTax->getBaseAmount(),
+                        Tax::KEY_CODE => $code,
+                        Tax::KEY_TITLE => $itemAppliedTax->getTitle(),
+                        Tax::KEY_PERCENT => $itemAppliedTax->getPercent(),
+                        Tax::KEY_AMOUNT => $itemAppliedTax->getAmount(),
+                        Tax::KEY_BASE_AMOUNT => $itemAppliedTax->getBaseAmount(),
                     ];
                 } else {
-                    $orderAppliedTaxesData[$code][AppliedTax::KEY_AMOUNT] += $itemAppliedTax->getAmount();
-                    $orderAppliedTaxesData[$code][AppliedTax::KEY_BASE_AMOUNT] += $itemAppliedTax->getBaseAmount();
+                    $orderAppliedTaxesData[$code][Tax::KEY_AMOUNT] += $itemAppliedTax->getAmount();
+                    $orderAppliedTaxesData[$code][Tax::KEY_BASE_AMOUNT] += $itemAppliedTax->getBaseAmount();
                 }
             }
         }
         foreach ($orderAppliedTaxesData as $orderAppliedTaxData) {
             $orderAppliedTaxes[] = $appliedTaxDataObjectFactory->create()
-                ->setCode($orderAppliedTaxData[AppliedTax::KEY_CODE])
-                ->setTitle($orderAppliedTaxData[AppliedTax::KEY_TITLE])
-                ->setPercent($orderAppliedTaxData[AppliedTax::KEY_PERCENT])
-                ->setAmount($orderAppliedTaxData[AppliedTax::KEY_AMOUNT])
-                ->setBaseAmount($orderAppliedTaxData[AppliedTax::KEY_BASE_AMOUNT]);
+                ->setCode($orderAppliedTaxData[Tax::KEY_CODE])
+                ->setTitle($orderAppliedTaxData[Tax::KEY_TITLE])
+                ->setPercent($orderAppliedTaxData[Tax::KEY_PERCENT])
+                ->setAmount($orderAppliedTaxData[Tax::KEY_AMOUNT])
+                ->setBaseAmount($orderAppliedTaxData[Tax::KEY_BASE_AMOUNT]);
         }
         return $orderAppliedTaxes;
     }
diff --git a/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php b/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php
index 71d18024ea5f1a199b99ff3811fa4d82d7009ed7..cd893b122f89381b05baa4b5cc41e59d120bd40b 100644
--- a/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php
+++ b/app/code/Magento/Tax/Model/Sales/Quote/ItemDetails.php
@@ -13,12 +13,28 @@ use Magento\Tax\Api\Data\QuoteDetailsItemInterface;
  */
 class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_CODE                 = 'code';
+    const KEY_TYPE                 = 'type';
+    const KEY_TAX_CLASS_KEY        = 'tax_class_key';
+    const KEY_UNIT_PRICE           = 'unit_price';
+    const KEY_QUANTITY             = 'quantity';
+    const KEY_TAX_INCLUDED         = 'tax_included';
+    const KEY_SHORT_DESCRIPTION    = 'short_description';
+    const KEY_DISCOUNT_AMOUNT      = 'discount_amount';
+    const KEY_PARENT_CODE          = 'parent_code';
+    const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code';
+    const KEY_TAX_CLASS_ID         = 'tax_class_id';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
     public function getCode()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_CODE);
+        return $this->getData(self::KEY_CODE);
     }
 
     /**
@@ -26,7 +42,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getType()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_TYPE);
+        return $this->getData(self::KEY_TYPE);
     }
 
     /**
@@ -34,7 +50,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getTaxClassKey()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_TAX_CLASS_KEY);
+        return $this->getData(self::KEY_TAX_CLASS_KEY);
     }
 
     /**
@@ -42,7 +58,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getUnitPrice()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_UNIT_PRICE);
+        return $this->getData(self::KEY_UNIT_PRICE);
     }
 
     /**
@@ -50,7 +66,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getQuantity()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_QUANTITY);
+        return $this->getData(self::KEY_QUANTITY);
     }
 
     /**
@@ -58,7 +74,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getTaxIncluded()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_TAX_INCLUDED);
+        return $this->getData(self::KEY_TAX_INCLUDED);
     }
 
     /**
@@ -66,7 +82,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getShortDescription()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_SHORT_DESCRIPTION);
+        return $this->getData(self::KEY_SHORT_DESCRIPTION);
     }
 
     /**
@@ -74,7 +90,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getDiscountAmount()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_DISCOUNT_AMOUNT);
+        return $this->getData(self::KEY_DISCOUNT_AMOUNT);
     }
 
     /**
@@ -82,7 +98,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getParentCode()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_PARENT_CODE);
+        return $this->getData(self::KEY_PARENT_CODE);
     }
 
     /**
@@ -90,7 +106,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getAssociatedItemCode()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE);
+        return $this->getData(self::KEY_ASSOCIATED_ITEM_CODE);
     }
 
     /**
@@ -98,7 +114,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function getTaxClassId()
     {
-        return $this->getData(QuoteDetailsItemInterface::KEY_TAX_CLASS_ID);
+        return $this->getData(self::KEY_TAX_CLASS_ID);
     }
 
     /**
@@ -109,7 +125,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setCode($code)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_CODE, $code);
+        return $this->setData(self::KEY_CODE, $code);
     }
 
     /**
@@ -120,7 +136,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setType($type)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_TYPE, $type);
+        return $this->setData(self::KEY_TYPE, $type);
     }
 
     /**
@@ -131,7 +147,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setTaxClassKey(\Magento\Tax\Api\Data\TaxClassKeyInterface $taxClassKey = null)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_TAX_CLASS_KEY, $taxClassKey);
+        return $this->setData(self::KEY_TAX_CLASS_KEY, $taxClassKey);
     }
 
     /**
@@ -142,7 +158,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setUnitPrice($unitPrice)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_UNIT_PRICE, $unitPrice);
+        return $this->setData(self::KEY_UNIT_PRICE, $unitPrice);
     }
 
     /**
@@ -153,7 +169,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setQuantity($quantity)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_QUANTITY, $quantity);
+        return $this->setData(self::KEY_QUANTITY, $quantity);
     }
 
     /**
@@ -164,7 +180,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setIsTaxIncluded($isTaxIncluded)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_TAX_INCLUDED, $isTaxIncluded);
+        return $this->setData(self::KEY_TAX_INCLUDED, $isTaxIncluded);
     }
 
     /**
@@ -175,7 +191,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setShortDescription($shortDescription)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_SHORT_DESCRIPTION, $shortDescription);
+        return $this->setData(self::KEY_SHORT_DESCRIPTION, $shortDescription);
     }
 
     /**
@@ -186,7 +202,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setDiscountAmount($discountAmount)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_DISCOUNT_AMOUNT, $discountAmount);
+        return $this->setData(self::KEY_DISCOUNT_AMOUNT, $discountAmount);
     }
 
     /**
@@ -197,7 +213,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setParentCode($parentCode)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_PARENT_CODE, $parentCode);
+        return $this->setData(self::KEY_PARENT_CODE, $parentCode);
     }
 
     /**
@@ -208,7 +224,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setAssociatedItemCode($associatedItemCode)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode);
+        return $this->setData(self::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode);
     }
 
     /**
@@ -219,7 +235,7 @@ class ItemDetails extends AbstractExtensibleModel implements QuoteDetailsItemInt
      */
     public function setTaxClassId($taxClassId)
     {
-        return $this->setData(QuoteDetailsItemInterface::KEY_TAX_CLASS_ID, $taxClassId);
+        return $this->setData(self::KEY_TAX_CLASS_ID, $taxClassId);
     }
 
     /**
diff --git a/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php b/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php
index 1ef369ba40d82dfb3655926be7970e003892a152..1569e8496bd0cb9417b6f0609df658f06f21a0ed 100644
--- a/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php
+++ b/app/code/Magento/Tax/Model/Sales/Quote/QuoteDetails.php
@@ -13,12 +13,23 @@ use Magento\Tax\Api\Data\QuoteDetailsInterface;
  */
 class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_BILLING_ADDRESS        = 'billing_address';
+    const KEY_SHIPPING_ADDRESS       = 'shipping_address';
+    const KEY_CUSTOMER_TAX_CLASS_KEY = 'customer_tax_class_key';
+    const KEY_ITEMS                  = 'items';
+    const KEY_CUSTOMER_TAX_CLASS_ID  = 'customer_tax_class_id';
+    const KEY_CUSTOMER_ID            = 'customer_id';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
     public function getBillingAddress()
     {
-        return $this->getData(QuoteDetailsInterface::KEY_BILLING_ADDRESS);
+        return $this->getData(self::KEY_BILLING_ADDRESS);
     }
 
     /**
@@ -26,7 +37,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function getShippingAddress()
     {
-        return $this->getData(QuoteDetailsInterface::KEY_SHIPPING_ADDRESS);
+        return $this->getData(self::KEY_SHIPPING_ADDRESS);
     }
 
     /**
@@ -34,7 +45,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function getCustomerTaxClassKey()
     {
-        return $this->getData(QuoteDetailsInterface::KEY_CUSTOMER_TAX_CLASS_KEY);
+        return $this->getData(self::KEY_CUSTOMER_TAX_CLASS_KEY);
     }
 
     /**
@@ -42,7 +53,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function getCustomerId()
     {
-        return $this->getData(QuoteDetailsInterface::KEY_CUSTOMER_ID);
+        return $this->getData(self::KEY_CUSTOMER_ID);
     }
 
     /**
@@ -50,7 +61,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function getItems()
     {
-        return $this->getData(QuoteDetailsInterface::KEY_ITEMS);
+        return $this->getData(self::KEY_ITEMS);
     }
 
     /**
@@ -58,7 +69,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function getCustomerTaxClassId()
     {
-        return $this->getData(QuoteDetailsInterface::CUSTOMER_TAX_CLASS_ID);
+        return $this->getData(self::KEY_CUSTOMER_TAX_CLASS_ID);
     }
 
     /**
@@ -69,7 +80,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function setBillingAddress(\Magento\Customer\Api\Data\AddressInterface $billingAddress = null)
     {
-        return $this->setData(QuoteDetailsInterface::KEY_BILLING_ADDRESS, $billingAddress);
+        return $this->setData(self::KEY_BILLING_ADDRESS, $billingAddress);
     }
 
     /**
@@ -80,7 +91,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function setShippingAddress(\Magento\Customer\Api\Data\AddressInterface $shippingAddress = null)
     {
-        return $this->setData(QuoteDetailsInterface::KEY_SHIPPING_ADDRESS, $shippingAddress);
+        return $this->setData(self::KEY_SHIPPING_ADDRESS, $shippingAddress);
     }
 
     /**
@@ -91,7 +102,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function setCustomerTaxClassKey(\Magento\Tax\Api\Data\TaxClassKeyInterface $customerTaxClassKey = null)
     {
-        return $this->setData(QuoteDetailsInterface::KEY_CUSTOMER_TAX_CLASS_KEY, $customerTaxClassKey);
+        return $this->setData(self::KEY_CUSTOMER_TAX_CLASS_KEY, $customerTaxClassKey);
     }
 
     /**
@@ -102,7 +113,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function setCustomerId($customerId)
     {
-        return $this->setData(QuoteDetailsInterface::KEY_CUSTOMER_ID, $customerId);
+        return $this->setData(self::KEY_CUSTOMER_ID, $customerId);
     }
 
     /**
@@ -113,7 +124,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function setItems(array $items = null)
     {
-        return $this->setData(QuoteDetailsInterface::KEY_ITEMS, $items);
+        return $this->setData(self::KEY_ITEMS, $items);
     }
 
     /**
@@ -124,7 +135,7 @@ class QuoteDetails extends AbstractExtensibleModel implements QuoteDetailsInterf
      */
     public function setCustomerTaxClassId($customerTaxClassId)
     {
-        return $this->setData(QuoteDetailsInterface::CUSTOMER_TAX_CLASS_ID, $customerTaxClassId);
+        return $this->setData(self::KEY_CUSTOMER_TAX_CLASS_ID, $customerTaxClassId);
     }
 
     /**
diff --git a/app/code/Magento/Tax/Model/TaxCalculation.php b/app/code/Magento/Tax/Model/TaxCalculation.php
index fdbc6cb724bfe1bf27df13375ba11d8ff29af87e..579ee5519899603976a08b1aeb874a59f1f8d774 100644
--- a/app/code/Magento/Tax/Model/TaxCalculation.php
+++ b/app/code/Magento/Tax/Model/TaxCalculation.php
@@ -6,19 +6,19 @@
 
 namespace Magento\Tax\Model;
 
-use Magento\Tax\Api\Data\TaxDetailsInterface;
+use Magento\Tax\Api\TaxCalculationInterface;
+use Magento\Tax\Api\TaxClassManagementInterface;
 use Magento\Tax\Api\Data\TaxDetailsItemInterface;
 use Magento\Tax\Api\Data\QuoteDetailsItemInterface;
 use Magento\Tax\Api\Data\TaxDetailsInterfaceFactory;
 use Magento\Tax\Api\Data\TaxDetailsItemInterfaceFactory;
-use Magento\Tax\Api\Data\AppliedTaxRateInterface;
-use Magento\Tax\Api\Data\AppliedTaxInterface;
-use Magento\Tax\Api\TaxClassManagementInterface;
+use Magento\Tax\Model\Calculation\AbstractCalculator;
 use Magento\Tax\Model\Calculation\CalculatorFactory;
 use Magento\Tax\Model\Config;
-use Magento\Tax\Model\Calculation\AbstractCalculator;
+use Magento\Tax\Model\TaxDetails\AppliedTax;
+use Magento\Tax\Model\TaxDetails\AppliedTaxRate;
+use Magento\Tax\Model\TaxDetails\TaxDetails;
 use Magento\Store\Model\StoreManagerInterface;
-use Magento\Tax\Api\TaxCalculationInterface;
 
 /**
  * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -142,11 +142,11 @@ class TaxCalculation implements TaxCalculationInterface
 
         // initial TaxDetails data
         $taxDetailsData = [
-            TaxDetailsInterface::KEY_SUBTOTAL => 0.0,
-            TaxDetailsInterface::KEY_TAX_AMOUNT => 0.0,
-            TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0,
-            TaxDetailsInterface::KEY_APPLIED_TAXES => [],
-            TaxDetailsInterface::KEY_ITEMS => [],
+            TaxDetails::KEY_SUBTOTAL => 0.0,
+            TaxDetails::KEY_TAX_AMOUNT => 0.0,
+            TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0,
+            TaxDetails::KEY_APPLIED_TAXES => [],
+            TaxDetails::KEY_ITEMS => [],
         ];
         $items = $quoteDetails->getItems();
         if (empty($items)) {
@@ -327,21 +327,21 @@ class TaxCalculation implements TaxCalculationInterface
      */
     protected function aggregateItemData($taxDetailsData, TaxDetailsItemInterface $item)
     {
-        $taxDetailsData[TaxDetailsInterface::KEY_SUBTOTAL]
-            = $taxDetailsData[TaxDetailsInterface::KEY_SUBTOTAL] + $item->getRowTotal();
+        $taxDetailsData[TaxDetails::KEY_SUBTOTAL]
+            = $taxDetailsData[TaxDetails::KEY_SUBTOTAL] + $item->getRowTotal();
 
-        $taxDetailsData[TaxDetailsInterface::KEY_TAX_AMOUNT]
-            = $taxDetailsData[TaxDetailsInterface::KEY_TAX_AMOUNT] + $item->getRowTax();
+        $taxDetailsData[TaxDetails::KEY_TAX_AMOUNT]
+            = $taxDetailsData[TaxDetails::KEY_TAX_AMOUNT] + $item->getRowTax();
 
-        $taxDetailsData[TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT] =
-            $taxDetailsData[TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT]
+        $taxDetailsData[TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT] =
+            $taxDetailsData[TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT]
             + $item->getDiscountTaxCompensationAmount();
 
         $itemAppliedTaxes = $item->getAppliedTaxes();
         if ($itemAppliedTaxes === null) {
             return $taxDetailsData;
         }
-        $appliedTaxes = $taxDetailsData[TaxDetailsInterface::KEY_APPLIED_TAXES];
+        $appliedTaxes = $taxDetailsData[TaxDetails::KEY_APPLIED_TAXES];
         foreach ($itemAppliedTaxes as $taxId => $itemAppliedTax) {
             if (!isset($appliedTaxes[$taxId])) {
                 //convert rate data object to array
@@ -349,23 +349,23 @@ class TaxCalculation implements TaxCalculationInterface
                 $rateDataObjects = $itemAppliedTax->getRates();
                 foreach ($rateDataObjects as $rateDataObject) {
                     $rates[$rateDataObject->getCode()] = [
-                        AppliedTaxRateInterface::KEY_CODE => $rateDataObject->getCode(),
-                        AppliedTaxRateInterface::KEY_TITLE => $rateDataObject->getTitle(),
-                        AppliedTaxRateInterface::KEY_PERCENT => $rateDataObject->getPercent(),
+                        AppliedTaxRate::KEY_CODE => $rateDataObject->getCode(),
+                        AppliedTaxRate::KEY_TITLE => $rateDataObject->getTitle(),
+                        AppliedTaxRate::KEY_PERCENT => $rateDataObject->getPercent(),
                     ];
                 }
                 $appliedTaxes[$taxId] = [
-                    AppliedTaxInterface::KEY_AMOUNT => $itemAppliedTax->getAmount(),
-                    AppliedTaxInterface::KEY_PERCENT => $itemAppliedTax->getPercent(),
-                    AppliedTaxInterface::KEY_RATES => $rates,
-                    AppliedTaxInterface::KEY_TAX_RATE_KEY => $itemAppliedTax->getTaxRateKey(),
+                    AppliedTax::KEY_AMOUNT => $itemAppliedTax->getAmount(),
+                    AppliedTax::KEY_PERCENT => $itemAppliedTax->getPercent(),
+                    AppliedTax::KEY_RATES => $rates,
+                    AppliedTax::KEY_TAX_RATE_KEY => $itemAppliedTax->getTaxRateKey(),
                 ];
             } else {
-                $appliedTaxes[$taxId][AppliedTaxInterface::KEY_AMOUNT] += $itemAppliedTax->getAmount();
+                $appliedTaxes[$taxId][AppliedTax::KEY_AMOUNT] += $itemAppliedTax->getAmount();
             }
         }
 
-        $taxDetailsData[TaxDetailsInterface::KEY_APPLIED_TAXES] = $appliedTaxes;
+        $taxDetailsData[TaxDetails::KEY_APPLIED_TAXES] = $appliedTaxes;
         return $taxDetailsData;
     }
 
diff --git a/app/code/Magento/Tax/Model/TaxClass/Key.php b/app/code/Magento/Tax/Model/TaxClass/Key.php
index 085b018aa88f7ad8ba4ef5a36fea99c31f6ded54..c882d2dae170cfa9589909859f8b0eb995a41d01 100644
--- a/app/code/Magento/Tax/Model/TaxClass/Key.php
+++ b/app/code/Magento/Tax/Model/TaxClass/Key.php
@@ -13,12 +13,19 @@ use Magento\Tax\Api\Data\TaxClassKeyInterface;
  */
 class Key extends AbstractExtensibleModel implements TaxClassKeyInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_TYPE  = 'type';
+    const KEY_VALUE = 'value';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
     public function getType()
     {
-        return $this->getData(TaxClassKeyInterface::KEY_TYPE);
+        return $this->getData(self::KEY_TYPE);
     }
 
     /**
@@ -26,7 +33,7 @@ class Key extends AbstractExtensibleModel implements TaxClassKeyInterface
      */
     public function getValue()
     {
-        return $this->getData(TaxClassKeyInterface::KEY_VALUE);
+        return $this->getData(self::KEY_VALUE);
     }
 
     /**
@@ -37,7 +44,7 @@ class Key extends AbstractExtensibleModel implements TaxClassKeyInterface
      */
     public function setType($type)
     {
-        return $this->setData(TaxClassKeyInterface::KEY_TYPE, $type);
+        return $this->setData(self::KEY_TYPE, $type);
     }
 
     /**
@@ -48,7 +55,7 @@ class Key extends AbstractExtensibleModel implements TaxClassKeyInterface
      */
     public function setValue($value)
     {
-        return $this->setData(TaxClassKeyInterface::KEY_VALUE, $value);
+        return $this->setData(self::KEY_VALUE, $value);
     }
 
     /**
diff --git a/app/code/Magento/Tax/Model/TaxClass/Management.php b/app/code/Magento/Tax/Model/TaxClass/Management.php
index 679a054b62314f379cfbc94ec0b0633a1142c059..eb7871c1086c22efe4e988254490bf02abdebf48 100644
--- a/app/code/Magento/Tax/Model/TaxClass/Management.php
+++ b/app/code/Magento/Tax/Model/TaxClass/Management.php
@@ -9,8 +9,8 @@ namespace Magento\Tax\Model\TaxClass;
 
 use Magento\Framework\Api\FilterBuilder;
 use Magento\Framework\Api\SearchCriteriaBuilder;
-use Magento\Tax\Api\Data\TaxClassInterface;
 use Magento\Tax\Api\Data\TaxClassKeyInterface;
+use Magento\Tax\Model\ClassModel;
 
 class Management implements \Magento\Tax\Api\TaxClassManagementInterface
 {
@@ -61,10 +61,10 @@ class Management implements \Magento\Tax\Api\TaxClassManagementInterface
                     return $taxClassKey->getValue();
                 case TaxClassKeyInterface::TYPE_NAME:
                     $searchCriteria = $this->searchCriteriaBuilder->addFilter(
-                        [$this->filterBuilder->setField(TaxClassInterface::KEY_TYPE)->setValue($taxClassType)->create()]
+                        [$this->filterBuilder->setField(ClassModel::KEY_TYPE)->setValue($taxClassType)->create()]
                     )->addFilter(
                         [
-                            $this->filterBuilder->setField(TaxClassInterface::KEY_NAME)
+                            $this->filterBuilder->setField(ClassModel::KEY_NAME)
                                 ->setValue($taxClassKey->getValue())
                                 ->create(),
                         ]
diff --git a/app/code/Magento/Tax/Model/TaxClass/Repository.php b/app/code/Magento/Tax/Model/TaxClass/Repository.php
index a0fc6fe76d46ec8718a7cccf2af8a44068b73425..01dd656b0d5e51d5b64e22c8fd4d80e937d19e2b 100644
--- a/app/code/Magento/Tax/Model/TaxClass/Repository.php
+++ b/app/code/Magento/Tax/Model/TaxClass/Repository.php
@@ -15,8 +15,8 @@ use Magento\Framework\Api\SortOrder;
 use Magento\Framework\Exception\CouldNotDeleteException;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Exception\LocalizedException as ModelException;
-use Magento\Tax\Api\Data\TaxClassInterface;
 use Magento\Tax\Api\TaxClassManagementInterface;
+use Magento\Tax\Model\ClassModel;
 use Magento\Tax\Model\ClassModelRegistry;
 use Magento\Tax\Model\Resource\TaxClass\Collection as TaxClassCollection;
 use Magento\Tax\Model\Resource\TaxClass\CollectionFactory as TaxClassCollectionFactory;
@@ -164,19 +164,19 @@ class Repository implements \Magento\Tax\Api\TaxClassRepositoryInterface
         $exception = new InputException();
 
         if (!\Zend_Validate::is(trim($taxClass->getClassName()), 'NotEmpty')) {
-            $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => TaxClassInterface::KEY_NAME]));
+            $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => ClassModel::KEY_NAME]));
         }
 
         $classType = $taxClass->getClassType();
         if (!\Zend_Validate::is(trim($classType), 'NotEmpty')) {
-            $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => TaxClassInterface::KEY_TYPE]));
+            $exception->addError(__(InputException::REQUIRED_FIELD, ['fieldName' => ClassModel::KEY_TYPE]));
         } elseif ($classType !== TaxClassManagementInterface::TYPE_CUSTOMER
             && $classType !== TaxClassManagementInterface::TYPE_PRODUCT
         ) {
             $exception->addError(
                 __(
                     InputException::INVALID_FIELD_VALUE,
-                    ['fieldName' => TaxClassInterface::KEY_TYPE, 'value' => $classType]
+                    ['fieldName' => ClassModel::KEY_TYPE, 'value' => $classType]
                 )
             );
         }
diff --git a/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php b/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php
index 4a52c81b669fb5a3a13b775ab60686f18ecb5c99..ca0dd21abdd84a2714adf6c8eb26aa89c83584de 100644
--- a/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php
+++ b/app/code/Magento/Tax/Model/TaxClass/Source/Customer.php
@@ -8,7 +8,7 @@ namespace Magento\Tax\Model\TaxClass\Source;
 
 use Magento\Framework\Exception\StateException;
 use Magento\Tax\Api\TaxClassManagementInterface;
-use Magento\Tax\Api\Data\TaxClassInterface as TaxClass;
+use Magento\Tax\Model\ClassModel;
 
 /**
  * Customer tax class source model.
@@ -57,7 +57,7 @@ class Customer extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource
     {
         if (empty($this->_options)) {
             $options = [];
-            $filter = $this->filterBuilder->setField(TaxClass::KEY_TYPE)
+            $filter = $this->filterBuilder->setField(ClassModel::KEY_TYPE)
                 ->setValue(TaxClassManagementInterface::TYPE_CUSTOMER)
                 ->create();
             $searchCriteria = $this->searchCriteriaBuilder->addFilter([$filter])->create();
diff --git a/app/code/Magento/Tax/Model/TaxClass/Source/Product.php b/app/code/Magento/Tax/Model/TaxClass/Source/Product.php
index bf8711f93228abcfec3c8c6836ff0dd3847a4b23..37844ab57076c71cfc5fb8c57010de4db1fe5444 100644
--- a/app/code/Magento/Tax/Model/TaxClass/Source/Product.php
+++ b/app/code/Magento/Tax/Model/TaxClass/Source/Product.php
@@ -7,8 +7,8 @@
 namespace Magento\Tax\Model\TaxClass\Source;
 
 use Magento\Framework\DB\Ddl\Table;
-use Magento\Tax\Api\Data\TaxClassInterface as TaxClass;
 use Magento\Tax\Api\TaxClassManagementInterface;
+use Magento\Tax\Model\ClassModel;
 
 /**
  * Product tax class source model.
@@ -68,7 +68,7 @@ class Product extends \Magento\Eav\Model\Entity\Attribute\Source\AbstractSource
     {
         if (!$this->_options) {
             $filter = $this->_filterBuilder
-                ->setField(TaxClass::KEY_TYPE)
+                ->setField(ClassModel::KEY_TYPE)
                 ->setValue(TaxClassManagementInterface::TYPE_PRODUCT)
                 ->create();
             $searchCriteria = $this->_searchCriteriaBuilder->addFilter([$filter])->create();
diff --git a/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php b/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php
index 79265cacbc0c8615b0989d9deae57cb4ab6ee405..c5857168feddd83a0280449aadd5bc05c831838e 100644
--- a/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php
+++ b/app/code/Magento/Tax/Model/TaxDetails/AppliedTax.php
@@ -13,12 +13,21 @@ use Magento\Tax\Api\Data\AppliedTaxInterface;
  */
 class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_TAX_RATE_KEY = 'tax_rate_key';
+    const KEY_PERCENT      = 'percent';
+    const KEY_AMOUNT       = 'amount';
+    const KEY_RATES        = 'rates';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
     public function getTaxRateKey()
     {
-        return $this->getData(AppliedTaxInterface::KEY_TAX_RATE_KEY);
+        return $this->getData(self::KEY_TAX_RATE_KEY);
     }
 
     /**
@@ -26,7 +35,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
      */
     public function getPercent()
     {
-        return $this->getData(AppliedTaxInterface::KEY_PERCENT);
+        return $this->getData(self::KEY_PERCENT);
     }
 
     /**
@@ -34,7 +43,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
      */
     public function getAmount()
     {
-        return $this->getData(AppliedTaxInterface::KEY_AMOUNT);
+        return $this->getData(self::KEY_AMOUNT);
     }
 
     /**
@@ -42,7 +51,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
      */
     public function getRates()
     {
-        return $this->getData(AppliedTaxInterface::KEY_RATES);
+        return $this->getData(self::KEY_RATES);
     }
 
     /**
@@ -53,7 +62,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
      */
     public function setTaxRateKey($taxRateKey)
     {
-        return $this->setData(AppliedTaxInterface::KEY_TAX_RATE_KEY, $taxRateKey);
+        return $this->setData(self::KEY_TAX_RATE_KEY, $taxRateKey);
     }
 
     /**
@@ -64,7 +73,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
      */
     public function setPercent($percent)
     {
-        return $this->setData(AppliedTaxInterface::KEY_PERCENT, $percent);
+        return $this->setData(self::KEY_PERCENT, $percent);
     }
 
     /**
@@ -75,7 +84,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
      */
     public function setAmount($amount)
     {
-        return $this->setData(AppliedTaxInterface::KEY_AMOUNT, $amount);
+        return $this->setData(self::KEY_AMOUNT, $amount);
     }
 
     /**
@@ -86,7 +95,7 @@ class AppliedTax extends AbstractExtensibleModel implements AppliedTaxInterface
      */
     public function setRates(array $rates = null)
     {
-        return $this->setData(AppliedTaxInterface::KEY_RATES, $rates);
+        return $this->setData(self::KEY_RATES, $rates);
     }
 
     /**
diff --git a/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php b/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php
index 8cd44cb70e0e36de4ebe281b05e439e18be8312d..a04aed56a7f4a8b6224f628d9786d98a136821f3 100644
--- a/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php
+++ b/app/code/Magento/Tax/Model/TaxDetails/AppliedTaxRate.php
@@ -13,12 +13,20 @@ use Magento\Tax\Api\Data\AppliedTaxRateInterface;
  */
 class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_CODE    = 'code';
+    const KEY_TITLE   = 'title';
+    const KEY_PERCENT = 'percent';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
     public function getCode()
     {
-        return $this->getData(AppliedTaxRateInterface::KEY_CODE);
+        return $this->getData(self::KEY_CODE);
     }
 
     /**
@@ -26,7 +34,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn
      */
     public function getTitle()
     {
-        return $this->getData(AppliedTaxRateInterface::KEY_TITLE);
+        return $this->getData(self::KEY_TITLE);
     }
 
     /**
@@ -34,7 +42,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn
      */
     public function getPercent()
     {
-        return $this->getData(AppliedTaxRateInterface::KEY_PERCENT);
+        return $this->getData(self::KEY_PERCENT);
     }
 
     /**
@@ -45,7 +53,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn
      */
     public function setCode($code)
     {
-        return $this->setData(AppliedTaxRateInterface::KEY_CODE, $code);
+        return $this->setData(self::KEY_CODE, $code);
     }
 
     /**
@@ -56,7 +64,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn
      */
     public function setTitle($title)
     {
-        return $this->setData(AppliedTaxRateInterface::KEY_TITLE, $title);
+        return $this->setData(self::KEY_TITLE, $title);
     }
 
     /**
@@ -67,7 +75,7 @@ class AppliedTaxRate extends AbstractExtensibleModel implements AppliedTaxRateIn
      */
     public function setPercent($percent)
     {
-        return $this->setData(AppliedTaxRateInterface::KEY_PERCENT, $percent);
+        return $this->setData(self::KEY_PERCENT, $percent);
     }
 
     /**
diff --git a/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php b/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php
index 9ba71b43dedf1c2bf676436e6a3754e4f168d69f..d4f1628fbdbb8f4921e1162f01e23bd68aa94223 100644
--- a/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php
+++ b/app/code/Magento/Tax/Model/TaxDetails/ItemDetails.php
@@ -13,12 +13,30 @@ use Magento\Tax\Api\Data\TaxDetailsItemInterface;
  */
 class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_CODE                 = 'code';
+    const KEY_TYPE                 = 'type';
+    const KEY_TAX_PERCENT          = 'tax_percent';
+    const KEY_PRICE                = 'price';
+    const KEY_PRICE_INCL_TAX       = 'price_incl_tax';
+    const KEY_ROW_TOTAL            = 'row_total';
+    const KEY_ROW_TOTAL_INCL_TAX   = 'row_total_incl_tax';
+    const KEY_ROW_TAX              = 'row_tax';
+    const KEY_TAXABLE_AMOUNT       = 'taxable_amount';
+    const KEY_DISCOUNT_AMOUNT      = 'discount_amount';
+    const KEY_APPLIED_TAXES        = 'applied_taxes';
+    const KEY_ASSOCIATED_ITEM_CODE = 'associated_item_code';
+    const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
     public function getCode()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_CODE);
+        return $this->getData(self::KEY_CODE);
     }
 
     /**
@@ -26,7 +44,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getType()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_TYPE);
+        return $this->getData(self::KEY_TYPE);
     }
 
     /**
@@ -34,7 +52,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getTaxPercent()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_TAX_PERCENT);
+        return $this->getData(self::KEY_TAX_PERCENT);
     }
 
     /**
@@ -42,7 +60,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getPrice()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_PRICE);
+        return $this->getData(self::KEY_PRICE);
     }
 
     /**
@@ -50,7 +68,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getPriceInclTax()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_PRICE_INCL_TAX);
+        return $this->getData(self::KEY_PRICE_INCL_TAX);
     }
 
     /**
@@ -58,7 +76,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getRowTotal()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_ROW_TOTAL);
+        return $this->getData(self::KEY_ROW_TOTAL);
     }
 
     /**
@@ -66,7 +84,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getRowTotalInclTax()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_ROW_TOTAL_INCL_TAX);
+        return $this->getData(self::KEY_ROW_TOTAL_INCL_TAX);
     }
 
     /**
@@ -74,7 +92,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getRowTax()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_ROW_TAX);
+        return $this->getData(self::KEY_ROW_TAX);
     }
 
     /**
@@ -82,7 +100,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getTaxableAmount()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_TAXABLE_AMOUNT);
+        return $this->getData(self::KEY_TAXABLE_AMOUNT);
     }
 
     /**
@@ -90,7 +108,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getDiscountAmount()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_DISCOUNT_AMOUNT);
+        return $this->getData(self::KEY_DISCOUNT_AMOUNT);
     }
 
     /**
@@ -98,7 +116,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getDiscountTaxCompensationAmount()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT);
+        return $this->getData(self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT);
     }
 
     /**
@@ -106,7 +124,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getAppliedTaxes()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_APPLIED_TAXES);
+        return $this->getData(self::KEY_APPLIED_TAXES);
     }
 
     /**
@@ -114,7 +132,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function getAssociatedItemCode()
     {
-        return $this->getData(TaxDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE);
+        return $this->getData(self::KEY_ASSOCIATED_ITEM_CODE);
     }
 
     /**
@@ -125,7 +143,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setCode($code)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_CODE, $code);
+        return $this->setData(self::KEY_CODE, $code);
     }
 
     /**
@@ -136,7 +154,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setType($type)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_TYPE, $type);
+        return $this->setData(self::KEY_TYPE, $type);
     }
 
     /**
@@ -147,7 +165,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setTaxPercent($taxPercent)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_TAX_PERCENT, $taxPercent);
+        return $this->setData(self::KEY_TAX_PERCENT, $taxPercent);
     }
 
     /**
@@ -158,7 +176,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setPrice($price)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_PRICE, $price);
+        return $this->setData(self::KEY_PRICE, $price);
     }
 
     /**
@@ -169,7 +187,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setPriceInclTax($priceInclTax)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_PRICE_INCL_TAX, $priceInclTax);
+        return $this->setData(self::KEY_PRICE_INCL_TAX, $priceInclTax);
     }
 
     /**
@@ -180,7 +198,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setRowTotal($rowTotal)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_ROW_TOTAL, $rowTotal);
+        return $this->setData(self::KEY_ROW_TOTAL, $rowTotal);
     }
 
     /**
@@ -191,7 +209,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setRowTotalInclTax($rowTotalInclTax)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_ROW_TOTAL_INCL_TAX, $rowTotalInclTax);
+        return $this->setData(self::KEY_ROW_TOTAL_INCL_TAX, $rowTotalInclTax);
     }
 
     /**
@@ -202,7 +220,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setRowTax($rowTax)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_ROW_TAX, $rowTax);
+        return $this->setData(self::KEY_ROW_TAX, $rowTax);
     }
 
     /**
@@ -213,7 +231,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setTaxableAmount($taxableAmount)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_TAXABLE_AMOUNT, $taxableAmount);
+        return $this->setData(self::KEY_TAXABLE_AMOUNT, $taxableAmount);
     }
 
     /**
@@ -224,7 +242,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setDiscountAmount($discountAmount)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_DISCOUNT_AMOUNT, $discountAmount);
+        return $this->setData(self::KEY_DISCOUNT_AMOUNT, $discountAmount);
     }
 
     /**
@@ -236,7 +254,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
     public function setDiscountTaxCompensationAmount($discountTaxCompensationAmount)
     {
         return $this->setData(
-            TaxDetailsItemInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT,
+            self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT,
             $discountTaxCompensationAmount
         );
     }
@@ -249,7 +267,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setAppliedTaxes(array $appliedTaxes = null)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_APPLIED_TAXES, $appliedTaxes);
+        return $this->setData(self::KEY_APPLIED_TAXES, $appliedTaxes);
     }
 
     /**
@@ -260,7 +278,7 @@ class ItemDetails extends AbstractExtensibleModel implements TaxDetailsItemInter
      */
     public function setAssociatedItemCode($associatedItemCode)
     {
-        return $this->setData(TaxDetailsItemInterface::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode);
+        return $this->setData(self::KEY_ASSOCIATED_ITEM_CODE, $associatedItemCode);
     }
 
     /**
diff --git a/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php b/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php
index 91f1587c8b575ce08e5f52a0196c724f40917a36..721dedffeb29e43cb8433b1222e9e0059e8a4f7a 100644
--- a/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php
+++ b/app/code/Magento/Tax/Model/TaxDetails/TaxDetails.php
@@ -13,12 +13,22 @@ use Magento\Tax\Api\Data\TaxDetailsInterface;
  */
 class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
 {
+    /**#@+
+     * Constants defined for keys of array, makes typos less likely
+     */
+    const KEY_SUBTOTAL      = 'subtotal';
+    const KEY_TAX_AMOUNT    = 'tax_amount';
+    const KEY_APPLIED_TAXES = 'applied_taxes';
+    const KEY_ITEMS         = 'items';
+    const KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT = 'discount_tax_compensation_amount';
+    /**#@-*/
+
     /**
      * {@inheritdoc}
      */
     public function getSubtotal()
     {
-        return $this->getData(TaxDetailsInterface::KEY_SUBTOTAL);
+        return $this->getData(self::KEY_SUBTOTAL);
     }
 
     /**
@@ -26,7 +36,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function getTaxAmount()
     {
-        return $this->getData(TaxDetailsInterface::KEY_TAX_AMOUNT);
+        return $this->getData(self::KEY_TAX_AMOUNT);
     }
 
     /**
@@ -34,7 +44,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function getDiscountTaxCompensationAmount()
     {
-        return $this->getData(TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT);
+        return $this->getData(self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT);
     }
 
     /**
@@ -42,7 +52,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function getAppliedTaxes()
     {
-        return $this->getData(TaxDetailsInterface::KEY_APPLIED_TAXES);
+        return $this->getData(self::KEY_APPLIED_TAXES);
     }
 
     /**
@@ -50,7 +60,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function getItems()
     {
-        return $this->getData(TaxDetailsInterface::KEY_ITEMS);
+        return $this->getData(self::KEY_ITEMS);
     }
 
     /**
@@ -61,7 +71,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function setSubtotal($subtotal)
     {
-        return $this->setData(TaxDetailsInterface::KEY_SUBTOTAL, $subtotal);
+        return $this->setData(self::KEY_SUBTOTAL, $subtotal);
     }
 
     /**
@@ -72,7 +82,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function setTaxAmount($taxAmount)
     {
-        return $this->setData(TaxDetailsInterface::KEY_TAX_AMOUNT, $taxAmount);
+        return $this->setData(self::KEY_TAX_AMOUNT, $taxAmount);
     }
 
     /**
@@ -84,7 +94,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
     public function setDiscountTaxCompensationAmount($discountTaxCompensationAmount)
     {
         return $this->setData(
-            TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT,
+            self::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT,
             $discountTaxCompensationAmount
         );
     }
@@ -97,7 +107,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function setAppliedTaxes(array $appliedTaxes = null)
     {
-        return $this->setData(TaxDetailsInterface::KEY_APPLIED_TAXES, $appliedTaxes);
+        return $this->setData(self::KEY_APPLIED_TAXES, $appliedTaxes);
     }
 
     /**
@@ -108,7 +118,7 @@ class TaxDetails extends AbstractExtensibleModel implements TaxDetailsInterface
      */
     public function setItems(array $items = null)
     {
-        return $this->setData(TaxDetailsInterface::KEY_ITEMS, $items);
+        return $this->setData(self::KEY_ITEMS, $items);
     }
 
     /**
diff --git a/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Rate/AjaxLoadTest.php b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Rate/AjaxLoadTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..6373e5aa514bf11a2d6174c78e64489bd7d558b0
--- /dev/null
+++ b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Rate/AjaxLoadTest.php
@@ -0,0 +1,238 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Tax\Test\Unit\Controller\Adminhtml\Rate;
+
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+use Magento\Framework\Exception\NoSuchEntityException;
+
+/**
+ * Test for AjaxLoadTest
+ */
+class AjaxLoadTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Framework\App\Request\Http
+     */
+    private $request;
+
+    /**
+     * @var \Magento\Framework\App\Response\Http
+     */
+    private $resultFactory;
+
+    /**
+     * @var \Magento\Tax\Model\Calculation\RateRepository
+     */
+    private $taxRateRepository;
+
+    /*
+     * test setup
+     */
+    public function setUp()
+    {
+        $this->request = $this->getMockBuilder('\Magento\Framework\App\Request\Http')
+            ->disableOriginalConstructor()
+            ->setMethods(['getParam'])
+            ->getMock();
+
+        $this->resultFactory = $this->getMockBuilder('Magento\Framework\Controller\ResultFactory')
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->taxRateRepository = $this->getMockBuilder('\Magento\Tax\Model\Calculation\RateRepository')
+            ->disableOriginalConstructor()
+            ->setMethods(['get'])
+            ->getMock();
+    }
+
+    /**
+     * Executes the controller action and asserts non exception logic
+     */
+    public function testExecute()
+    {
+        $taxRateId=1;
+        $returnArray=[
+        'tax_calculation_rate_id' => null,
+                    'tax_country_id' => 'US',
+                    'tax_region_id' => 2,
+                    'tax_postcode' => null,
+                    'code' => 'Tax Rate Code',
+                    'rate' => 7.5,
+                    'zip_is_range'=> 0,
+                    'title[1]' => 'texas',
+                ];
+        $objectManager = new ObjectManager($this);
+        $rateTitles = [$objectManager->getObject(
+            '\Magento\Tax\Model\Calculation\Rate\Title',
+            ['data' => ['store_id' => 1, 'value' => 'texas']]
+        )
+        ];
+        $rateMock = $objectManager->getObject(
+            'Magento\Tax\Model\Calculation\Rate',
+            [
+                'data' =>
+                    [
+                        'tax_country_id' => 'US',
+                        'tax_region_id' => 2,
+                        'tax_postcode' => null,
+                        'rate' => 7.5,
+                        'code' => 'Tax Rate Code',
+                        'titles' => $rateTitles,
+                    ],
+            ]
+        );
+
+        $this->request->expects($this->any())
+            ->method('getParam')
+            ->will($this->returnValue($taxRateId));
+
+        $this->taxRateRepository->expects($this->any())
+            ->method('get')
+            ->with($taxRateId)
+            ->will($this->returnValue($rateMock));
+
+        $taxRateConverter = $this->getMockBuilder('\Magento\Tax\Model\Calculation\Rate\Converter')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $taxRateConverter->expects($this->any())
+            ->method('createArrayFromServiceObject')
+            ->with($rateMock, true)
+            ->willReturn($returnArray);
+
+        $jsonObject= $this->getMockBuilder('Magento\Framework\Controller\Result\Json')
+            ->disableOriginalConstructor()
+            ->setMethods(['setData'])
+            ->getMock();
+
+        $jsonObject->expects($this->once())
+            ->method('setData')
+            ->with(['success' => true, 'error_message' => '', 'result'=>
+                $returnArray,
+            ]);
+
+        $this->resultFactory->expects($this->any())
+            ->method('create')
+            ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON)
+            ->willReturn($jsonObject);
+
+        $notification = $objectManager->getObject(
+            'Magento\Tax\Controller\Adminhtml\Rate\AjaxLoad',
+            [
+                'taxRateRepository' => $this->taxRateRepository,
+                'taxRateConverter' => $taxRateConverter,
+                'request' => $this->request,
+                'resultFactory' => $this->resultFactory,
+            ]
+        );
+
+
+        // No exception thrown
+        $this->assertSame($jsonObject, $notification->execute());
+
+    }
+
+    /**
+     * Check if validation throws a localized catched exception in case of incorrect id
+     */
+    public function testExecuteLocalizedException()
+    {
+        $taxRateId=999;
+        $exceptionMessage='No such entity with taxRateId = '.$taxRateId;
+        $noSuchEntityEx= new NoSuchEntityException(__($exceptionMessage));
+
+        $objectManager = new ObjectManager($this);
+
+        $this->request->expects($this->any())
+            ->method('getParam')
+            ->will($this->returnValue($taxRateId));
+
+        $this->taxRateRepository->expects($this->any())
+            ->method('get')
+            ->with($taxRateId)
+            ->willThrowException($noSuchEntityEx);
+
+        $jsonObject= $this->getMockBuilder('Magento\Framework\Controller\Result\Json')
+            ->disableOriginalConstructor()
+            ->setMethods(['setData'])
+            ->getMock();
+
+        $jsonObject->expects($this->once())
+            ->method('setData')
+            ->with([
+                'success' => false,
+                'error_message' => $exceptionMessage,
+            ]);
+
+        $this->resultFactory->expects($this->any())
+            ->method('create')
+            ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON)
+            ->willReturn($jsonObject);
+
+        $notification = $objectManager->getObject(
+            'Magento\Tax\Controller\Adminhtml\Rate\AjaxLoad',
+            [
+                'taxRateRepository' => $this->taxRateRepository,
+                'request' => $this->request,
+                'resultFactory' => $this->resultFactory,
+            ]
+        );
+
+        //exception thrown with catch
+        $this->assertSame($jsonObject, $notification->execute());
+    }
+
+    /**
+     * Check if validation throws a localized catched exception in case of incorrect id
+     */
+    public function testExecuteException()
+    {
+        $taxRateId=999;
+        $exceptionMessage=__('An error occurred while loading this tax rate.');
+        $noSuchEntityEx= new \Exception();
+
+        $objectManager = new ObjectManager($this);
+
+        $this->request->expects($this->any())
+            ->method('getParam')
+            ->will($this->returnValue($taxRateId));
+
+        $this->taxRateRepository->expects($this->any())
+            ->method('get')
+            ->with($taxRateId)
+            ->willThrowException($noSuchEntityEx);
+
+        $jsonObject= $this->getMockBuilder('Magento\Framework\Controller\Result\Json')
+            ->disableOriginalConstructor()
+            ->setMethods(['setData'])
+            ->getMock();
+
+        $jsonObject->expects($this->once())
+            ->method('setData')
+            ->with([
+                'success' => false,
+                'error_message' => $exceptionMessage,
+            ]);
+
+        $this->resultFactory->expects($this->any())
+            ->method('create')
+            ->with(\Magento\Framework\Controller\ResultFactory::TYPE_JSON)
+            ->willReturn($jsonObject);
+
+        $notification = $objectManager->getObject(
+            'Magento\Tax\Controller\Adminhtml\Rate\AjaxLoad',
+            [
+                'taxRateRepository' => $this->taxRateRepository,
+                'request' => $this->request,
+                'resultFactory' => $this->resultFactory,
+            ]
+        );
+
+        //exception thrown with catch
+        $this->assertSame($jsonObject, $notification->execute());
+    }
+}
diff --git a/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php
index 2f2a823d5f64d3cb8c3b00a4c864a7108e181c32..8714905efc56bc118efd516eb8346ec1e2045ad6 100644
--- a/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Controller/Adminhtml/Tax/IgnoreTaxNotificationTest.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Tax\Test\Unit\Controller\Adminhtml\Tax;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 class IgnoreTaxNotificationTest extends \PHPUnit_Framework_TestCase
@@ -51,7 +52,7 @@ class IgnoreTaxNotificationTest extends \PHPUnit_Framework_TestCase
             ->getMock();
         $config->expects($this->once())
             ->method('saveConfig')
-            ->with('tax/notification/ignore_tax', 1, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, 0)
+            ->with('tax/notification/ignore_tax', 1, ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0)
             ->willReturn(null);
 
         $manager = $this->getMockBuilder('\Magento\Framework\ObjectManagerInterface')
diff --git a/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php b/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php
index 7fa4b63e75e603558c6a26fe028472552f58fd72..dc4dfcf84c4a377205e513c526e15c427018307c 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/Calculation/Rate/ConverterTest.php
@@ -5,7 +5,7 @@
  */
 namespace Magento\Tax\Test\Unit\Model\Calculation\Rate;
 
-use \Magento\Tax\Model\Calculation\Rate\Converter;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
 
 class ConverterTest extends \PHPUnit_Framework_TestCase
 {
@@ -14,9 +14,45 @@ class ConverterTest extends \PHPUnit_Framework_TestCase
      */
     protected $converter;
 
+    /**
+     * @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory
+     */
+    protected $taxRateDataObjectFactory;
+
+    /**
+     * @var \Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory
+     */
+    protected $taxRateTitleDataObjectFactory;
+
+    /**
+     * @var \Magento\Framework\TestFramework\Unit\Helper
+     */
+    protected $objectManager;
+
     public function setUp()
     {
-        $this->converter = new Converter();
+        $this->taxRateDataObjectFactory = $this->getMockBuilder(
+            '\Magento\Tax\Api\Data\TaxRateInterfaceFactory'
+        )
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->taxRateTitleDataObjectFactory = $this->getMockBuilder(
+            '\Magento\Tax\Api\Data\TaxRateTitleInterfaceFactory'
+        )
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+
+        $this->objectManager = new ObjectManager($this);
+        $this->converter =  $this->objectManager->getObject(
+            'Magento\Tax\Model\Calculation\Rate\Converter',
+            [
+                'taxRateDataObjectFactory' =>  $this->taxRateDataObjectFactory,
+                'taxRateTitleDataObjectFactory' => $this->taxRateTitleDataObjectFactory,
+            ]
+        );
     }
 
     public function testCreateTitlesFromServiceObject()
@@ -39,4 +75,48 @@ class ConverterTest extends \PHPUnit_Framework_TestCase
 
         $this->assertEquals([], $this->converter->createTitleArrayFromServiceObject($taxRateMock));
     }
+
+
+    public function testCreateArrayFromServiceObject()
+    {
+        $taxRateMock = $this->getMock('Magento\Tax\Api\Data\TaxRateInterface');
+        $titlesMock = $this->getMock('Magento\Tax\Api\Data\TaxRateTitleInterface');
+
+        $taxRateMock->expects($this->atLeastOnce())->method('getTitles')->willReturn([$titlesMock]);
+        $titlesMock->expects($this->atLeastOnce())->method('getStoreId')->willReturn(1);
+        $titlesMock->expects($this->atLeastOnce())->method('getValue')->willReturn('Value');
+
+        $this->assertArrayHasKey('title[1]', $this->converter->createArrayFromServiceObject($taxRateMock, true));
+        $this->assertArrayHasKey('title', $this->converter->createArrayFromServiceObject($taxRateMock));
+        $this->assertTrue(is_array($this->converter->createArrayFromServiceObject($taxRateMock)));
+    }
+
+    public function testPopulateTaxRateData()
+    {
+        $rateTitles = [$this->objectManager->getObject(
+            '\Magento\Tax\Model\Calculation\Rate\Title',
+            ['data' => ['store_id' => 1, 'value' => 'texas']]
+        )
+        ];
+        $dataArray=[
+            'tax_country_id' => 'US',
+            'tax_region_id' => 2,
+            'tax_postcode' => null,
+            'rate' => 7.5,
+            'code' => 'Tax Rate Code',
+            'titles' => $rateTitles,
+        ];
+
+        $taxRate = $this->objectManager->getObject(
+            'Magento\Tax\Model\Calculation\Rate',
+            [
+                'data' =>$dataArray,
+            ]
+        );
+
+        $this->taxRateDataObjectFactory->expects($this->once())->method('create')->willReturn($taxRate);
+
+        $this->assertSame($taxRate, $this->converter->populateTaxRateData($dataArray));
+        $this->assertEquals($taxRate->getTitles(), $rateTitles);
+    }
 }
diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php
index f4bd04f1adf82bf8c77346c6ead678e85bb99f2c..d88ea2756c2e95330fc1a520c2d2a42c8744b259 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/TaxCalculationTest.php
@@ -199,11 +199,11 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase
         $customerId = 100;
         $taxClassId = 200;
         $taxDetailsData = [
-            \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_SUBTOTAL => 0.0,
-            \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_TAX_AMOUNT => 0.0,
-            \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0,
-            \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_APPLIED_TAXES => [],
-            \Magento\Tax\Api\Data\TaxDetailsInterface::KEY_ITEMS => [],
+            \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_SUBTOTAL => 0.0,
+            \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_TAX_AMOUNT => 0.0,
+            \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_DISCOUNT_TAX_COMPENSATION_AMOUNT => 0.0,
+            \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_APPLIED_TAXES => [],
+            \Magento\Tax\Model\TaxDetails\TaxDetails::KEY_ITEMS => [],
         ];
 
         $quoteDetailsMock = $this->getMock('\Magento\Tax\Api\Data\QuoteDetailsInterface');
diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php
index 3ceefb565192b79193555b490f15d159f414024b..de391779d3ec915a2796f3404b84206c024cbdbb 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/ManagementTest.php
@@ -79,8 +79,8 @@ class ManagementTest extends \PHPUnit_Framework_TestCase
             ->method('setField')
             ->with(
                 $this->logicalOr(
-                    \Magento\Tax\Api\Data\TaxClassInterface::KEY_TYPE,
-                    \Magento\Tax\Api\Data\TaxClassInterface::KEY_NAME
+                    \Magento\Tax\Model\ClassModel::KEY_TYPE,
+                    \Magento\Tax\Model\ClassModel::KEY_NAME
                 )
             )->willReturnSelf();
 
diff --git a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php
index 6adec4612fe7b22e96f97bba7dd73119d4a4449c..c36618f8faf5c2a7cdc68b9aab3a16fbe274a5d9 100644
--- a/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php
+++ b/app/code/Magento/Tax/Test/Unit/Model/TaxClass/Source/CustomerTest.php
@@ -120,7 +120,7 @@ class CustomerTest extends \PHPUnit_Framework_TestCase
 
         $this->filterBuilderMock->expects($this->once())
             ->method('setField')
-            ->with(\Magento\Tax\Api\Data\TaxClassInterface::KEY_TYPE)
+            ->with(\Magento\Tax\Model\ClassModel::KEY_TYPE)
             ->willReturnSelf();
         $this->filterBuilderMock->expects($this->once())
             ->method('setValue')
diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml
index e1941d06d1d4973744aa9e6cc415e282cc799780..4861e4b3bdc539fe555e6eefa3eaad39a489ecdb 100644
--- a/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml
+++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml
@@ -73,16 +73,33 @@ require([
                 id = select.find('option').eq(index).attr('value'),
                 item;
 
-            for(var i = 0, c = taxRateCollection.length; i < c; i++) {
-                if (taxRateCollection[i].tax_calculation_rate_id == id) {
-                    item = taxRateCollection[i];
-                    break;
-                }
-            }
-            item.itemElement = that.prev();
-            $('#tax-rate-form')
-                    .dialogRates({itemRate: item})
-                    .dialogRates('open');
+            $('body').trigger('processStart')
+            $.ajax({
+                type: "POST",
+                data: {id:id},
+                url: '<?php echo $block->getTaxRateLoadUrl()?>',
+                success: function(result, status) {
+                    $('body').trigger('processStop');
+                    if (result.success) {
+                        item=result.result;
+                        item.itemElement = that.prev();
+                        $('#tax-rate-form')
+                            .dialogRates({itemRate: item})
+                            .dialogRates('open');
+
+                    } else {
+                        if (result.error_message)
+                            alert(result.error_message);
+                        else
+                            alert('<?php echo __('An error occurred'); ?>');
+                    }
+                },
+                error: function () {
+                    $('body').trigger('processStop');
+                    alert('<?php echo __('An error occurred'); ?>');
+                },
+                dataType: "json"
+            });
         };
 
         TaxRateEditableMultiselect.prototype.init = function () {
@@ -142,6 +159,7 @@ require([
                                 index = that.parent().index(),
                                 select = that.closest('.mselect-list').prev();
 
+                        $('body').trigger('processStart')
                         var ajaxOptions = {
                             type: 'POST',
                             data: {
@@ -151,12 +169,20 @@ require([
                             dataType: 'json',
                             url: '<?php echo $block->getTaxRateDeleteUrl()?>',
                             success: function(result, status) {
+                                $('body').trigger('processStop');
                                 if (result.success) {
                                     that.parent().remove();
                                     select.find('option').eq(index).remove();
                                 } else {
-                                    alert(result.error_message);
+                                    if (result.error_message)
+                                        alert(result.error_message);
+                                    else
+                                        alert('<?php echo __('An error occurred'); ?>');
                                 }
+                            },
+                            error: function () {
+                                $('body').trigger('processStop');
+                                alert('<?php echo __('An error occurred'); ?>');
                             }
                         };
                         $.ajax(ajaxOptions);
@@ -215,13 +241,15 @@ require([
                         if (!taxRateFormElement.validation().valid()) {
                             return;
                         }
-
+                        $('.tax-rate-popup').trigger('processStart');
+                        $('.loading-mask').css('z-index','1004');
                         var ajaxOptions = {
                             type: 'POST',
                             data: itemRateData,
                             dataType: 'json',
                             url: '<?php echo $block->getTaxRateSaveUrl()?>',
                             success: function(result, status) {
+                                $('body').trigger('processStop');
                                 if (result.success) {
                                     itemRate.code = result.code;
                                     if (itemRate.tax_calculation_rate_id) {
@@ -238,10 +266,16 @@ require([
                                             .val(itemRate.tax_calculation_rate_id);
                                     }
                                     taxRateForm.dialogRates("close");
-                                    taxRateCollection.push(itemRate);
                                 } else {
-                                    alert(result.error_message);
+                                    if (result.error_message)
+                                        alert(result.error_message);
+                                    else
+                                        alert('<?php echo __('An error occurred'); ?>');
                                 }
+                            },
+                            error: function () {
+                                $('body').trigger('processStop');
+                                alert('<?php echo __('An error occurred'); ?>');
                             }
                         };
                         $.ajax(ajaxOptions);
@@ -256,6 +290,7 @@ require([
                     }
                 }]
             });
+            $('.grid-loading-mask').hide();
         }
     };
 
diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml
index eae01d9d0b302c71017afb3571349eb82bcc6043..d5c0d8aee3b6a8f84af22f3fd90f2c2afde1f0f5 100644
--- a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml
+++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml
@@ -5,14 +5,13 @@
  */
 /* @var $block \Magento\Tax\Block\Adminhtml\Rate\Form */
 ?>
+
+<div data-role="spinner" class="grid-loading-mask">
+    <div class="grid-loader"></div>
+</div>
+
 <div class="form-inline" id="<?php echo $block->getNameInLayout() ?>" style="display:none">
     <?php echo $block->getFormHtml();?>
     <?php echo $block->getChildHtml('form_after');?>
 </div>
 
-<script>
-
-
-    var taxRateCollection = <?php echo json_encode($block->getRateCollection());?>;
-
-</script>
diff --git a/app/code/Magento/Theme/Model/Config.php b/app/code/Magento/Theme/Model/Config.php
index 5993e9bf010d5586384e03bdc4df50010f882547..39332cc9a0b7b7705db033cb7996e0fd3c482e90 100644
--- a/app/code/Magento/Theme/Model/Config.php
+++ b/app/code/Magento/Theme/Model/Config.php
@@ -9,6 +9,8 @@
  */
 namespace Magento\Theme\Model;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class Config
 {
     /**
@@ -173,7 +175,7 @@ class Config
     protected function _assignThemeToDefaultScope($themeId, &$isReassigned)
     {
         $configPath = \Magento\Framework\View\DesignInterface::XML_PATH_THEME_ID;
-        $this->_configWriter->save($configPath, $themeId, \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT);
+        $this->_configWriter->save($configPath, $themeId, ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
         $isReassigned = true;
         return $this;
     }
diff --git a/app/code/Magento/Theme/Model/View/Design.php b/app/code/Magento/Theme/Model/View/Design.php
index f839d6ba900f71e2998634aa9e36776bcd863105..f0cf795f6b4482b9c8ac59bc444e62f4af24f14c 100644
--- a/app/code/Magento/Theme/Model/View/Design.php
+++ b/app/code/Magento/Theme/Model/View/Design.php
@@ -6,6 +6,8 @@
 
 namespace Magento\Theme\Model\View;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 /**
  * Keeps design settings for current request
  */
@@ -167,7 +169,7 @@ class Design implements \Magento\Framework\View\DesignInterface
             if ($this->_storeManager->isSingleStoreMode()) {
                 $theme = $this->_scopeConfig->getValue(
                     self::XML_PATH_THEME_ID,
-                    \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+                    ScopeConfigInterface::SCOPE_TYPE_DEFAULT
                 );
             } else {
                 $theme = (string) $this->_scopeConfig->getValue(
diff --git a/app/code/Magento/Theme/view/adminhtml/requirejs-config.js b/app/code/Magento/Theme/view/adminhtml/requirejs-config.js
index 251c057d689f12e75e85991ed46d8b0e2faee673..0cc7098574c9d120f5235644bdf74ded7d9c82b2 100644
--- a/app/code/Magento/Theme/view/adminhtml/requirejs-config.js
+++ b/app/code/Magento/Theme/view/adminhtml/requirejs-config.js
@@ -5,6 +5,9 @@
 
 var config = {
     "shim": {
+        "extjs/ext-tree": [
+            "prototype"
+        ],
         "extjs/ext-tree-checkbox": [
             "extjs/ext-tree",
             "extjs/defaults"
diff --git a/app/code/Magento/User/Controller/Adminhtml/User/Edit.php b/app/code/Magento/User/Controller/Adminhtml/User/Edit.php
index 3d3ad64cd201a0bf5cc7eb3691852ab451317e21..39ac43d55391ba02f47089b28233a7f13e791d86 100644
--- a/app/code/Magento/User/Controller/Adminhtml/User/Edit.php
+++ b/app/code/Magento/User/Controller/Adminhtml/User/Edit.php
@@ -6,6 +6,8 @@
  */
 namespace Magento\User\Controller\Adminhtml\User;
 
+use Magento\Framework\Locale\Resolver;
+
 class Edit extends \Magento\User\Controller\Adminhtml\User
 {
     /**
@@ -25,7 +27,7 @@ class Edit extends \Magento\User\Controller\Adminhtml\User
                 return;
             }
         } else {
-            $model->setInterfaceLocale(\Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE);
+            $model->setInterfaceLocale(Resolver::DEFAULT_LOCALE);
         }
 
         // Restore previously entered form data from session
diff --git a/app/code/Magento/Webapi/Controller/Rest/Router/Route.php b/app/code/Magento/Webapi/Controller/Rest/Router/Route.php
index 5c125c29611d9e2000436a7147329dfa449ab729..03e6d2fd8ca06eb864bfa318af4a91182e8c1bb7 100644
--- a/app/code/Magento/Webapi/Controller/Rest/Router/Route.php
+++ b/app/code/Magento/Webapi/Controller/Rest/Router/Route.php
@@ -69,7 +69,7 @@ class Route implements RouterInterface
                 $this->variables[$key] = substr($value, 1);
                 $value = null;
             }
-            $result[$key] = strtolower($value);
+            $result[$key] = $value;
         }
         return $result;
     }
@@ -92,19 +92,12 @@ class Route implements RouterInterface
     /**
      * Retrieve unified requested path
      *
-     * Lowercase all path chunks, except variables names.
-     * E.g. the path '/V1/Categories/:categoryId' will be converted to '/v1/categories/:categoryId'.
-     *
      * @param string $path
      * @return array
      */
     protected function getPathParts($path)
     {
-        $result = explode('/', trim($path, '/'));
-        array_walk($result, function (&$item) {
-            $item = substr($item, 0, 1) === ":" ? $item : strtolower($item);
-        });
-        return $result;
+        return explode('/', trim($path, '/'));
     }
 
     /**
diff --git a/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php b/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php
index 25d196ce6a80461109ec3246bbe3a5bb895cfa22..ec73bd7236c104f5364b03433e83d8c622e12414 100644
--- a/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php
+++ b/app/code/Magento/Webapi/Controller/Soap/Request/Handler.php
@@ -15,6 +15,7 @@ use Magento\Framework\Webapi\ServiceInputProcessor;
 use Magento\Webapi\Controller\Soap\Request as SoapRequest;
 use Magento\Framework\Webapi\Exception as WebapiException;
 use Magento\Webapi\Model\Soap\Config as SoapConfig;
+use Magento\Framework\Reflection\MethodsMap;
 
 /**
  * Handler of requests to SOAP server.
@@ -48,6 +49,9 @@ class Handler
     /** @var DataObjectProcessor */
     protected $_dataObjectProcessor;
 
+    /** @var MethodsMap */
+    protected $methodsMapProcessor;
+
     /**
      * Initialize dependencies.
      *
@@ -58,6 +62,7 @@ class Handler
      * @param SimpleDataObjectConverter $dataObjectConverter
      * @param ServiceInputProcessor $serviceInputProcessor
      * @param DataObjectProcessor $dataObjectProcessor
+     * @param MethodsMap $methodsMapProcessor
      */
     public function __construct(
         SoapRequest $request,
@@ -66,7 +71,8 @@ class Handler
         AuthorizationInterface $authorization,
         SimpleDataObjectConverter $dataObjectConverter,
         ServiceInputProcessor $serviceInputProcessor,
-        DataObjectProcessor $dataObjectProcessor
+        DataObjectProcessor $dataObjectProcessor,
+        MethodsMap $methodsMapProcessor
     ) {
         $this->_request = $request;
         $this->_objectManager = $objectManager;
@@ -75,6 +81,7 @@ class Handler
         $this->_dataObjectConverter = $dataObjectConverter;
         $this->serviceInputProcessor = $serviceInputProcessor;
         $this->_dataObjectProcessor = $dataObjectProcessor;
+        $this->methodsMapProcessor = $methodsMapProcessor;
     }
 
     /**
@@ -149,7 +156,7 @@ class Handler
     protected function _prepareResponseData($data, $serviceClassName, $serviceMethodName)
     {
         /** @var string $dataType */
-        $dataType = $this->_dataObjectProcessor->getMethodReturnType($serviceClassName, $serviceMethodName);
+        $dataType = $this->methodsMapProcessor->getMethodReturnType($serviceClassName, $serviceMethodName);
         $result = null;
         if (is_object($data)) {
             $result = $this->_dataObjectConverter
diff --git a/app/code/Magento/Webapi/Model/Rest/Config.php b/app/code/Magento/Webapi/Model/Rest/Config.php
index dfee1d873c02158645f482b3fe7242ecc5636bfe..51d0e67d7803603ef4ff02e6db406f041166b2d9 100644
--- a/app/code/Magento/Webapi/Model/Rest/Config.php
+++ b/app/code/Magento/Webapi/Model/Rest/Config.php
@@ -67,7 +67,7 @@ class Config
         /** @var $route \Magento\Webapi\Controller\Rest\Router\Route */
         $route = $this->_routeFactory->createRoute(
             'Magento\Webapi\Controller\Rest\Router\Route',
-            $this->_formatRoutePath($routeData[self::KEY_ROUTE_PATH])
+            $routeData[self::KEY_ROUTE_PATH]
         );
 
         $route->setServiceClass($routeData[self::KEY_CLASS])
@@ -78,22 +78,6 @@ class Config
         return $route;
     }
 
-    /**
-     * Lowercase all parts of the given route path except for the path parameters.
-     *
-     * @param string $routePath The route path (e.g. '/V1/Categories/:categoryId')
-     * @return string The modified route path (e.g. '/v1/categories/:categoryId')
-     */
-    protected function _formatRoutePath($routePath)
-    {
-        $routePathParts = explode('/', $routePath);
-        $pathParts = [];
-        foreach ($routePathParts as $pathPart) {
-            $pathParts[] = substr($pathPart, 0, 1) === ":" ? $pathPart : strtolower($pathPart);
-        }
-        return implode('/', $pathParts);
-    }
-
     /**
      * Get service base URL
      *
diff --git a/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php b/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php
index 6732d012dc1e726e29e4afab132711759d4ce7f9..4a9192b36882b9f69a66755af08cd3f050374beb 100644
--- a/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php
+++ b/app/code/Magento/Webapi/Test/Unit/Controller/Rest/Router/RouteTest.php
@@ -95,10 +95,7 @@ class RouteTest extends \PHPUnit_Framework_TestCase
             ['/V1/one/two/:threeValue/four/:fiveValue', '/V1/one/two/3/four/5', ['threeValue' => 3, 'fiveValue' => 5]],
 
             ['/v1/One', '/v1/One', []],
-            ['/v1/oNe', '/V1/one', []],
-            ['/v1/onE', '/V1/oNe', []],
 
-            ['/v1/One/:twoValue', '/V1/one/2', ['twoValue' => 2]],
             ['/v1/oNe/:TwoValue', '/v1/oNe/2', ['TwoValue' => 2]],
             ['/v1/onE/:twovalue', '/v1/onE/2', ['twovalue' => 2]],
 
@@ -108,6 +105,9 @@ class RouteTest extends \PHPUnit_Framework_TestCase
             ['/V1/one-one/:two_value', '/V1/one-one/2', ['two_value' => 2]],
 
             // Error
+            ['/v1/oNe', '/V1/one', false],
+            ['/v1/onE', '/V1/oNe', false],
+            ['/v1/One/:twoValue', '/V1/one/2', false],
             ['/V1/one', '/V1/two', false],
             ['/V1/one/:twoValue', '/V1/one', false],
             ['/V1/one/two', '/V1/one', false],
diff --git a/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php b/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php
index 2800d5466585fdc8eec97ba82dd53d7f5fee0c93..8c3741c3b8d48c86e0e876125cee6558a2c3a403 100644
--- a/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php
+++ b/app/code/Magento/Webapi/Test/Unit/Controller/Soap/Request/HandlerTest.php
@@ -40,6 +40,9 @@ class HandlerTest extends \PHPUnit_Framework_TestCase
     /** @var \Magento\Framework\Reflection\DataObjectProcessor|\PHPUnit_Framework_MockObject_MockObject */
     protected $_dataObjectProcessorMock;
 
+    /** @var \Magento\Framework\Reflection\MethodsMap|\PHPUnit_Framework_MockObject_MockObject */
+    protected $_methodsMapProcessorMock;
+
     /** @var array */
     protected $_arguments;
 
@@ -67,7 +70,13 @@ class HandlerTest extends \PHPUnit_Framework_TestCase
         );
         $this->_dataObjectProcessorMock = $this->getMock(
             'Magento\Framework\Reflection\DataObjectProcessor',
-            ['getMethodReturnType'],
+            [],
+            [],
+            '',
+            false);
+        $this->_methodsMapProcessorMock = $this->getMock(
+            'Magento\Framework\Reflection\MethodsMap',
+            [],
             [],
             '',
             false);
@@ -80,7 +89,8 @@ class HandlerTest extends \PHPUnit_Framework_TestCase
             $this->_authorizationMock,
             $this->_dataObjectConverter,
             $this->_serviceInputProcessorMock,
-            $this->_dataObjectProcessorMock
+            $this->_dataObjectProcessorMock,
+            $this->_methodsMapProcessorMock
         );
         parent::setUp();
     }
@@ -128,10 +138,6 @@ class HandlerTest extends \PHPUnit_Framework_TestCase
             ->method('process')
             ->will($this->returnArgument(2));
 
-        $this->_dataObjectProcessorMock->expects($this->any())->method('getMethodReturnType')
-            ->with($className, $methodName)
-            ->will($this->returnValue('string'));
-
         /** Execute SUT. */
         $this->assertEquals(
             ['result' => $serviceResponse],
diff --git a/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php b/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php
index 3453c7e1d7951de030d96012f813477fe73efd4b..c3f586d835a8d3c3d1d85e3626e258680541441c 100644
--- a/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php
+++ b/app/code/Magento/Webapi/Test/Unit/Model/DataObjectProcessorTest.php
@@ -23,7 +23,21 @@ class DataObjectProcessorTest extends \PHPUnit_Framework_TestCase
     protected function setup()
     {
         $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-        $this->dataObjectProcessor = $objectManager->getObject('Magento\Framework\Reflection\DataObjectProcessor');
+        $methodsMapProcessor = $objectManager->getObject(
+            'Magento\Framework\Reflection\MethodsMap',
+            [
+                'fieldNamer' => $objectManager->getObject('Magento\Framework\Reflection\FieldNamer'),
+                'typeProcessor' => $objectManager->getObject('Magento\Framework\Reflection\TypeProcessor'),
+            ]
+        );
+        $this->dataObjectProcessor = $objectManager->getObject(
+            'Magento\Framework\Reflection\DataObjectProcessor',
+            [
+                'methodsMapProcessor' => $methodsMapProcessor,
+                'typeCaster' => $objectManager->getObject('Magento\Framework\Reflection\TypeCaster'),
+                'fieldNamer' => $objectManager->getObject('Magento\Framework\Reflection\FieldNamer'),
+            ]
+        );
         parent::setUp();
     }
 
diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php b/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php
index 1b4f971c2c3a447261a22ce6bb898311813e65b7..2010509ff416165858cbdcb1b1d3b3ae298c9759 100644
--- a/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php
+++ b/app/code/Magento/Webapi/Test/Unit/Model/Files/TestDataInterface.php
@@ -8,11 +8,23 @@ namespace Magento\Webapi\Test\Unit\Model\Files;
 
 interface TestDataInterface
 {
+    /**
+     * @return string
+     */
     public function getId();
 
+    /**
+     * @return string
+     */
     public function getAddress();
 
+    /**
+     * @return string
+     */
     public function isDefaultShipping();
 
+    /**
+     * @return string
+     */
     public function isRequiredBilling();
 }
diff --git a/app/code/Magento/Webapi/etc/di.xml b/app/code/Magento/Webapi/etc/di.xml
index 12b661f639c428c681b98b1df7c0f5a8131da632..c58acdbf51c4b9e3252745d425b3d365e2a60449 100644
--- a/app/code/Magento/Webapi/etc/di.xml
+++ b/app/code/Magento/Webapi/etc/di.xml
@@ -22,11 +22,17 @@
     <type name="Magento\Framework\Xml\Parser" shared="false" />
     <type name="Magento\Framework\Code\Scanner\DirectoryScanner" shared="false" />
     <type name="Magento\Server\Reflection" shared="false" />
-    <type name="Magento\Framework\Reflection\DataObjectProcessor">
+    <type name="Magento\Framework\Reflection\MethodsMap">
         <arguments>
             <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Webapi</argument>
         </arguments>
     </type>
+    <type name="Magento\Framework\Reflection\DataObjectProcessor">
+        <arguments>
+            <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessor\Proxy</argument>
+            <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument>
+        </arguments>
+    </type>
     <type name="Magento\Integration\Model\ConfigBasedIntegrationManager">
         <plugin name="webapiSetup" type="Magento\Webapi\Model\Plugin\Manager" />
     </type>
diff --git a/app/code/Magento/Webapi/etc/webapi_rest/di.xml b/app/code/Magento/Webapi/etc/webapi_rest/di.xml
index 48420045d4afca1644c159d3fa68a541cd8c403a..7941e76f564b0fa13986a9847e250a34f36d383c 100644
--- a/app/code/Magento/Webapi/etc/webapi_rest/di.xml
+++ b/app/code/Magento/Webapi/etc/webapi_rest/di.xml
@@ -71,4 +71,25 @@
     <type name="Magento\Framework\Authorization">
         <plugin name="guestAuthorization" type="Magento\Webapi\Model\Plugin\GuestAuthorization" />
     </type>
+
+    <!-- Configuration to check that the permissions are checked on fields -->
+    <virtualType name="Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked" type="Magento\Framework\Reflection\ExtensionAttributesProcessor">
+        <arguments>
+            <argument name="isPermissionChecked" xsi:type="boolean">true</argument>
+            <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessor\Proxy</argument>
+        </arguments>
+    </virtualType>
+    <virtualType name="Magento\Framework\Reflection\DataObjectProcessorPermissionChecked" type="Magento\Framework\Reflection\DataObjectProcessor">
+        <arguments>
+            <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked</argument>
+            <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument>
+        </arguments>
+    </virtualType>
+
+    <type name="Magento\Framework\Webapi\ServiceOutputProcessor">
+        <arguments>
+            <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessorPermissionChecked</argument>
+        </arguments>
+    </type>
+    <!-- End of configuration to check that permissions are checked on fields -->
 </config>
diff --git a/app/code/Magento/Webapi/etc/webapi_soap/di.xml b/app/code/Magento/Webapi/etc/webapi_soap/di.xml
index 6d5607c49c18dc46d9e6d9cbc429bc11f4a26467..c8cabfba0ac2529cb3365da99ae129086bee75fe 100644
--- a/app/code/Magento/Webapi/etc/webapi_soap/di.xml
+++ b/app/code/Magento/Webapi/etc/webapi_soap/di.xml
@@ -39,4 +39,26 @@
     <type name="Magento\Framework\Authorization">
         <plugin name="guestAuthorization" type="Magento\Webapi\Model\Plugin\GuestAuthorization" />
     </type>
+
+    <!-- Configuration to check that the permissions are checked on fields -->
+    <virtualType name="Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked" type="Magento\Framework\Reflection\ExtensionAttributesProcessor">
+        <arguments>
+            <argument name="isPermissionChecked" xsi:type="boolean">true</argument>
+            <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessor\Proxy</argument>
+        </arguments>
+    </virtualType>
+    <virtualType name="Magento\Framework\Reflection\DataObjectProcessorPermissionChecked" type="Magento\Framework\Reflection\DataObjectProcessor">
+        <arguments>
+            <argument name="extensionAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\ExtensionAttributesProcessorPermissionChecked</argument>
+            <argument name="customAttributesProcessor" xsi:type="object">Magento\Framework\Reflection\CustomAttributesProcessor\Proxy</argument>
+        </arguments>
+    </virtualType>
+
+    <type name="Magento\Webapi\Controller\Soap\Request\Handler">
+        <arguments>
+            <argument name="dataObjectProcessor" xsi:type="object">Magento\Framework\Reflection\DataObjectProcessorPermissionChecked</argument>
+        </arguments>
+    </type>
+    <!-- End of configuration to check that permissions are checked on fields -->
+
 </config>
diff --git a/app/code/Magento/Weee/Model/Observer.php b/app/code/Magento/Weee/Model/Observer.php
index daca840e903b7e2e8c0f11390a84c259a66ff837..08831d28845bc7fb62e842af1178310da6208160 100644
--- a/app/code/Magento/Weee/Model/Observer.php
+++ b/app/code/Magento/Weee/Model/Observer.php
@@ -194,4 +194,37 @@ class Observer extends \Magento\Framework\Model\AbstractModel
         $response->setTypes($types);
         return $this;
     }
+
+    /**
+     * Modify the options config for the front end to resemble the weee final price
+     *
+     * @param   \Magento\Framework\Event\Observer $observer
+     * @return  $this
+     */
+    public function getPriceConfiguration(\Magento\Framework\Event\Observer $observer)
+    {
+        if ($this->_weeeData->isEnabled()) {
+            $priceConfigObj=$observer->getData('configObj');
+            $priceConfig=$priceConfigObj->getConfig();
+            if (is_array($priceConfig)) {
+                foreach ($priceConfig as $keyConfigs => $configs) {
+                    if (is_array($configs)) {
+                        if (array_key_exists('prices', $configs)) {
+                            $priceConfig[$keyConfigs]['prices']['weeePrice'] = [
+                                'amount' => $configs['prices']['finalPrice']['amount'],
+                            ];
+                        } else {
+                            foreach ($configs as $keyConfig => $config) {
+                                $priceConfig[$keyConfigs][$keyConfig]['prices']['weeePrice'] = [
+                                    'amount' => $config['prices']['finalPrice']['amount'],
+                                ];
+                            }
+                        }
+                    }
+                }
+            }
+            $priceConfigObj->setConfig($priceConfig);
+        }
+        return $this;
+    }
 }
diff --git a/app/code/Magento/Weee/Pricing/Render/Adjustment.php b/app/code/Magento/Weee/Pricing/Render/Adjustment.php
index 2272c719cb94c6b6196e023f0540071693975efb..f13c044532f0514e0c230b8f51ae88b045d060b1 100644
--- a/app/code/Magento/Weee/Pricing/Render/Adjustment.php
+++ b/app/code/Magento/Weee/Pricing/Render/Adjustment.php
@@ -61,6 +61,14 @@ class Adjustment extends AbstractAdjustment
         return $this->toHtml();
     }
 
+    /**
+     * @return float
+     */
+    public function getRawFinalAmount()
+    {
+        return $this->finalAmount;
+    }
+
     /**
      * Obtain adjustment code
      *
diff --git a/app/code/Magento/Weee/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Weee/Test/Unit/Model/ObserverTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..63a13abb53622e5f93590a1586d695fb17e18707
--- /dev/null
+++ b/app/code/Magento/Weee/Test/Unit/Model/ObserverTest.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/**
+ * Test class for \Magento\Weee\Model\Observer
+ */
+namespace Magento\Weee\Test\Unit\Model;
+
+use \Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class ObserverTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Tests the methods that rely on the ScopeConfigInterface object to provide their return values
+     *
+     */
+    public function testGetPriceConfiguration()
+    {
+        $testArray=[
+            [
+                [
+                    'prices' =>
+                        [
+                            'finalPrice' => [
+                                    'amount' => 31.50,
+                            ],
+                        ],
+                ],
+                [
+                    'prices' =>
+                        [
+                            'finalPrice' =>[
+                                'amount' => 31.50,
+                            ],
+                        ],
+                ],
+            ],
+        ];
+
+        $configObj = new \Magento\Framework\Object(
+            [
+                'config' => $testArray,
+            ]
+        );
+
+        $testArrayWithWeee=$testArray;
+        $testArrayWithWeee[0][0]['prices']['weeePrice']= [
+            'amount' => $testArray[0][0]['prices']['finalPrice']['amount'],
+        ];
+        $testArrayWithWeee[0][1]['prices']['weeePrice']= [
+            'amount' => $testArray[0][1]['prices']['finalPrice']['amount'],
+        ];
+
+        $weeHelper=$this->getMock('Magento\Weee\Helper\Data', [], [], '', false);
+        $weeHelper->expects($this->any())
+            ->method('isEnabled')
+            ->will($this->returnValue(true));
+
+        $observerObject=$this->getMock('Magento\Framework\Event\Observer', [], [], '', false);
+
+        $observerObject->expects($this->any())
+            ->method('getData')
+            ->with('configObj')
+            ->will($this->returnValue($configObj));
+
+         $objectManager = new ObjectManager($this);
+         $weeeObserverObject = $objectManager->getObject(
+             'Magento\Weee\Model\Observer',
+             [
+                 'weeeData' => $weeHelper,
+             ]
+         );
+         $weeeObserverObject->getPriceConfiguration($observerObject);
+
+         $this->assertEquals($testArrayWithWeee, $configObj->getData('config'));
+    }
+}
diff --git a/app/code/Magento/Weee/etc/events.xml b/app/code/Magento/Weee/etc/events.xml
index b6ef0f804b1fd72258baff580d5257b90dd1a31e..f4300f38b2369817d96c1902c29c57d378676f17 100644
--- a/app/code/Magento/Weee/etc/events.xml
+++ b/app/code/Magento/Weee/etc/events.xml
@@ -9,4 +9,7 @@
     <event name="catalog_entity_attribute_save_before">
         <observer name="weee" instance="Magento\Weee\Model\Observer" method="assignBackendModelToAttribute" shared="false" />
     </event>
+    <event name="catalog_product_option_price_configuration_after">
+        <observer name="weee" instance="Magento\Weee\Model\Observer" method="getPriceConfiguration" shared="false" />
+    </event>
 </config>
diff --git a/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml b/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml
index 86267dea51a757c481bff1d110c6ce121cd41a5e..f8a77699ff4d9356fa99cf9eed5925efbc1b3a3d 100644
--- a/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml
+++ b/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml
@@ -28,5 +28,7 @@ $closeBrace = ')';
               data-label="<?php echo $block->renderWeeeTaxAttributeName($weeeTaxAttribute); ?>"><?php echo $block->renderWeeeTaxAttribute($weeeTaxAttribute); ?></span>
     <?php endforeach; ?>
     <span class="price-final_price"
+          data-price-type="weeePrice"
+          data-price-amount="<?php echo $block->getRawFinalAmount(); ?>"
           data-label="<?php echo __('Final Price'); ?>"><?php echo $block->getFinalAmount(); ?></span>
 <?php endif; ?>
diff --git a/app/etc/di.xml b/app/etc/di.xml
index c461dc254c841721a21146ff4010a00f848f1124..7ffd8702c787b917c82fb2073953559106c98119 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -964,6 +964,11 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Framework\Object\Copy\Config">
+        <arguments>
+            <argument name="dataStorage" xsi:type="object">Magento\Framework\Object\Copy\Config\Data\Proxy</argument>
+        </arguments>
+    </type>
     <type name="Magento\Framework\Object\Copy\Config\Reader">
         <arguments>
             <argument name="fileName" xsi:type="string">fieldset.xml</argument>
@@ -1101,4 +1106,9 @@
             </argument>
         </arguments>
     </type>
+    <type name="Magento\Framework\Url\Decoder">
+        <arguments>
+            <argument name="urlBuilder" xsi:type="object">Magento\Framework\UrlInterface\Proxy</argument>
+        </arguments>
+    </type>
 </config>
diff --git a/dev/tests/api-functional/_files/Magento/TestModule1/etc/data_object.xml b/dev/tests/api-functional/_files/Magento/TestModule1/etc/service_data_attributes.xml
similarity index 69%
rename from dev/tests/api-functional/_files/Magento/TestModule1/etc/data_object.xml
rename to dev/tests/api-functional/_files/Magento/TestModule1/etc/service_data_attributes.xml
index de2236e2195f380aedd5614447065bc9e480f7aa..fa96514a063b6112004e7a6073a2cc131a87f44c 100644
--- a/dev/tests/api-functional/_files/Magento/TestModule1/etc/data_object.xml
+++ b/dev/tests/api-functional/_files/Magento/TestModule1/etc/service_data_attributes.xml
@@ -5,13 +5,13 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\TestModule1\Service\V1\Entity\Item">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\TestModule1\Service\V1\Entity\Item">
         <attribute code="custom_attribute_data_object" type="Magento\TestModuleMSC\Model\Data\CustomAttributeDataObject" />
         <attribute code="custom_attribute_string" type="string" />
-    </custom_attributes>
-    <custom_attributes for="Magento\TestModuleMSC\Model\Data\CustomAttributeDataObject">
+    </extension_attributes>
+    <extension_attributes for="Magento\TestModuleMSC\Model\Data\CustomAttributeDataObject">
         <attribute code="custom_attribute_nested" type="Magento\TestModuleMSC\Model\Data\CustomAttributeNestedDataObject" />
         <attribute code="custom_attribute_int" type="int" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/data_object.xml b/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/service_data_attributes.xml
similarity index 69%
rename from dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/data_object.xml
rename to dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/service_data_attributes.xml
index ece2b1f581775daa25e1acec4f4fc120a876c431..f11e639ccd2753296d65071cf32f3e5fb47e0e7d 100644
--- a/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/data_object.xml
+++ b/dev/tests/api-functional/_files/Magento/TestModuleMSC/etc/service_data_attributes.xml
@@ -5,13 +5,13 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\TestModuleMSC\Api\Data\ItemInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\TestModuleMSC\Api\Data\ItemInterface">
         <attribute code="custom_attribute_data_object" type="Magento\TestModuleMSC\Api\Data\CustomAttributeDataObjectInterface" />
         <attribute code="custom_attribute_string" type="string" />
-    </custom_attributes>
-    <custom_attributes for="Magento\TestModuleMSC\Api\Data\CustomAttributeDataObjectInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\TestModuleMSC\Api\Data\CustomAttributeDataObjectInterface">
         <attribute code="custom_attribute_nested" type="Magento\TestModuleMSC\Api\Data\CustomAttributeNestedDataObjectInterface" />
         <attribute code="custom_attribute_int" type="int" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
index fb6588a7f4107cf2ff07095ff3820e442ee10a87..a0f06e0ce7aac3fa6752c18a2adb47af1ba2e544 100644
--- a/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Catalog/Api/ProductRepositoryInterfaceTest.php
@@ -126,7 +126,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
 
     public function testProductLinks()
     {
-        $this->markTestSkipped('Skipped until MAGETWO-35458 is ready');
         // Create simple product
         $productData =  [
             ProductInterface::SKU => "product_simple_500",
@@ -140,9 +139,10 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
         ];
 
         $this->saveProduct($productData);
+
         $productLinkData = ["product_sku" => "product_simple_with_related_500", "link_type" => "related",
                             "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple",
-                            "position" => 0];
+                            "position" => 0, "extension_attributes" => []];
         $productWithRelatedData =  [
             ProductInterface::SKU => "product_simple_with_related_500",
             ProductInterface::NAME => "Product Simple with Related 500",
@@ -166,7 +166,7 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
         // update link information
         $productLinkData = ["product_sku" => "product_simple_with_related_500", "link_type" => "upsell",
                             "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple",
-                            "position" => 0];
+                            "position" => 0, "extension_attributes" => []];
         $productWithUpsellData =  [
             ProductInterface::SKU => "product_simple_with_related_500",
             ProductInterface::NAME => "Product Simple with Related 500",
@@ -174,7 +174,6 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
             ProductInterface::TYPE_ID => 'simple',
             ProductInterface::PRICE => 100,
             ProductInterface::STATUS => 1,
-            ProductInterface::TYPE_ID => 'simple',
             ProductInterface::ATTRIBUTE_SET_ID => 4,
             "product_links" => [$productLinkData]
         ];
@@ -195,18 +194,15 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
             ProductInterface::TYPE_ID => 'simple',
             ProductInterface::PRICE => 100,
             ProductInterface::STATUS => 1,
-            ProductInterface::TYPE_ID => 'simple',
             ProductInterface::ATTRIBUTE_SET_ID => 4,
             "product_links" => []
         ];
 
         $this->saveProduct($productWithNoLinkData);
         $response = $this->getProduct("product_simple_with_related_500");
-
         $this->assertArrayHasKey('product_links', $response);
         $links = $response['product_links'];
-        $this->assertEquals(1, count($links));
-        $this->assertEquals([], $links[0]);
+        $this->assertEquals([], $links);
 
         $this->deleteProduct("product_simple_500");
         $this->deleteProduct("product_simple_with_related_500");
diff --git a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a5a6a9faa72c64b8ceb5dcce87fd81f55ec92f3
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/ProductRepositoryInterfaceTest.php
@@ -0,0 +1,398 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\CatalogInventory\Api;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\CatalogInventory\Api\Data\StockStatusInterface;
+use Magento\Framework\Api\ExtensibleDataInterface;
+use Magento\TestFramework\TestCase\WebapiAbstract;
+
+class ProductRepositoryInterfaceTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogProductRepositoryV1';
+    const SERVICE_VERSION = 'V1';
+    const RESOURCE_PATH = '/V1/products';
+
+    const KEY_EXTENSION_ATTRIBUTES = ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY;
+    const KEY_STOCK_ITEM = StockStatusInterface::STOCK_ITEM;
+    const KEY_QTY = StockStatusInterface::QTY;
+    const KEY_ITEM_ID = 'item_id';
+    const KEY_PRODUCT_ID = StockStatusInterface::PRODUCT_ID;
+    const KEY_WEBSITE_ID = StockStatusInterface::WEBSITE_ID;
+    const KEY_CUSTOM_ATTRIBUTES = 'custom_attributes';
+    const KEY_ATTRIBUTE_CODE = \Magento\Eav\Api\Data\AttributeInterface::ATTRIBUTE_CODE;
+    const CODE_QUANTITY_AND_STOCK_STATUS = 'quantity_and_stock_status';
+
+    const PRODUCT_SKU = 'sku-test-catalog-inventory';
+
+    /**
+     * Tests the 'happy path'
+     */
+    public function testCatalogInventory()
+    {
+        // create a simple product with catalog inventory
+        $qty = 1234;
+        $productData = $this->getSimpleProductData($qty);
+        $stockItemData = $this->getStockItemData($qty);
+        $this->assertArrayNotHasKey(self::KEY_ITEM_ID, $stockItemData);
+        $this->assertArrayNotHasKey(self::KEY_WEBSITE_ID, $stockItemData);
+        $this->assertArrayNotHasKey(self::KEY_PRODUCT_ID, $stockItemData);
+        $productData[self::KEY_EXTENSION_ATTRIBUTES] = $stockItemData;
+
+        $response = $this->saveProduct($productData);
+
+        $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response);
+        $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]));
+        $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM];
+        $returnedQty = $stockItemData[self::KEY_QTY];
+        $this->assertEquals($qty, $returnedQty, 'CREATE: Expected qty to be same: ' . $qty .', '. $returnedQty);
+        $this->assertArrayHasKey(self::KEY_ITEM_ID, $stockItemData);
+        $this->assertArrayHasKey(self::KEY_PRODUCT_ID, $stockItemData);
+        $this->assertArrayHasKey(self::KEY_WEBSITE_ID, $stockItemData);
+
+        // officially get the product
+        $response = $this->getProduct($productData[ProductInterface::SKU]);
+
+        $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM];
+        $returnedQty = $stockItemData[self::KEY_QTY];
+        $this->assertEquals($qty, $returnedQty, 'GET: Expected qty to be same: ' . $qty .', '. $returnedQty);
+
+        // update the catalog inventory
+        $qty = $this->getDifferent($qty);  // update the quantity
+        $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_QTY] = $qty;
+
+        $response = $this->updateProduct($response);
+
+        $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM];
+        $returnedQty = $stockItemData[self::KEY_QTY];
+        $this->assertEquals($qty, $returnedQty, 'UPDATE 1: Expected qty to be same: ' . $qty .', '. $returnedQty);
+
+        $customAttributeQty = $this->findCustomAttributeQty($response[self::KEY_CUSTOM_ATTRIBUTES]);
+        $this->assertTrue(!is_bool($customAttributeQty), 'Expected to find a quantity in the custom attributes');
+        $this->assertEquals(
+            $qty,
+            $customAttributeQty,
+            'UPDATE 1: Expected custom attribute qty to be updated: ' . $qty .', '. $customAttributeQty
+        );
+
+        // update the product without any mention of catalog inventory; no change expected for catalog inventory
+        // note: $qty expected to be the same as previously set, above
+        $newPrice = $this->getDifferent($response[ProductInterface::PRICE]);
+        $response[ProductInterface::PRICE] = $newPrice;
+        unset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]);
+
+        $response = $this->updateProduct($response);
+
+        $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response);
+        $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]));
+        $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM];
+        $returnedQty = $stockItemData[self::KEY_QTY];
+        $this->assertEquals($qty, $returnedQty, 'UPDATE 2: Expected qty to be same: ' . $qty .', '. $returnedQty);
+        $this->assertEquals($newPrice, $response[ProductInterface::PRICE]);
+
+        // delete the product; expect that all goes well
+        $response = $this->deleteProduct($productData[ProductInterface::SKU]);
+        $this->assertTrue($response);
+    }
+
+    /**
+     * Tests conditions that stray from the 'happy path'
+     */
+    public function testCatalogInventoryWithBogusData()
+    {
+        // create a simple product with catalog inventory
+        $qty = 666;
+        $productData = $this->getSimpleProductData($qty);
+        $stockItemData = $this->getStockItemData($qty);
+        $this->assertArrayNotHasKey(self::KEY_ITEM_ID, $stockItemData);
+        $this->assertArrayNotHasKey(self::KEY_WEBSITE_ID, $stockItemData);
+        $this->assertArrayNotHasKey(self::KEY_PRODUCT_ID, $stockItemData);
+        $productData[self::KEY_EXTENSION_ATTRIBUTES] = $stockItemData;
+
+        $response = $this->saveProduct($productData);
+
+        $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response);
+        $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]));
+        $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM];
+        $returnedQty = $stockItemData[self::KEY_QTY];
+        $this->assertEquals($qty, $returnedQty, 'POST 1: Expected qty to be same: ' . $qty .', '. $returnedQty);
+        $this->assertArrayHasKey(self::KEY_ITEM_ID, $stockItemData);
+        $this->assertArrayHasKey(self::KEY_PRODUCT_ID, $stockItemData);
+        $this->assertArrayHasKey(self::KEY_WEBSITE_ID, $stockItemData);
+
+        // re-save the catalog inventory:
+        // -- update quantity (which should be honored)
+        // -- supply an incorrect product id (which should be ignored, and be replaced with the actual one)
+        // -- supply an incorrect website id (which should be ignored, and be replaced with the actual one)
+        $qty = 777;  // update the quantity
+        $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_QTY] = $qty;
+
+        $originalProductId = $stockItemData[self::KEY_PRODUCT_ID];
+        $bogusProductId = $this->getDifferent($originalProductId);
+        $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_PRODUCT_ID] = $bogusProductId;
+
+        $originalWebsiteId = $stockItemData[self::KEY_WEBSITE_ID];
+        $bogusWebsiteId = $this->getDifferent($originalWebsiteId);
+        $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM][self::KEY_WEBSITE_ID] = $bogusWebsiteId;
+
+        $response = $this->saveProduct($response);
+
+        $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM];
+        $returnedQty = $stockItemData[self::KEY_QTY];
+        $this->assertEquals($qty, $returnedQty, 'POST 2: Expected qty to be same: ' . $qty .', '. $returnedQty);
+
+        $returnedProductId = $stockItemData[self::KEY_PRODUCT_ID];
+        $this->assertEquals($originalProductId, $returnedProductId);
+
+        $returnedWebsiteId = $stockItemData[self::KEY_WEBSITE_ID];
+        $this->assertEquals($originalWebsiteId, $returnedWebsiteId);
+
+        // delete the product; expect that all goes well
+        $response = $this->deleteProduct($productData[ProductInterface::SKU]);
+        $this->assertTrue($response);
+    }
+
+    /**
+     * Tests that creating a simple product has a side-effect of creating catalog inventory
+     */
+    public function testSimpleProductCreationWithoutSpecifyingCatalogInventory()
+    {
+        // create a simple product with catalog inventory
+        $qty = null;
+        $productData = $this->getSimpleProductData($qty);
+        $this->assertArrayNotHasKey(self::KEY_CUSTOM_ATTRIBUTES, $productData);
+        $this->assertArrayNotHasKey(self::KEY_EXTENSION_ATTRIBUTES, $productData);
+
+        $response = $this->saveProduct($productData);
+
+        $this->assertArrayHasKey(self::KEY_EXTENSION_ATTRIBUTES, $response);
+        $this->assertTrue(isset($response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM]));
+        $stockItemData = $response[self::KEY_EXTENSION_ATTRIBUTES][self::KEY_STOCK_ITEM];
+        $this->assertArrayHasKey(self::KEY_ITEM_ID, $stockItemData);
+        $this->assertArrayHasKey(self::KEY_PRODUCT_ID, $stockItemData);
+        $this->assertArrayHasKey(self::KEY_WEBSITE_ID, $stockItemData);
+
+        // delete the product; expect that all goes well
+        $response = $this->deleteProduct($productData[ProductInterface::SKU]);
+        $this->assertTrue($response);
+    }
+
+    // --- my helpers -----------------------------------------------------------------------------
+
+    /**
+     * Return a value that is different than the original one
+     *
+     * @param int $original
+     * @return int
+     */
+    protected function getDifferent($original)
+    {
+        return 1 + $original * $original;
+    }
+
+    /**
+     * Returns the product's quantity from the array of custom attributes.
+     * If no quantity can be found, will return false.
+     *
+     * @param array $customAttributes
+     * @return int|bool
+     */
+    protected function findCustomAttributeQty($customAttributes)
+    {
+        $qty = false;
+        $qtyAndStockStatus = [];
+        foreach ($customAttributes as $customAttribute) {
+            if ($customAttribute[self::KEY_ATTRIBUTE_CODE] == self::CODE_QUANTITY_AND_STOCK_STATUS) {
+                $qtyAndStockStatus = $customAttribute['value'];
+                break;
+            }
+        }
+        if (!empty($qtyAndStockStatus) && is_array($qtyAndStockStatus)) {
+
+            if (array_key_exists('any_type', $qtyAndStockStatus)) {
+                // for SOAP, need to use the inner array
+                $qtyAndStockStatus = $qtyAndStockStatus['any_type'];
+            }
+
+            // ex: [true, 1234]
+            if (is_bool($qtyAndStockStatus[0]) || is_string($qtyAndStockStatus[0])) {
+                $qty = $qtyAndStockStatus[1];
+            } else {
+                $qty = $qtyAndStockStatus[0];
+            }
+        }
+        return $qty;
+    }
+
+    /**
+     * Get Simple Product Data
+     *
+     * @param int $qty
+     * @return array
+     */
+    protected function getSimpleProductData($qty = 1000)
+    {
+        $productData = [
+            ProductInterface::SKU => self::PRODUCT_SKU,
+            ProductInterface::NAME => self::PRODUCT_SKU,
+            ProductInterface::VISIBILITY => 4,
+            ProductInterface::TYPE_ID => 'simple',
+            ProductInterface::PRICE => 10,
+            ProductInterface::STATUS => 1,
+            ProductInterface::ATTRIBUTE_SET_ID => 4,
+        ];
+
+        if ($qty != null) {
+            $productData[self::KEY_CUSTOM_ATTRIBUTES] = [
+                [self::KEY_ATTRIBUTE_CODE => 'description', 'value' => 'My Product Description'],
+                [self::KEY_ATTRIBUTE_CODE => self::CODE_QUANTITY_AND_STOCK_STATUS, 'value' => [true, $qty]],
+            ];
+        }
+
+        return $productData;
+    }
+
+    /**
+     * Get sample Stock Item data
+     *
+     * @param int $qty
+     * @return array
+     */
+    protected function getStockItemData($qty = 1000)
+    {
+        return [
+            self::KEY_STOCK_ITEM => [
+                self::KEY_QTY => $qty,
+                'is_in_stock' => true,
+                'is_qty_decimal' => false,
+                'show_default_notification_message' => false,
+                'use_config_min_qty' => true,
+                'min_qty' => 0,
+                'use_config_min_sale_qty' => 1,
+                'min_sale_qty' => 1,
+                'use_config_max_sale_qty' => true,
+                'max_sale_qty' => 10000,
+                'use_config_backorders' => true,
+                'backorders' => 0,
+                'use_config_notify_stock_qty' => true,
+                'notify_stock_qty' => 1,
+                'use_config_qty_increments' => true,
+                'qty_increments' => 0,
+                'use_config_enable_qty_inc' => false,
+                'enable_qty_increments' => false,
+                'use_config_manage_stock' => false,
+                'manage_stock' => true,
+                'low_stock_date' => "0",
+                'is_decimal_divided' => false,
+                'stock_status_changed_auto' => 0,
+            ]
+        ];
+    }
+
+    // --- common REST helpers --------------------------------------------------------------------
+
+    /**
+     * Get a product via its sku
+     *
+     * @param string $sku
+     * @return array the product data
+     */
+    protected function getProduct($sku)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $sku,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Get',
+            ],
+        ];
+
+        $response = $this->_webApiCall($serviceInfo, ['sku' => $sku]);
+        return $response;
+    }
+
+    /**
+     * Save a product
+     *
+     * @param array $product
+     * @return array the created product data
+     */
+    protected function saveProduct($product)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+
+    /**
+     * Update an existing product via its sku
+     *
+     * @param array $product
+     * @return array the product data, including any updates
+     */
+    protected function updateProduct($product)
+    {
+        $sku = $product[ProductInterface::SKU];
+        if (TESTS_WEB_API_ADAPTER == self::ADAPTER_REST) {
+            $product[ProductInterface::SKU] = null;
+        }
+
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $sku,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        $response =  $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+
+    /**
+     * Delete a product via its sku
+     *
+     * @param string $sku
+     * @return bool
+     */
+    protected function deleteProduct($sku)
+    {
+        $resourcePath = self::RESOURCE_PATH . '/' . $sku;
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => $resourcePath,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'deleteById',
+            ],
+        ];
+        $requestData = ['sku' => $sku];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php
index 0513f5e68abfdbe603bb5972604abb8d21453165..f8ef77f8e297b3bdf6a41c501c70adb40d31db99 100644
--- a/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/CatalogInventory/Api/StockItemTest.php
@@ -223,7 +223,7 @@ class StockItemTest extends WebapiAbstract
                     'enable_qty_increments' => '',
                     'use_config_manage_stock' => 1,
                     'manage_stock' => 1,
-                    'low_stock_date' => 0,
+                    'low_stock_date' => '',
                     'is_decimal_divided' => '',
                     'stock_status_changed_auto' => 0
                 ],
diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php
index 4551eaa3e45e2d713487938cf485344c5ff10b5c..bf9607d24969cae9ad70a61e46973c1ce2d95fb3 100644
--- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionRepositoryTest.php
@@ -166,7 +166,6 @@ class OptionRepositoryTest extends \Magento\TestFramework\TestCase\WebapiAbstrac
         ];
         $option = [
             'attribute_id' => 'test_configurable',
-            'type' => 'select',
             'label' => 'Test',
             'values' => [
                 [
diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionTypesListTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionTypesListTest.php
deleted file mode 100644
index a96d5ca79a5cb22ccf148210a39a37f0b545ba81..0000000000000000000000000000000000000000
--- a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/OptionTypesListTest.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-/**
- *
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\ConfigurableProduct\Api;
-
-class OptionTypesListTest extends \Magento\TestFramework\TestCase\WebapiAbstract
-{
-    const SERVICE_READ_NAME = 'configurableProductOptionTypesListV1';
-    const SERVICE_VERSION = 'V1';
-    const RESOURCE_PATH = '/V1/configurable-products/:sku/options/';
-
-    public function testGetTypes()
-    {
-        $expectedTypes = ['multiselect', 'select'];
-        $result = $this->getTypes();
-        $this->assertEquals($expectedTypes, $result);
-    }
-
-    /**
-     * @return array
-     */
-    protected function getTypes()
-    {
-        $serviceInfo = [
-            'rest' => [
-                'resourcePath' => str_replace(':sku/', '', self::RESOURCE_PATH) . 'types',
-                'httpMethod'   => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET
-            ],
-            'soap' => [
-                'service'        => self::SERVICE_READ_NAME,
-                'serviceVersion' => self::SERVICE_VERSION,
-                'operation'      => self::SERVICE_READ_NAME . 'GetItems'
-            ]
-        ];
-        return $this->_webApiCall($serviceInfo);
-    }
-}
diff --git a/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b9a2c658dfa3bd771656523fa6a3669ec2eaf411
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/ConfigurableProduct/Api/ProductRepositoryTest.php
@@ -0,0 +1,486 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\ConfigurableProduct\Api;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Framework\Api\ExtensibleDataInterface;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\TestCase\WebapiAbstract;
+
+/**
+ * Class ProductRepositoryTest for testing ConfigurableProduct integration
+ */
+class ProductRepositoryTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogProductRepositoryV1';
+    const SERVICE_VERSION = 'V1';
+    const RESOURCE_PATH = '/V1/products';
+    const CONFIGURABLE_PRODUCT_SKU = 'configurable-product-sku';
+
+    /**
+     * @var \Magento\Eav\Model\Config
+     */
+    protected $eavConfig;
+
+    /**
+     * @var \Magento\Framework\ObjectManagerInterface
+     */
+    protected $objectManager;
+
+    /**
+     * @var \Magento\Catalog\Model\Entity\Attribute
+     */
+    protected $configurableAttribute;
+
+    /**
+     * Execute per test initialization
+     */
+    public function setUp()
+    {
+        $this->objectManager = Bootstrap::getObjectManager();
+        $this->eavConfig = $this->objectManager->get('Magento\Eav\Model\Config');
+    }
+
+    /**
+     * Execute per test cleanup
+     */
+    public function tearDown()
+    {
+        $this->deleteProductBySku(self::CONFIGURABLE_PRODUCT_SKU);
+        parent::tearDown();
+    }
+
+    protected function getConfigurableAttributeOptions()
+    {
+        /** @var \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection $optionCollection */
+        $optionCollection = $this->objectManager->create(
+            'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection'
+        );
+        $options = $optionCollection->setAttributeFilter($this->configurableAttribute->getId())->getData();
+        return $options;
+    }
+
+    protected function createConfigurableProduct()
+    {
+        $productId1 = 10;
+        $productId2 = 20;
+
+        $label = "color";
+
+        $this->configurableAttribute = $this->eavConfig->getAttribute('catalog_product', 'test_configurable');
+        $this->assertNotNull($this->configurableAttribute);
+
+        $options = $this->getConfigurableAttributeOptions();
+        $this->assertEquals(2, count($options));
+
+        $configurableProductOptions = [
+            [
+                "attribute_id" =>  $this->configurableAttribute->getId(),
+                "label" => $label,
+                "position" => 0,
+                "values" => [
+                    [
+                        "pricing_value" => 10,
+                        "is_percent" =>  0,
+                        "value_index" =>  $options[0]['option_id'],
+                    ],
+                    [
+                        "pricing_value" => 5,
+                        "is_percent" =>  1,
+                        "value_index" =>  $options[1]['option_id'],
+                    ]
+                ],
+            ]
+        ];
+
+        $product = [
+            "sku" => self::CONFIGURABLE_PRODUCT_SKU,
+            "name" => self::CONFIGURABLE_PRODUCT_SKU,
+            "type_id" => "configurable",
+            "price" => 50,
+            'attribute_set_id' => 4,
+            "custom_attributes" => [
+                [
+                    "attribute_code" => $this->configurableAttribute->getAttributeCode(),
+                    "value" => $options[0]['option_id'],
+                ],
+            ],
+            "extension_attributes" => [
+                "configurable_product_options" => $configurableProductOptions,
+                "configurable_product_links" => [$productId1, $productId2],
+            ],
+        ];
+
+        $response = $this->createProduct($product);
+        return $response;
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
+    public function testCreateConfigurableProduct()
+    {
+        $productId1 = 10;
+        $productId2 = 20;
+        $label = "color";
+
+        $response = $this->createConfigurableProduct();
+        $this->assertEquals(self::CONFIGURABLE_PRODUCT_SKU, $response[ProductInterface::SKU]);
+        $this->assertEquals(50, $response['price']);
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"])
+        );
+        $resultConfigurableProductOptions
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"];
+        $this->assertEquals(1, count($resultConfigurableProductOptions));
+        $this->assertTrue(isset($resultConfigurableProductOptions[0]['label']));
+        $this->assertTrue(isset($resultConfigurableProductOptions[0]['id']));
+        $this->assertEquals($label, $resultConfigurableProductOptions[0]['label']);
+        $this->assertTrue(
+            isset($resultConfigurableProductOptions[0]['values'])
+        );
+        $this->assertEquals(2, count($resultConfigurableProductOptions[0]['values']));
+
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"])
+        );
+        $resultConfigurableProductLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"];
+        $this->assertEquals(2, count($resultConfigurableProductLinks));
+
+        $this->assertEquals([$productId1, $productId2], $resultConfigurableProductLinks);
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
+    public function testDeleteConfigurableProductOption()
+    {
+        $productId1 = 10;
+        $productId2 = 20;
+
+        $response = $this->createConfigurableProduct();
+        //delete existing option
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'] = [];
+        //leave the product links unchanged
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links']);
+        $response = $this->saveProduct($response);
+
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"])
+        );
+        $resultConfigurableProductOptions
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"];
+        $this->assertEquals(0, count($resultConfigurableProductOptions));
+
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"])
+        );
+        $resultConfigurableProductLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"];
+        $this->assertEquals(2, count($resultConfigurableProductLinks));
+
+        $this->assertEquals([$productId1, $productId2], $resultConfigurableProductLinks);
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
+    public function testUpdateConfigurableProductOption()
+    {
+        $productId1 = 10;
+        $newLabel = 'size';
+
+        $response = $this->createConfigurableProduct();
+        $option = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"][0];
+
+        $optionId = $option['id'];
+        $updatedOption = [
+            'id' => $optionId,
+            'attribute_id' => $option['attribute_id'],
+            'label' => $newLabel,
+            'position' => 1,
+            'values' => [
+                [
+                    'pricing_value' => 15,
+                    'is_percent' => 1,
+                    'value_index' => $option['values'][0]['value_index'],
+                ],
+            ],
+        ];
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'][0] =
+            $updatedOption;
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [$productId1];
+        $response = $this->saveProduct($response);
+
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"])
+        );
+        $resultConfigurableProductOptions
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"];
+        $this->assertEquals(1, count($resultConfigurableProductOptions));
+
+        $this->assertEquals($updatedOption, $resultConfigurableProductOptions[0]);
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
+    public function testUpdateConfigurableProductLinks()
+    {
+        $productId1 = 10;
+        $productId2 = 20;
+
+        $response = $this->createConfigurableProduct();
+        $options = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'];
+        //leave existing option untouched
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']);
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [$productId1];
+        $response = $this->saveProduct($response);
+
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"])
+        );
+        $resultConfigurableProductOptions
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_options"];
+        $this->assertEquals(1, count($resultConfigurableProductOptions));
+        //Since one product is removed, the available values for the option is reduced
+        $this->assertEquals(1, count($resultConfigurableProductOptions[0]['values']));
+
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"])
+        );
+        $resultConfigurableProductLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["configurable_product_links"];
+        $this->assertEquals(1, count($resultConfigurableProductLinks));
+        $this->assertEquals([$productId1], $resultConfigurableProductLinks);
+
+        //adding back the product links, the option value should be restored
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']);
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links']
+            = [$productId1, $productId2];
+        //set the value for required attribute
+        $response["custom_attributes"][] =
+        [
+            "attribute_code" => $this->configurableAttribute->getAttributeCode(),
+            "value" => $resultConfigurableProductOptions[0]['values'][0]['value_index'],
+        ];
+
+        $response = $this->saveProduct($response);
+        $currentOptions = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'];
+
+        $this->assertEquals($options, $currentOptions);
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
+    public function testUpdateConfigurableProductLinksWithNonExistingProduct()
+    {
+        $productId1 = 10;
+        $nonExistingId = 999;
+
+        $response = $this->createConfigurableProduct();
+        //leave existing option untouched
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']);
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [
+            $productId1, $nonExistingId
+        ];
+
+        $expectedMessage = 'Product with id "%1" does not exist.';
+        try {
+            $this->saveProduct($response);
+            $this->fail("Expected exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expectedMessage,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $errorObj = $this->processRestExceptionResult($e);
+            $this->assertEquals($expectedMessage, $errorObj['message']);
+            $this->assertEquals(['0' => '999'], $errorObj['parameters']);
+        }
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
+    public function testUpdateConfigurableProductLinksWithDuplicateAttributes()
+    {
+        $productId1 = 10;
+        $productId2 = 20;
+
+        $response = $this->createConfigurableProduct();
+        $options = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'];
+        //make product2 and product1 have the same value for the configurable attribute
+        $optionValue1 = $options[0]['values'][0]['value_index'];
+        $product2 = $this->getProduct('simple_' . $productId2);
+        $product2['custom_attributes'] = [
+            [
+                'attribute_code' => 'test_configurable',
+                'value' => $optionValue1,
+            ]
+        ];
+        $this->saveProduct($product2);
+
+        //leave existing option untouched
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options']);
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [
+            $productId1, $productId2
+        ];
+
+        $expectedMessage = 'Products "%1" and %2 have the same set of attribute values.';
+        try {
+            $this->saveProduct($response);
+            $this->fail("Expected exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expectedMessage,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $errorObj = $this->processRestExceptionResult($e);
+            $this->assertEquals($expectedMessage, $errorObj['message']);
+            $this->assertEquals(['0' => 20, '1' => 10], $errorObj['parameters']);
+        }
+    }
+
+    /**
+     * @magentoApiDataFixture Magento/ConfigurableProduct/_files/product_configurable.php
+     */
+    public function testUpdateConfigurableProductLinksWithWithoutVariationAttributes()
+    {
+        $productId1 = 10;
+        $productId2 = 20;
+
+        $response = $this->createConfigurableProduct();
+
+        /** delete all variation attribute */
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_options'] = [];
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]['configurable_product_links'] = [
+            $productId1, $productId2
+        ];
+
+        $expectedMessage = 'The configurable product does not have any variation attribute.';
+        try {
+            $this->saveProduct($response);
+            $this->fail("Expected exception");
+        } catch (\SoapFault $e) {
+            $this->assertContains(
+                $expectedMessage,
+                $e->getMessage(),
+                "SoapFault does not contain expected message."
+            );
+        } catch (\Exception $e) {
+            $errorObj = $this->processRestExceptionResult($e);
+            $this->assertEquals($expectedMessage, $errorObj['message']);
+        }
+    }
+
+    /**
+     * Get product
+     *
+     * @param string $productSku
+     * @return array the product data
+     */
+    protected function getProduct($productSku)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $productSku,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Get',
+            ],
+        ];
+
+        $response = (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ?
+            $this->_webApiCall($serviceInfo, ['sku' => $productSku]) : $this->_webApiCall($serviceInfo);
+
+        return $response;
+    }
+
+    /**
+     * Create product
+     *
+     * @param array $product
+     * @return array the created product data
+     */
+    protected function createProduct($product)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+
+    /**
+     * Delete a product by sku
+     *
+     * @param $productSku
+     * @return bool
+     */
+    protected function deleteProductBySku($productSku)
+    {
+        $resourcePath = self::RESOURCE_PATH . '/' . $productSku;
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => $resourcePath,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'deleteById',
+            ],
+        ];
+        $requestData = ["sku" => $productSku];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+
+    /**
+     * Save product
+     *
+     * @param array $product
+     * @return array the created product data
+     */
+    protected function saveProduct($product)
+    {
+        $resourcePath = self::RESOURCE_PATH . '/' . $product['sku'];
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => $resourcePath,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php
index 4234afc66f27883af89395192189e18bf3c154e0..656da53f38e842a1f3122b6fd35fbd87cdb24c05 100644
--- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/LinkRepositoryTest.php
@@ -80,9 +80,10 @@ class LinkRepositoryTest extends WebapiAbstract
     protected function getTargetProduct($isScopeGlobal = false)
     {
         $objectManager = Bootstrap::getObjectManager();
-        $product = $objectManager->get('Magento\Catalog\Model\ProductFactory')->create()->load(1);
         if ($isScopeGlobal) {
-            $product->setStoreId(0);
+            $product = $objectManager->get('Magento\Catalog\Model\ProductFactory')->create()->setStoreId(0)->load(1);
+        } else {
+            $product = $objectManager->get('Magento\Catalog\Model\ProductFactory')->create()->load(1);
         }
 
         return $product;
@@ -114,18 +115,18 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => true,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Title',
                 'sort_order' => 1,
                 'price' => 10.1,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_type' => 'file',
-                'link_file' => [
+                'link_file_content' => [
                     'file_data' => base64_encode(file_get_contents($this->testImagePath)),
                     'name' => 'image.jpg',
                 ],
-                'sample_file' => [
+                'sample_file_content' => [
                     'file_data' => base64_encode(file_get_contents($this->testImagePath)),
                     'name' => 'image.jpg',
                 ],
@@ -137,15 +138,15 @@ class LinkRepositoryTest extends WebapiAbstract
         $globalScopeLink = $this->getTargetLink($this->getTargetProduct(true), $newLinkId);
         $link = $this->getTargetLink($this->getTargetProduct(), $newLinkId);
         $this->assertNotNull($link);
-        $this->assertEquals($requestData['linkContent']['title'], $link->getTitle());
-        $this->assertEquals($requestData['linkContent']['title'], $globalScopeLink->getTitle());
-        $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder());
-        $this->assertEquals($requestData['linkContent']['price'], $link->getPrice());
-        $this->assertEquals($requestData['linkContent']['price'], $globalScopeLink->getPrice());
-        $this->assertEquals($requestData['linkContent']['shareable'], $link->getIsShareable());
-        $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads());
-        $this->assertEquals($requestData['linkContent']['link_type'], $link->getLinkType());
-        $this->assertEquals($requestData['linkContent']['sample_type'], $link->getSampleType());
+        $this->assertEquals($requestData['link']['title'], $link->getTitle());
+        $this->assertEquals($requestData['link']['title'], $globalScopeLink->getTitle());
+        $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder());
+        $this->assertEquals($requestData['link']['price'], $link->getPrice());
+        $this->assertEquals($requestData['link']['price'], $globalScopeLink->getPrice());
+        $this->assertEquals($requestData['link']['is_shareable'], $link->getIsShareable());
+        $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads());
+        $this->assertEquals($requestData['link']['link_type'], $link->getLinkType());
+        $this->assertEquals($requestData['link']['sample_type'], $link->getSampleType());
         $this->assertStringEndsWith('.jpg', $link->getSampleFile());
         $this->assertStringEndsWith('.jpg', $link->getLinkFile());
         $this->assertNull($link->getLinkUrl());
@@ -160,11 +161,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Store View Title',
                 'sort_order' => 1,
                 'price' => 150,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_url' => 'http://www.example.com/',
                 'link_type' => 'url',
@@ -177,15 +178,15 @@ class LinkRepositoryTest extends WebapiAbstract
         $link = $this->getTargetLink($this->getTargetProduct(), $newLinkId);
         $globalScopeLink = $this->getTargetLink($this->getTargetProduct(true), $newLinkId);
         $this->assertNotNull($link);
-        $this->assertEquals($requestData['linkContent']['title'], $link->getTitle());
-        $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder());
-        $this->assertEquals($requestData['linkContent']['price'], $link->getPrice());
-        $this->assertEquals($requestData['linkContent']['shareable'], $link->getIsShareable());
-        $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads());
-        $this->assertEquals($requestData['linkContent']['link_url'], $link->getLinkUrl());
-        $this->assertEquals($requestData['linkContent']['link_type'], $link->getLinkType());
-        $this->assertEquals($requestData['linkContent']['sample_url'], $link->getSampleUrl());
-        $this->assertEquals($requestData['linkContent']['sample_type'], $link->getSampleType());
+        $this->assertEquals($requestData['link']['title'], $link->getTitle());
+        $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder());
+        $this->assertEquals($requestData['link']['price'], $link->getPrice());
+        $this->assertEquals($requestData['link']['is_shareable'], $link->getIsShareable());
+        $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads());
+        $this->assertEquals($requestData['link']['link_url'], $link->getLinkUrl());
+        $this->assertEquals($requestData['link']['link_type'], $link->getLinkType());
+        $this->assertEquals($requestData['link']['sample_url'], $link->getSampleUrl());
+        $this->assertEquals($requestData['link']['sample_type'], $link->getSampleType());
         $this->assertEmpty($globalScopeLink->getTitle());
         $this->assertEmpty($globalScopeLink->getPrice());
     }
@@ -198,11 +199,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link with URL resources',
                 'sort_order' => 1,
                 'price' => 10.1,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_url' => 'http://www.example.com/',
                 'link_type' => 'url',
@@ -214,15 +215,15 @@ class LinkRepositoryTest extends WebapiAbstract
         $newLinkId = $this->_webApiCall($this->createServiceInfo, $requestData);
         $link = $this->getTargetLink($this->getTargetProduct(), $newLinkId);
         $this->assertNotNull($link);
-        $this->assertEquals($requestData['linkContent']['title'], $link->getTitle());
-        $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder());
-        $this->assertEquals($requestData['linkContent']['price'], $link->getPrice());
-        $this->assertEquals($requestData['linkContent']['shareable'], $link->getIsShareable());
-        $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads());
-        $this->assertEquals($requestData['linkContent']['link_url'], $link->getLinkUrl());
-        $this->assertEquals($requestData['linkContent']['link_type'], $link->getLinkType());
-        $this->assertEquals($requestData['linkContent']['sample_type'], $link->getSampleType());
-        $this->assertEquals($requestData['linkContent']['sample_url'], $link->getSampleUrl());
+        $this->assertEquals($requestData['link']['title'], $link->getTitle());
+        $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder());
+        $this->assertEquals($requestData['link']['price'], $link->getPrice());
+        $this->assertEquals($requestData['link']['is_shareable'], $link->getIsShareable());
+        $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads());
+        $this->assertEquals($requestData['link']['link_url'], $link->getLinkUrl());
+        $this->assertEquals($requestData['link']['link_type'], $link->getLinkType());
+        $this->assertEquals($requestData['link']['sample_type'], $link->getSampleType());
+        $this->assertEquals($requestData['link']['sample_url'], $link->getSampleUrl());
     }
 
     /**
@@ -235,12 +236,15 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link with URL resources',
                 'sort_order' => 1,
                 'price' => 10.1,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
+                'link_type' => 'invalid',
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com',
             ],
         ];
 
@@ -257,16 +261,16 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 1,
                 'price' => 10,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_type' => 'url',
                 'link_url' => 'http://www.example.com/',
                 'sample_type' => 'file',
-                'sample_file' => [
+                'sample_file_content' => [
                     'file_data' => 'not_a_base64_encoded_content',
                     'name' => 'image.jpg',
                 ],
@@ -286,17 +290,19 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 1,
                 'price' => 10,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_type' => 'file',
-                'link_file' => [
+                'link_file_content' => [
                     'file_data' => 'not_a_base64_encoded_content',
                     'name' => 'image.jpg',
                 ],
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com/',
             ],
         ];
 
@@ -313,17 +319,19 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Title',
                 'sort_order' => 15,
                 'price' => 10,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_type' => 'file',
-                'link_file' => [
+                'link_file_content' => [
                     'file_data' => base64_encode(file_get_contents($this->testImagePath)),
                     'name' => 'name/with|forbidden{characters',
                 ],
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com/',
             ],
         ];
 
@@ -340,16 +348,16 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 1,
                 'price' => 10,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_type' => 'url',
                 'link_url' => 'http://www.example.com/',
                 'sample_type' => 'file',
-                'sample_file' => [
+                'sample_file_content' => [
                     'file_data' => base64_encode(file_get_contents($this->testImagePath)),
                     'name' => 'name/with|forbidden{characters',
                 ],
@@ -369,14 +377,16 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 1,
                 'price' => 10,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'link_type' => 'url',
                 'link_url' => 'http://example<.>com/',
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com/',
             ],
         ];
 
@@ -393,11 +403,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 1,
                 'price' => 150,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 0,
                 'sample_type' => 'url',
                 'sample_url' => 'http://example<.>com/',
@@ -420,11 +430,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 1,
                 'price' => $linkPrice,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 0,
                 'sample_type' => 'url',
                 'sample_url' => 'http://example.com/',
@@ -442,7 +452,6 @@ class LinkRepositoryTest extends WebapiAbstract
     public function getInvalidLinkPrice()
     {
         return [
-            ['string_value'],
             [-1.5],
         ];
     }
@@ -458,11 +467,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => $sortOrder,
                 'price' => 10,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 0,
                 'sample_type' => 'url',
                 'sample_url' => 'http://example.com/',
@@ -494,11 +503,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 0,
                 'price' => 10,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => $numberOfDownloads,
                 'sample_type' => 'url',
                 'sample_url' => 'http://example.com/',
@@ -530,11 +539,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'simple',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 50,
                 'price' => 200,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 10,
                 'sample_type' => 'url',
                 'sample_url' => 'http://example.com/',
@@ -555,11 +564,11 @@ class LinkRepositoryTest extends WebapiAbstract
         $requestData = [
             'isGlobalScopeContent' => false,
             'sku' => 'wrong-sku',
-            'linkContent' => [
+            'link' => [
                 'title' => 'Link Title',
                 'sort_order' => 15,
                 'price' => 200,
-                'shareable' => true,
+                'is_shareable' => true,
                 'number_of_downloads' => 100,
                 'sample_type' => 'url',
                 'sample_url' => 'http://example.com/',
@@ -580,24 +589,26 @@ class LinkRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/{$linkId}";
         $requestData = [
             'isGlobalScopeContent' => false,
-            'linkId' => $linkId,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
+                'id' => $linkId,
                 'title' => 'Updated Title',
                 'sort_order' => 2,
                 'price' => 100.10,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 50,
+                'link_type' => 'url',
+                'sample_type' => 'url',
             ],
         ];
         $this->assertEquals($linkId, $this->_webApiCall($this->updateServiceInfo, $requestData));
         $link = $this->getTargetLink($this->getTargetProduct(), $linkId);
         $this->assertNotNull($link);
-        $this->assertEquals($requestData['linkContent']['title'], $link->getTitle());
-        $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder());
-        $this->assertEquals($requestData['linkContent']['price'], $link->getPrice());
-        $this->assertEquals($requestData['linkContent']['shareable'], (bool)$link->getIsShareable());
-        $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads());
+        $this->assertEquals($requestData['link']['title'], $link->getTitle());
+        $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder());
+        $this->assertEquals($requestData['link']['price'], $link->getPrice());
+        $this->assertEquals($requestData['link']['is_shareable'], (bool)$link->getIsShareable());
+        $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads());
     }
 
     /**
@@ -611,14 +622,16 @@ class LinkRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/{$linkId}";
         $requestData = [
             'isGlobalScopeContent' => true,
-            'linkId' => $linkId,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
+                'id' => $linkId,
                 'title' => 'Updated Title',
                 'sort_order' => 2,
                 'price' => 100.10,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 50,
+                'link_type' => 'url',
+                'sample_type' => 'url',
             ],
         ];
 
@@ -629,11 +642,11 @@ class LinkRepositoryTest extends WebapiAbstract
         // Title and price were set on store view level in fixture so they must be the same
         $this->assertEquals($originalLink->getTitle(), $link->getTitle());
         $this->assertEquals($originalLink->getPrice(), $link->getPrice());
-        $this->assertEquals($requestData['linkContent']['title'], $globalScopeLink->getTitle());
-        $this->assertEquals($requestData['linkContent']['price'], $globalScopeLink->getPrice());
-        $this->assertEquals($requestData['linkContent']['sort_order'], $link->getSortOrder());
-        $this->assertEquals($requestData['linkContent']['shareable'], (bool)$link->getIsShareable());
-        $this->assertEquals($requestData['linkContent']['number_of_downloads'], $link->getNumberOfDownloads());
+        $this->assertEquals($requestData['link']['title'], $globalScopeLink->getTitle());
+        $this->assertEquals($requestData['link']['price'], $globalScopeLink->getPrice());
+        $this->assertEquals($requestData['link']['sort_order'], $link->getSortOrder());
+        $this->assertEquals($requestData['link']['is_shareable'], (bool)$link->getIsShareable());
+        $this->assertEquals($requestData['link']['number_of_downloads'], $link->getNumberOfDownloads());
     }
 
     /**
@@ -645,14 +658,16 @@ class LinkRepositoryTest extends WebapiAbstract
         $this->updateServiceInfo['rest']['resourcePath'] = '/V1/products/wrong-sku/downloadable-links/1';
         $requestData = [
             'isGlobalScopeContent' => true,
-            'linkId' => 1,
             'sku' => 'wrong-sku',
-            'linkContent' => [
+            'link' => [
+                'id' => 1,
                 'title' => 'Updated Title',
                 'sort_order' => 2,
                 'price' => 100.10,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 50,
+                'link_type' => 'url',
+                'sample_type' => 'url',
             ],
         ];
         $this->_webApiCall($this->updateServiceInfo, $requestData);
@@ -670,14 +685,16 @@ class LinkRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/{$linkId}";
         $requestData = [
             'isGlobalScopeContent' => true,
-            'linkId' => 9999,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
+                'id' => $linkId,
                 'title' => 'Title',
                 'sort_order' => 2,
                 'price' => 100.10,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 50,
+                'link_type' => 'url',
+                'sample_type' => 'url',
             ],
         ];
 
@@ -697,14 +714,16 @@ class LinkRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/{$linkId}";
         $requestData = [
             'isGlobalScopeContent' => false,
-            'linkId' => $linkId,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
+                'id' => $linkId,
                 'title' => 'Updated Link Title',
                 'sort_order' => 2,
                 'price' => $linkPrice,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 50,
+                'link_type' => 'url',
+                'sample_type' => 'url',
             ],
         ];
 
@@ -724,14 +743,16 @@ class LinkRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/{$linkId}";
         $requestData = [
             'isGlobalScopeContent' => false,
-            'linkId' => $linkId,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
+                'id' => $linkId,
                 'title' => 'Updated Link Title',
                 'sort_order' => $sortOrder,
                 'price' => 100.50,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => 50,
+                'link_type' => 'url',
+                'sample_type' => 'url',
             ],
         ];
         $this->_webApiCall($this->updateServiceInfo, $requestData);
@@ -750,14 +771,16 @@ class LinkRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/{$linkId}";
         $requestData = [
             'isGlobalScopeContent' => false,
-            'linkId' => $linkId,
             'sku' => 'downloadable-product',
-            'linkContent' => [
+            'link' => [
+                'id' => $linkId,
                 'title' => 'Updated Link Title',
                 'sort_order' => 200,
                 'price' => 100.50,
-                'shareable' => false,
+                'is_shareable' => false,
                 'number_of_downloads' => $numberOfDownloads,
+                'link_type' => 'url',
+                'sample_type' => 'url',
             ],
         ];
         $this->_webApiCall($this->updateServiceInfo, $requestData);
@@ -771,7 +794,7 @@ class LinkRepositoryTest extends WebapiAbstract
         $linkId = $this->getTargetLink($this->getTargetProduct())->getId();
         $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/{$linkId}";
         $requestData = [
-            'linkId' => $linkId,
+            'id' => $linkId,
         ];
 
         $this->assertTrue($this->_webApiCall($this->deleteServiceInfo, $requestData));
@@ -788,7 +811,7 @@ class LinkRepositoryTest extends WebapiAbstract
         $linkId = 9999;
         $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/{$linkId}";
         $requestData = [
-            'linkId' => $linkId,
+            'id' => $linkId,
         ];
 
         $this->_webApiCall($this->deleteServiceInfo, $requestData);
diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a079eb6d4b6bfa58256a646f396a535c91a0e0cc
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/ProductRepositoryTest.php
@@ -0,0 +1,627 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Downloadable\Api;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Framework\Api\ExtensibleDataInterface;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\TestCase\WebapiAbstract;
+use Magento\Bundle\Api\Data\LinkInterface;
+
+/**
+ * Class ProductRepositoryTest for testing ProductRepository interface with Downloadable Product
+ */
+class ProductRepositoryTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogProductRepositoryV1';
+    const SERVICE_VERSION = 'V1';
+    const RESOURCE_PATH = '/V1/products';
+    const PRODUCT_SKU = 'sku-test-product-downloadable';
+
+    /**
+     * @var string
+     */
+    protected $testImagePath;
+
+    protected function setUp()
+    {
+        $this->testImagePath = __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test_image.jpg';
+    }
+
+    /**
+     * Execute per test cleanup
+     */
+    public function tearDown()
+    {
+        $this->deleteProductBySku(self::PRODUCT_SKU);
+        parent::tearDown();
+    }
+
+    protected function getLinkData()
+    {
+        return [
+            'link1' => [
+                'title' => "link1",
+                'sort_order'=> 10,
+                'is_shareable' => 1,
+                'price' => 2.0,
+                'number_of_downloads' => 0,
+                'link_type' => 'file',
+                'link_file_content' => [
+                    'name' => 'link1_content.jpg',
+                    'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+                ],
+                'sample_type' => 'file',
+                'sample_file_content' => [
+                    'name' => 'link1_sample.jpg',
+                    'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+                ],
+            ],
+            'link2' => [
+                'title' => 'link2',
+                'sort_order'=> 20,
+                'is_shareable' => 0,
+                'price' => 3.0,
+                'number_of_downloads' => 100,
+                'link_type' => "url",
+                'link_url' => 'http://www.example.com/link2.jpg',
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com/link2.jpg',
+            ],
+        ];
+    }
+
+    protected function getExpectedLinkData()
+    {
+        return [
+            [
+                'title' => 'link1',
+                'sort_order' => 10,
+                'is_shareable' => 1,
+                'price' => 2,
+                'number_of_downloads' => 0,
+                'link_type' => 'file',
+                'sample_type' => 'file',
+            ],
+            [
+                'title' => 'link2',
+                'sort_order' => 20,
+                'is_shareable' => 0,
+                'price' => 3,
+                'number_of_downloads' => 100,
+                'link_type' => 'url',
+                'link_url' => 'http://www.example.com/link2.jpg',
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com/link2.jpg',
+            ],
+        ];
+    }
+
+    protected function getSampleData()
+    {
+        return [
+            'sample1' => [
+                'title' => 'sample1',
+                'sort_order' => 10,
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com/sample1.jpg',
+            ],
+            'sample2' => [
+                'title' => 'sample2',
+                'sort_order' => 20,
+                'sample_type' => 'file',
+                'sample_file_content' => [
+                    'name' => 'sample2.jpg',
+                    'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+                ],
+            ],
+        ];
+    }
+
+    protected function getExpectedSampleData()
+    {
+        return [
+            [
+                'title' => 'sample1',
+                'sort_order' => 10,
+                'sample_type' => 'url',
+                'sample_url' => 'http://www.example.com/sample1.jpg',
+            ],
+            [
+                'title' => 'sample2',
+                'sort_order' => 20,
+                'sample_type' => 'file',
+            ],
+        ];
+    }
+
+    protected function createDownloadableProduct()
+    {
+        $product = [
+            "sku" => self::PRODUCT_SKU,
+            "name" => self::PRODUCT_SKU,
+            "type_id" => \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE,
+            "price" => 10,
+            'attribute_set_id' => 4,
+            "extension_attributes" => [
+                "downloadable_product_links" => array_values($this->getLinkData()),
+                "downloadable_product_samples" => array_values($this->getSampleData()),
+            ],
+        ];
+
+        $response =  $this->createProduct($product);
+        $this->assertEquals(self::PRODUCT_SKU, $response[ProductInterface::SKU]);
+        $this->assertEquals(10, $response['price']);
+        $this->assertEquals(
+            \Magento\Downloadable\Model\Product\Type::TYPE_DOWNLOADABLE,
+            $response['type_id']
+        );
+        return $response;
+    }
+
+    /**
+     * Create a downloadable product with two links and two samples
+     */
+    public function testCreateDownloadableProduct()
+    {
+        $response = $this->createDownloadableProduct();
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"])
+        );
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"])
+        );
+        $resultLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"];
+        $this->assertEquals(2, count($resultLinks));
+        $this->assertTrue(isset($resultLinks[0]['id']));
+        $this->assertTrue(isset($resultLinks[0]['link_file']));
+        $this->assertTrue(isset($resultLinks[0]['sample_file']));
+        unset($resultLinks[0]['id']);
+        unset($resultLinks[0]['link_file']);
+        unset($resultLinks[0]['sample_file']);
+        $this->assertTrue(isset($resultLinks[1]['id']));
+        unset($resultLinks[1]['id']);
+
+        $expectedLinkData = $this->getExpectedLinkData();
+        $this->assertEquals($expectedLinkData, $resultLinks);
+
+        $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"];
+        $this->assertEquals(2, count($resultSamples));
+        $this->assertTrue(isset($resultSamples[0]['id']));
+        unset($resultSamples[0]['id']);
+        $this->assertTrue(isset($resultSamples[1]['id']));
+        $this->assertTrue(isset($resultSamples[1]['sample_file']));
+        unset($resultSamples[1]['sample_file']);
+        unset($resultSamples[1]['id']);
+
+        $expectedSampleData = $this->getExpectedSampleData();
+        $this->assertEquals($expectedSampleData, $resultSamples);
+    }
+
+    /**
+     * Update downloadable product, update a link, add two link, delete a link
+     */
+    public function testUpdateDownloadableProductLinks()
+    {
+        $response = $this->createDownloadableProduct();
+        $resultLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"];
+        $link1Id = $resultLinks[0]['id'];
+        $link2Id = $resultLinks[1]['id'];
+
+        $linkFile = $resultLinks[0]['link_file'];
+        $sampleFile = $resultLinks[0]['sample_file'];
+        $updatedLink1Data = [
+            'id' => $link1Id,
+            'title' => 'link1_updated',
+            'sort_order' => 1, //the sort order needs to be smaller than 10
+            'is_shareable' => 0,
+            'price' => 5.0,
+            'number_of_downloads' => 999,
+            'link_type' => 'file',
+            'sample_type' => 'file',
+            'link_file' =>  'http://www.example.com/invalid', //this field will be overridden
+            'sample_file' => 'http://www.example.com/invalid', //this field will be overridden
+        ];
+        $linkData = $this->getLinkData();
+
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"] =
+            [$updatedLink1Data, $linkData['link1'], $linkData['link2']];
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]);
+
+        $response = $this->saveProduct($response);
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"])
+        );
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"])
+        );
+        $resultLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"];
+
+        $this->assertEquals(3, count($resultLinks));
+        $this->assertTrue(isset($resultLinks[0]['id']));
+        $this->assertEquals($link1Id, $resultLinks[0]['id']);
+        $this->assertTrue(isset($resultLinks[0]['link_file']));
+        $this->assertEquals($linkFile, $resultLinks[0]['link_file']);
+        $this->assertTrue(isset($resultLinks[0]['sample_file']));
+        $this->assertEquals($sampleFile, $resultLinks[0]['sample_file']);
+        unset($resultLinks[0]['id']);
+        unset($resultLinks[0]['link_file']);
+        unset($resultLinks[0]['sample_file']);
+        $this->assertTrue(isset($resultLinks[1]['id']));
+        $this->assertGreaterThan($link2Id, $resultLinks[1]['id']);
+        $this->assertTrue(isset($resultLinks[1]['link_file']));
+        $this->assertTrue(isset($resultLinks[1]['sample_file']));
+        unset($resultLinks[1]['id']);
+        unset($resultLinks[1]['link_file']);
+        unset($resultLinks[1]['sample_file']);
+        $this->assertTrue(isset($resultLinks[2]['id']));
+        $this->assertGreaterThan($link2Id, $resultLinks[2]['id']);
+        unset($resultLinks[2]['id']);
+
+        $expectedLinkData[] = [
+            'title' => 'link1_updated',
+            'sort_order' => 1, //the sort order needs to be smaller than 10
+            'is_shareable' => 0,
+            'price' => 5.0,
+            'number_of_downloads' => 999,
+            'link_type' => 'file',
+            'sample_type' => 'file',
+        ];
+        $expectedLinkData = array_merge($expectedLinkData, $this->getExpectedLinkData());
+        $this->assertEquals($expectedLinkData, $resultLinks);
+
+        $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"];
+        $this->assertEquals(2, count($resultSamples));
+    }
+
+    /**
+     * Update downloadable product, update two links and change file content
+     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+     */
+    public function testUpdateDownloadableProductLinksWithNewFile()
+    {
+        $response = $this->createDownloadableProduct();
+        $resultLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"];
+        $link1Id = $resultLinks[0]['id'];
+        $link2Id = $resultLinks[1]['id'];
+
+        $linkFile = 'link1_content_updated.jpg';
+        $sampleFile = 'link1_sample_updated.jpg';
+        $updatedLink1Data = [
+            'id' => $link1Id,
+            'title' => 'link1_updated',
+            'sort_order' => 1, //the sort order needs to be smaller than 10
+            'is_shareable' => 0,
+            'price' => 5.0,
+            'number_of_downloads' => 999,
+            'link_type' => 'file',
+            'link_file_content' => [
+                'name' => $linkFile,
+                'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+            ],
+            'sample_type' => 'file',
+            'sample_file_content' => [
+                'name' => $sampleFile,
+                'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+            ],
+        ];
+        $updatedLink2Data = [
+            'id' => $link2Id,
+            'title' => 'link2_updated',
+            'sort_order' => 2,
+            'is_shareable' => 0,
+            'price' => 6.0,
+            'number_of_downloads' => 0,
+            'link_type' => 'file',
+            'link_file_content' => [
+                'name' => 'link2_content.jpg',
+                'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+            ],
+            'sample_type' => 'file',
+            'sample_file_content' => [
+                'name' => 'link2_sample.jpg',
+                'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+            ],
+        ];
+
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"] =
+            [$updatedLink1Data, $updatedLink2Data];
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"]);
+
+        $response = $this->saveProduct($response);
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"])
+        );
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"])
+        );
+        $resultLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"];
+
+        $this->assertEquals(2, count($resultLinks));
+        $this->assertTrue(isset($resultLinks[0]['id']));
+        $this->assertEquals($link1Id, $resultLinks[0]['id']);
+        $this->assertTrue(isset($resultLinks[0]['link_file']));
+        $this->assertGreaterThan(0, strpos($resultLinks[0]['link_file'], $linkFile));
+        $this->assertTrue(isset($resultLinks[0]['sample_file']));
+        $this->assertGreaterThan(0, strpos($resultLinks[0]['sample_file'], $sampleFile));
+        unset($resultLinks[0]['id']);
+        unset($resultLinks[0]['link_file']);
+        unset($resultLinks[0]['sample_file']);
+        $this->assertTrue(isset($resultLinks[1]['id']));
+        $this->assertEquals($link2Id, $resultLinks[1]['id']);
+        $this->assertTrue(isset($resultLinks[1]['link_file']));
+        $this->assertTrue(isset($resultLinks[1]['sample_file']));
+        unset($resultLinks[1]['id']);
+        unset($resultLinks[1]['link_file']);
+        unset($resultLinks[1]['sample_file']);
+
+        $expectedLinkData = [
+            [
+                'title' => 'link1_updated',
+                'sort_order' => 1, //the sort order needs to be smaller than 10
+                'is_shareable' => 0,
+                'price' => 5.0,
+                'number_of_downloads' => 999,
+                'link_type' => 'file',
+                'sample_type' => 'file',
+            ],
+            [
+                'title' => 'link2_updated',
+                'sort_order' => 2,
+                'is_shareable' => 0,
+                'price' => 6.0,
+                'number_of_downloads' => 0,
+                'link_type' => 'file',
+                'sample_type' => 'file',
+                'link_url' => 'http://www.example.com/link2.jpg', //urls are still saved, just not used
+                'sample_url' => 'http://www.example.com/link2.jpg',
+            ]
+        ];
+        $this->assertEquals($expectedLinkData, $resultLinks);
+
+        $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"];
+        $this->assertEquals(2, count($resultSamples));
+    }
+
+    public function testUpdateDownloadableProductSamples()
+    {
+        $response = $this->createDownloadableProduct();
+
+        $resultSample
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"];
+        $sample1Id = $resultSample[0]['id'];
+        $sample2Id = $resultSample[1]['id'];
+
+        $updatedSample1Data = [
+            'id' => $sample1Id,
+            'title' => 'sample1_updated',
+            'sort_order' => 1,
+            'sample_type' => 'url',
+            'sample_url' => 'http://www.example.com/sample1.jpg',
+        ];
+        $sampleData = $this->getSampleData();
+
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]);
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"] =
+            [$updatedSample1Data, $sampleData['sample1'], $sampleData['sample2']];
+
+        $response = $this->saveProduct($response);
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"])
+        );
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"])
+        );
+        $resultLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"];
+
+        $this->assertEquals(2, count($resultLinks));
+
+        $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"];
+        $this->assertEquals(3, count($resultSamples));
+        $this->assertTrue(isset($resultSamples[0]['id']));
+        $this->assertEquals($sample1Id, $resultSamples[0]['id']);
+        unset($resultSamples[0]['id']);
+        $this->assertTrue(isset($resultSamples[1]['id']));
+        $this->assertGreaterThan($sample2Id, $resultSamples[1]['id']);
+        unset($resultSamples[1]['id']);
+        $this->assertTrue(isset($resultSamples[2]['id']));
+        $this->assertGreaterThan($sample2Id, $resultSamples[2]['id']);
+        $this->assertTrue(isset($resultSamples[2]['sample_file']));
+        unset($resultSamples[2]['sample_file']);
+        unset($resultSamples[2]['id']);
+
+        $expectedSampleData[] = [
+            'title' => 'sample1_updated',
+            'sort_order' => 1,
+            'sample_type' => 'url',
+            'sample_url' => 'http://www.example.com/sample1.jpg',
+        ];
+        $expectedSampleData = array_merge($expectedSampleData, $this->getExpectedSampleData());
+        $this->assertEquals($expectedSampleData, $resultSamples);
+    }
+
+    public function testUpdateDownloadableProductSamplesWithNewFile()
+    {
+        $response = $this->createDownloadableProduct();
+
+        $resultSample
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"];
+        $sample1Id = $resultSample[0]['id'];
+        $sample2Id = $resultSample[1]['id'];
+
+        //upload a file for sample 1
+        $updatedSample1Data = [
+            'id' => $sample1Id,
+            'title' => 'sample1_updated',
+            'sort_order' => 1,
+            'sample_type' => 'file',
+            'sample_file_content' => [
+                'name' => 'sample1.jpg',
+                'file_data' => base64_encode(file_get_contents($this->testImagePath)),
+            ],
+        ];
+        //change title for sample 2
+        $updatedSamp2e1Data = [
+            'id' => $sample2Id,
+            'title' => 'sample2_updated',
+            'sort_order' => 2,
+            'sample_type' => 'file',
+        ];
+
+        unset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"]);
+        $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"] =
+            [$updatedSample1Data, $updatedSamp2e1Data];
+
+        $response = $this->saveProduct($response);
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"])
+        );
+        $this->assertTrue(
+            isset($response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"])
+        );
+        $resultLinks
+            = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_links"];
+
+        $this->assertEquals(2, count($resultLinks));
+
+        $resultSamples = $response[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]["downloadable_product_samples"];
+        $this->assertEquals(2, count($resultSamples));
+        $this->assertTrue(isset($resultSamples[0]['id']));
+        $this->assertEquals($sample1Id, $resultSamples[0]['id']);
+        unset($resultSamples[0]['id']);
+        $this->assertTrue(isset($resultSamples[0]['sample_file']));
+        $this->assertContains('sample1.jpg', $resultSamples[0]['sample_file']);
+        unset($resultSamples[0]['sample_file']);
+        $this->assertTrue(isset($resultSamples[1]['id']));
+        $this->assertEquals($sample2Id, $resultSamples[1]['id']);
+        unset($resultSamples[1]['id']);
+        $this->assertTrue(isset($resultSamples[1]['sample_file']));
+        $this->assertContains('sample2.jpg', $resultSamples[1]['sample_file']);
+        unset($resultSamples[1]['sample_file']);
+
+        $expectedSampleData = [
+            [
+                'title' => 'sample1_updated',
+                'sort_order' => 1,
+                'sample_type' => 'file',
+                'sample_url' => 'http://www.example.com/sample1.jpg',
+            ],
+            [
+                'title' => 'sample2_updated',
+                'sort_order' => 2,
+                'sample_type' => 'file',
+            ],
+        ];
+        $this->assertEquals($expectedSampleData, $resultSamples);
+    }
+
+    /**
+     * Get product
+     *
+     * @param string $productSku
+     * @return array the product data
+     */
+    protected function getProduct($productSku)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $productSku,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Get',
+            ],
+        ];
+
+        $response = (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ?
+            $this->_webApiCall($serviceInfo, ['sku' => $productSku]) : $this->_webApiCall($serviceInfo);
+
+        return $response;
+    }
+
+    /**
+     * Create product
+     *
+     * @param array $product
+     * @return array the created product data
+     */
+    protected function createProduct($product)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+
+    /**
+     * Delete a product by sku
+     *
+     * @param $productSku
+     * @return bool
+     */
+    protected function deleteProductBySku($productSku)
+    {
+        $resourcePath = self::RESOURCE_PATH . '/' . $productSku;
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => $resourcePath,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'deleteById',
+            ],
+        ];
+        $requestData = ["sku" => $productSku];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+
+    /**
+     * Save product
+     *
+     * @param array $product
+     * @return array the created product data
+     */
+    protected function saveProduct($product)
+    {
+        $resourcePath = self::RESOURCE_PATH . '/' . $product['sku'];
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => $resourcePath,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        $response = $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php
index c4fe8606cb8bc9f680e3db3da0bdc4061941a05c..0a1bba69a89890b7feeec2afd84a793181e2d239 100644
--- a/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Downloadable/Api/SampleRepositoryTest.php
@@ -79,9 +79,11 @@ class SampleRepositoryTest extends WebapiAbstract
      */
     protected function getTargetProduct($isScopeGlobal = false)
     {
-        $product = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\ProductFactory')->create()->load(1);
         if ($isScopeGlobal) {
-            $product->setStoreId(0);
+            $product = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\ProductFactory')
+                ->create()->setStoreId(0)->load(1);
+        } else {
+            $product = Bootstrap::getObjectManager()->get('Magento\Catalog\Model\ProductFactory')->create()->load(1);
         }
 
         return $product;
@@ -120,11 +122,11 @@ class SampleRepositoryTest extends WebapiAbstract
     {
         $requestData = [
             'isGlobalScopeContent' => true,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Title',
                 'sort_order' => 1,
-                'sample_file' => [
+                'sample_file_content' => [
                     'file_data' => base64_encode(file_get_contents($this->testImagePath)),
                     'name' => 'image.jpg',
                 ],
@@ -136,10 +138,11 @@ class SampleRepositoryTest extends WebapiAbstract
         $globalScopeSample = $this->getTargetSample($this->getTargetProduct(true), $newSampleId);
         $sample = $this->getTargetSample($this->getTargetProduct(), $newSampleId);
         $this->assertNotNull($sample);
-        $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle());
-        $this->assertEquals($requestData['sampleContent']['title'], $globalScopeSample->getTitle());
-        $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder());
-        $this->assertEquals($requestData['sampleContent']['sample_type'], $sample->getSampleType());
+        $this->assertNotNull($sample->getId());
+        $this->assertEquals($requestData['sample']['title'], $sample->getTitle());
+        $this->assertEquals($requestData['sample']['title'], $globalScopeSample->getTitle());
+        $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder());
+        $this->assertEquals($requestData['sample']['sample_type'], $sample->getSampleType());
         $this->assertStringEndsWith('.jpg', $sample->getSampleFile());
         $this->assertNull($sample->getSampleUrl());
     }
@@ -151,8 +154,8 @@ class SampleRepositoryTest extends WebapiAbstract
     {
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Store View Title',
                 'sort_order' => 1,
                 'sample_url' => 'http://www.sample.example.com/',
@@ -164,10 +167,10 @@ class SampleRepositoryTest extends WebapiAbstract
         $sample = $this->getTargetSample($this->getTargetProduct(), $newSampleId);
         $globalScopeSample = $this->getTargetSample($this->getTargetProduct(true), $newSampleId);
         $this->assertNotNull($sample);
-        $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle());
-        $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder());
-        $this->assertEquals($requestData['sampleContent']['sample_url'], $sample->getSampleUrl());
-        $this->assertEquals($requestData['sampleContent']['sample_type'], $sample->getSampleType());
+        $this->assertEquals($requestData['sample']['title'], $sample->getTitle());
+        $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder());
+        $this->assertEquals($requestData['sample']['sample_url'], $sample->getSampleUrl());
+        $this->assertEquals($requestData['sample']['sample_type'], $sample->getSampleType());
         $this->assertEmpty($globalScopeSample->getTitle());
     }
 
@@ -178,8 +181,8 @@ class SampleRepositoryTest extends WebapiAbstract
     {
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Sample with URL resource',
                 'sort_order' => 1,
                 'sample_url' => 'http://www.sample.example.com/',
@@ -190,10 +193,10 @@ class SampleRepositoryTest extends WebapiAbstract
         $newSampleId = $this->_webApiCall($this->createServiceInfo, $requestData);
         $sample = $this->getTargetSample($this->getTargetProduct(), $newSampleId);
         $this->assertNotNull($sample);
-        $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle());
-        $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder());
-        $this->assertEquals($requestData['sampleContent']['sample_type'], $sample->getSampleType());
-        $this->assertEquals($requestData['sampleContent']['sample_url'], $sample->getSampleUrl());
+        $this->assertEquals($requestData['sample']['title'], $sample->getTitle());
+        $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder());
+        $this->assertEquals($requestData['sample']['sample_type'], $sample->getSampleType());
+        $this->assertEquals($requestData['sample']['sample_url'], $sample->getSampleUrl());
     }
 
     /**
@@ -201,14 +204,15 @@ class SampleRepositoryTest extends WebapiAbstract
      * @expectedException \Exception
      * @expectedExceptionMessage Invalid sample type.
      */
-    public function testCreateThrowsExceptionIfSampleTypeIsNotSpecified()
+    public function testCreateThrowsExceptionIfSampleTypeIsInvalid()
     {
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Sample with URL resource',
                 'sort_order' => 1,
+                'sample_type' => 'invalid',
             ],
         ];
 
@@ -224,12 +228,12 @@ class SampleRepositoryTest extends WebapiAbstract
     {
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Sample Title',
                 'sort_order' => 1,
                 'sample_type' => 'file',
-                'sample_file' => [
+                'sample_file_content' => [
                     'file_data' => 'not_a_base64_encoded_content',
                     'name' => 'image.jpg',
                 ],
@@ -248,12 +252,12 @@ class SampleRepositoryTest extends WebapiAbstract
     {
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Title',
                 'sort_order' => 15,
                 'sample_type' => 'file',
-                'sample_file' => [
+                'sample_file_content' => [
                     'file_data' => base64_encode(file_get_contents($this->testImagePath)),
                     'name' => 'name/with|forbidden{characters',
                 ],
@@ -272,8 +276,8 @@ class SampleRepositoryTest extends WebapiAbstract
     {
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Sample Title',
                 'sort_order' => 1,
                 'sample_type' => 'url',
@@ -294,8 +298,8 @@ class SampleRepositoryTest extends WebapiAbstract
     {
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
                 'title' => 'Sample Title',
                 'sort_order' => $sortOrder,
                 'sample_type' => 'url',
@@ -325,8 +329,8 @@ class SampleRepositoryTest extends WebapiAbstract
         $this->createServiceInfo['rest']['resourcePath'] = '/V1/products/simple/downloadable-links/samples';
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'simple',
-            'sampleContent' => [
+            'sku' => 'simple',
+            'sample' => [
                 'title' => 'Sample Title',
                 'sort_order' => 50,
                 'sample_type' => 'url',
@@ -345,8 +349,8 @@ class SampleRepositoryTest extends WebapiAbstract
         $this->createServiceInfo['rest']['resourcePath'] = '/V1/products/wrong-sku/downloadable-links/samples';
         $requestData = [
             'isGlobalScopeContent' => false,
-            'productSku' => 'wrong-sku',
-            'sampleContent' => [
+            'sku' => 'wrong-sku',
+            'sample' => [
                 'title' => 'Title',
                 'sort_order' => 15,
                 'sample_type' => 'url',
@@ -366,19 +370,21 @@ class SampleRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}";
         $requestData = [
             'isGlobalScopeContent' => false,
-            'sampleId' => $sampleId,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
+                'id' => $sampleId,
                 'title' => 'Updated Title',
                 'sort_order' => 2,
+                'sample_type' => 'url',
             ],
         ];
 
         $this->assertEquals($sampleId, $this->_webApiCall($this->updateServiceInfo, $requestData));
         $sample = $this->getTargetSample($this->getTargetProduct(), $sampleId);
         $this->assertNotNull($sample);
-        $this->assertEquals($requestData['sampleContent']['title'], $sample->getTitle());
-        $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder());
+        $this->assertEquals($requestData['sample']['id'], $sample->getId());
+        $this->assertEquals($requestData['sample']['title'], $sample->getTitle());
+        $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder());
     }
 
     /**
@@ -392,11 +398,12 @@ class SampleRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}";
         $requestData = [
             'isGlobalScopeContent' => true,
-            'sampleId' => $sampleId,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
+                'id' => $sampleId,
                 'title' => 'Updated Title',
                 'sort_order' => 2,
+                'sample_type' => 'url',
             ],
         ];
 
@@ -406,8 +413,8 @@ class SampleRepositoryTest extends WebapiAbstract
         $this->assertNotNull($sample);
         // Title was set on store view level in fixture so it must be the same
         $this->assertEquals($originalSample->getTitle(), $sample->getTitle());
-        $this->assertEquals($requestData['sampleContent']['title'], $globalScopeSample->getTitle());
-        $this->assertEquals($requestData['sampleContent']['sort_order'], $sample->getSortOrder());
+        $this->assertEquals($requestData['sample']['title'], $globalScopeSample->getTitle());
+        $this->assertEquals($requestData['sample']['sort_order'], $sample->getSortOrder());
     }
 
     /**
@@ -419,11 +426,12 @@ class SampleRepositoryTest extends WebapiAbstract
         $this->updateServiceInfo['rest']['resourcePath'] = '/V1/products/wrong-sku/downloadable-links/samples/1';
         $requestData = [
             'isGlobalScopeContent' => true,
-            'sampleId' => 1,
-            'productSku' => 'wrong-sku',
-            'sampleContent' => [
+            'sku' => 'wrong-sku',
+            'sample' => [
+                'id' => 1,
                 'title' => 'Updated Title',
                 'sort_order' => 2,
+                'sample_type' => 'url',
             ],
         ];
         $this->_webApiCall($this->updateServiceInfo, $requestData);
@@ -441,11 +449,12 @@ class SampleRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}";
         $requestData = [
             'isGlobalScopeContent' => true,
-            'sampleId' => 9999,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
+                'id' => $sampleId,
                 'title' => 'Title',
                 'sort_order' => 2,
+                'sample_type' => 'url',
             ],
         ];
 
@@ -465,11 +474,12 @@ class SampleRepositoryTest extends WebapiAbstract
             = "/V1/products/downloadable-product/downloadable-links/samples/{$sampleId}";
         $requestData = [
             'isGlobalScopeContent' => false,
-            'sampleId' => $sampleId,
-            'productSku' => 'downloadable-product',
-            'sampleContent' => [
+            'sku' => 'downloadable-product',
+            'sample' => [
+                'id' => $sampleId,
                 'title' => 'Updated Sample Title',
                 'sort_order' => $sortOrder,
+                'sample_type' => 'url',
             ],
         ];
         $this->_webApiCall($this->updateServiceInfo, $requestData);
@@ -483,7 +493,7 @@ class SampleRepositoryTest extends WebapiAbstract
         $sampleId = $this->getTargetSample($this->getTargetProduct())->getId();
         $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/samples/{$sampleId}";
         $requestData = [
-            'sampleId' => $sampleId,
+            'id' => $sampleId,
         ];
 
         $this->assertTrue($this->_webApiCall($this->deleteServiceInfo, $requestData));
@@ -500,7 +510,7 @@ class SampleRepositoryTest extends WebapiAbstract
         $sampleId = 9999;
         $this->deleteServiceInfo['rest']['resourcePath'] = "/V1/products/downloadable-links/samples/{$sampleId}";
         $requestData = [
-            'sampleId' => $sampleId,
+            'id' => $sampleId,
         ];
 
         $this->_webApiCall($this->deleteServiceInfo, $requestData);
diff --git a/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c8ab24527c633a12fd46442a9528570a4586e458
--- /dev/null
+++ b/dev/tests/api-functional/testsuite/Magento/GroupedProduct/Api/ProductRepositoryInterfaceTest.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\GroupedProduct\Api;
+
+use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\TestFramework\TestCase\WebapiAbstract;
+
+class ProductRepositoryInterfaceTest extends WebapiAbstract
+{
+    const SERVICE_NAME = 'catalogProductRepositoryV1';
+    const SERVICE_VERSION = 'V1';
+    const RESOURCE_PATH = '/V1/products';
+
+    /**
+     * Get Product
+     *
+     * @param $sku
+     * @return ProductInterface
+     */
+    protected function getProduct($sku)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $sku,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Get',
+            ],
+        ];
+
+        $response = $this->_webApiCall($serviceInfo, ['sku' => $sku]);
+        return $response;
+    }
+
+    /**
+     * Update Product
+     *
+     * @param $product
+     * @return mixed
+     */
+    protected function updateProduct($product)
+    {
+        $sku = $product[ProductInterface::SKU];
+        if (TESTS_WEB_API_ADAPTER == self::ADAPTER_REST) {
+            $product[ProductInterface::SKU] = null;
+        }
+
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $sku,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        $response =  $this->_webApiCall($serviceInfo, $requestData);
+        return $response;
+    }
+
+    /**
+     * Save Product
+     *
+     * @param $product
+     * @return mixed
+     */
+    protected function saveProduct($product)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'Save',
+            ],
+        ];
+        $requestData = ['product' => $product];
+        return $this->_webApiCall($serviceInfo, $requestData);
+    }
+
+    /**
+     * Delete Product
+     *
+     * @param string $sku
+     * @return boolean
+     */
+    protected function deleteProduct($sku)
+    {
+        $serviceInfo = [
+            'rest' => [
+                'resourcePath' => self::RESOURCE_PATH . '/' . $sku,
+                'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE,
+            ],
+            'soap' => [
+                'service' => self::SERVICE_NAME,
+                'serviceVersion' => self::SERVICE_VERSION,
+                'operation' => self::SERVICE_NAME . 'DeleteById',
+            ],
+        ];
+
+        return (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ?
+            $this->_webApiCall($serviceInfo, ['sku' => $sku]) : $this->_webApiCall($serviceInfo);
+    }
+
+    public function testProductLinks()
+    {
+        // Create simple product
+        $productData =  [
+            ProductInterface::SKU => "product_simple_500",
+            ProductInterface::NAME => "Product Simple 500",
+            ProductInterface::VISIBILITY => 4,
+            ProductInterface::TYPE_ID => 'simple',
+            ProductInterface::PRICE => 100,
+            ProductInterface::STATUS => 1,
+            ProductInterface::TYPE_ID => 'simple',
+            ProductInterface::ATTRIBUTE_SET_ID => 4,
+        ];
+
+        $this->saveProduct($productData);
+
+        // Create a group product
+        $productLinkData = ["product_sku" => "group_product_500", "link_type" => "associated",
+                            "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple",
+                            "position" => 0, "extension_attributes" => ["qty" => 1]];
+        $productWithGroupData =  [
+            ProductInterface::SKU => "group_product_500",
+            ProductInterface::NAME => "Group Product 500",
+            ProductInterface::VISIBILITY => 4,
+            ProductInterface::TYPE_ID => 'grouped',
+            ProductInterface::PRICE => 300,
+            ProductInterface::STATUS => 1,
+            ProductInterface::ATTRIBUTE_SET_ID => 4,
+            "product_links" => [$productLinkData]
+        ];
+
+        $this->saveProduct($productWithGroupData);
+        $response = $this->getProduct("group_product_500");
+        $this->assertArrayHasKey('product_links', $response);
+        $links = $response['product_links'];
+        $this->assertEquals(1, count($links));
+        $this->assertEquals($productLinkData, $links[0]);
+
+        // update link information for Group Product
+        $productLinkData1 = ["product_sku" => "group_product_500", "link_type" => "associated",
+                            "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple",
+                            "position" => 0, "extension_attributes" => ["qty" => 4]];
+        $productLinkData2 = ["product_sku" => "group_product_500", "link_type" => "upsell",
+                             "linked_product_sku" => "product_simple_500", "linked_product_type" => "simple",
+                             "position" => 0, "extension_attributes" => []];
+        $productWithGroupData =  [
+            ProductInterface::SKU => "group_product_500",
+            ProductInterface::NAME => "Group Product 500",
+            ProductInterface::VISIBILITY => 4,
+            ProductInterface::TYPE_ID => 'grouped',
+            ProductInterface::PRICE => 300,
+            ProductInterface::STATUS => 1,
+            ProductInterface::ATTRIBUTE_SET_ID => 4,
+            "product_links" => [$productLinkData1, $productLinkData2]
+        ];
+
+        $this->saveProduct($productWithGroupData);
+        $response = $this->getProduct("group_product_500");
+
+        $this->assertArrayHasKey('product_links', $response);
+        $links = $response['product_links'];
+        $this->assertEquals(2, count($links));
+        $this->assertEquals($productLinkData1, $links[1]);
+        $this->assertEquals($productLinkData2, $links[0]);
+
+        // Remove link
+        $productWithNoLinkData =  [
+            ProductInterface::SKU => "group_product_500",
+            ProductInterface::NAME => "Group Product 500",
+            ProductInterface::VISIBILITY => 4,
+            ProductInterface::TYPE_ID => 'grouped',
+            ProductInterface::PRICE => 300,
+            ProductInterface::STATUS => 1,
+            ProductInterface::ATTRIBUTE_SET_ID => 4,
+            "product_links" => []
+        ];
+
+        $this->saveProduct($productWithNoLinkData);
+        $response = $this->getProduct("group_product_500");
+        $this->assertArrayHasKey('product_links', $response);
+        $links = $response['product_links'];
+        $this->assertEquals([], $links);
+
+        $this->deleteProduct("product_simple_500");
+        $this->deleteProduct("group_product_500");
+    }
+}
diff --git a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php
index 1557278ce1bb31a76ac96d90a90c57b6bada4d21..2d20a4e9110939aa3c8298b8eb6e317436bf2454 100644
--- a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxClassRepositoryTest.php
@@ -12,6 +12,7 @@ use Magento\Framework\Api\FilterBuilder;
 use Magento\Framework\Api\SearchCriteriaBuilder;
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Tax\Api\Data\TaxClassInterfaceFactory;
+use Magento\Tax\Model\ClassModel;
 use Magento\Tax\Model\ClassModelRegistry;
 use Magento\TestFramework\Helper\Bootstrap;
 use Magento\TestFramework\TestCase\WebapiAbstract;
@@ -171,9 +172,9 @@ class TaxClassRepositoryTest extends WebapiAbstract
         ];
         $requestData = ['taxClassId' => $taxClassId];
         $taxClassData = $this->_webApiCall($serviceInfo, $requestData);
-        $this->assertEquals($taxClassData[Data\TaxClassInterface::KEY_NAME], $taxClassName);
+        $this->assertEquals($taxClassData[ClassModel::KEY_NAME], $taxClassName);
         $this->assertEquals(
-            $taxClassData[Data\TaxClassInterface::KEY_TYPE],
+            $taxClassData[ClassModel::KEY_TYPE],
             TaxClassManagementInterface::TYPE_CUSTOMER
         );
     }
@@ -220,7 +221,7 @@ class TaxClassRepositoryTest extends WebapiAbstract
     public function testSearchTaxClass()
     {
         $taxClassName = 'Retail Customer';
-        $taxClassNameField = Data\TaxClassInterface::KEY_NAME;
+        $taxClassNameField = ClassModel::KEY_NAME;
         $filter = $this->filterBuilder->setField($taxClassNameField)
             ->setValue($taxClassName)
             ->create();
@@ -249,23 +250,23 @@ class TaxClassRepositoryTest extends WebapiAbstract
     public function testSearchTaxClassMultipleFilterGroups()
     {
         $productTaxClass = [
-            Data\TaxClassInterface::KEY_NAME => 'Taxable Goods',
-            Data\TaxClassInterface::KEY_TYPE => 'PRODUCT',
+            ClassModel::KEY_NAME => 'Taxable Goods',
+            ClassModel::KEY_TYPE => 'PRODUCT',
         ];
-        $customerTaxClass = [Data\TaxClassInterface::KEY_NAME => 'Retail Customer',
-            Data\TaxClassInterface::KEY_TYPE => 'CUSTOMER', ];
+        $customerTaxClass = [ClassModel::KEY_NAME => 'Retail Customer',
+            ClassModel::KEY_TYPE => 'CUSTOMER', ];
 
-        $filter1 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_NAME)
-            ->setValue($productTaxClass[Data\TaxClassInterface::KEY_NAME])
+        $filter1 = $this->filterBuilder->setField(ClassModel::KEY_NAME)
+            ->setValue($productTaxClass[ClassModel::KEY_NAME])
             ->create();
-        $filter2 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_NAME)
-            ->setValue($customerTaxClass[Data\TaxClassInterface::KEY_NAME])
+        $filter2 = $this->filterBuilder->setField(ClassModel::KEY_NAME)
+            ->setValue($customerTaxClass[ClassModel::KEY_NAME])
             ->create();
-        $filter3 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_TYPE)
-            ->setValue($productTaxClass[Data\TaxClassInterface::KEY_TYPE])
+        $filter3 = $this->filterBuilder->setField(ClassModel::KEY_TYPE)
+            ->setValue($productTaxClass[ClassModel::KEY_TYPE])
             ->create();
-        $filter4 = $this->filterBuilder->setField(Data\TaxClassInterface::KEY_TYPE)
-            ->setValue($customerTaxClass[Data\TaxClassInterface::KEY_TYPE])
+        $filter4 = $this->filterBuilder->setField(ClassModel::KEY_TYPE)
+            ->setValue($customerTaxClass[ClassModel::KEY_TYPE])
             ->create();
 
         /**
@@ -291,12 +292,12 @@ class TaxClassRepositoryTest extends WebapiAbstract
         $searchResults = $this->_webApiCall($serviceInfo, $requestData);
         $this->assertEquals(2, $searchResults['total_count']);
         $this->assertEquals(
-            $productTaxClass[Data\TaxClassInterface::KEY_NAME],
-            $searchResults['items'][0][Data\TaxClassInterface::KEY_NAME]
+            $productTaxClass[ClassModel::KEY_NAME],
+            $searchResults['items'][0][ClassModel::KEY_NAME]
         );
         $this->assertEquals(
-            $customerTaxClass[Data\TaxClassInterface::KEY_NAME],
-            $searchResults['items'][1][Data\TaxClassInterface::KEY_NAME]
+            $customerTaxClass[ClassModel::KEY_NAME],
+            $searchResults['items'][1][ClassModel::KEY_NAME]
         );
 
         /** class_name == 'Retail Customer' && ( class_type == 'CUSTOMER' || class_type == 'PRODUCT') */
@@ -309,8 +310,8 @@ class TaxClassRepositoryTest extends WebapiAbstract
         $searchResults = $this->_webApiCall($serviceInfo, $requestData);
         $this->assertEquals(1, $searchResults['total_count']);
         $this->assertEquals(
-            $customerTaxClass[Data\TaxClassInterface::KEY_NAME],
-            $searchResults['items'][0][Data\TaxClassInterface::KEY_NAME]
+            $customerTaxClass[ClassModel::KEY_NAME],
+            $searchResults['items'][0][ClassModel::KEY_NAME]
         );
     }
 }
diff --git a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php
index 9f47505721b8f6bb1e4b403e6b2f56b861fd3668..a140347e2498fe75163c9822a4f5f8eb80fd9506 100644
--- a/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Tax/Api/TaxRateRepositoryTest.php
@@ -10,7 +10,7 @@ use Magento\Framework\Api\FilterBuilder;
 use Magento\Framework\Api\SearchCriteria;
 use Magento\Framework\Api\SearchCriteriaBuilder;
 use Magento\Framework\Api\SortOrderBuilder;
-use Magento\Tax\Api\Data\TaxRateInterface as TaxRate;
+use Magento\Tax\Model\Calculation\Rate;
 use Magento\TestFramework\Helper\Bootstrap;
 use Magento\TestFramework\TestCase\WebapiAbstract;
 
@@ -433,7 +433,7 @@ class TaxRateRepositoryTest extends WebapiAbstract
         $rates = $this->setupTaxRatesForSearch();
 
         // Find rates whose code is 'codeUs12'
-        $filter = $this->filterBuilder->setField(TaxRate::KEY_CODE)
+        $filter = $this->filterBuilder->setField(Rate::KEY_CODE)
             ->setValue('codeUs12')
             ->create();
 
@@ -480,11 +480,11 @@ class TaxRateRepositoryTest extends WebapiAbstract
         $rates = $this->setupTaxRatesForSearch();
 
         // Find rates which country id 'CZ'
-        $filter = $this->filterBuilder->setField(TaxRate::KEY_COUNTRY_ID)
+        $filter = $this->filterBuilder->setField(Rate::KEY_COUNTRY_ID)
             ->setValue('CZ')
             ->create();
         $sortOrder = $this->sortOrderBuilder
-            ->setField(TaxRate::KEY_POSTCODE)
+            ->setField(Rate::KEY_POSTCODE)
             ->setDirection(SearchCriteria::SORT_DESC)
             ->create();
         // Order them by descending postcode (not the default order)
diff --git a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php
index 41d6e8a2914deb9a863b42d2634d0c9aab934324..a64744df17ad68e016255d6d2396bad4e59564f6 100644
--- a/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php
+++ b/dev/tests/api-functional/testsuite/Magento/Webapi/Routing/RestErrorHandlingTest.php
@@ -81,7 +81,7 @@ class RestErrorHandlingTest extends \Magento\TestFramework\TestCase\WebapiAbstra
     {
         $serviceInfo = [
             'rest' => [
-                'resourcePath' => '/V1/errortest/otherexception',
+                'resourcePath' => '/V1/errortest/otherException',
                 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
             ],
         ];
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php
index 9854602a507fcc0045fcc2f5be86a85f96073b0d..3809d7f293849488c696e4631c03e6ed34e0de02 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Annotation/ConfigFixture.php
@@ -9,6 +9,8 @@
  */
 namespace Magento\TestFramework\Annotation;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class ConfigFixture
 {
     /**
@@ -73,7 +75,7 @@ class ConfigFixture
                 )->setValue(
                     $configPath,
                     $value,
-                    \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+                    ScopeConfigInterface::SCOPE_TYPE_DEFAULT
                 );
             }
         } else {
diff --git a/dev/tests/integration/framework/bootstrap.php b/dev/tests/integration/framework/bootstrap.php
index 41453aa0becc0e2d32513736f2cb99ddc60e8854..b71bb17d945ccd42f693ed57588116e05c474dbb 100644
--- a/dev/tests/integration/framework/bootstrap.php
+++ b/dev/tests/integration/framework/bootstrap.php
@@ -8,6 +8,11 @@ use Magento\Framework\Autoload\AutoloaderRegistry;
 require_once __DIR__ . '/../../../../app/bootstrap.php';
 require_once __DIR__ . '/autoload.php';
 
+$updateAppBootstrap = __DIR__ . '/../../../../update/app/bootstrap.php';
+if (file_exists($updateAppBootstrap)) {
+    require_once $updateAppBootstrap;
+}
+
 $testsBaseDir = dirname(__DIR__);
 $testsTmpDir = "{$testsBaseDir}/tmp";
 $magentoBaseDir = realpath("{$testsBaseDir}/../../../");
diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist
index 7a9e365c8f15056e5060cd27827720e2d18d2fce..bd6cbffb247d64f6617eef5fc9c8ebaebb753b7e 100644
--- a/dev/tests/integration/phpunit.xml.dist
+++ b/dev/tests/integration/phpunit.xml.dist
@@ -18,6 +18,7 @@
         </testsuite>
         <testsuite name="Magento Integration Tests">
             <directory suffix="Test.php">testsuite</directory>
+            <directory suffix="Test.php">../../../update/dev/tests/integration/testsuite</directory>
             <exclude>testsuite/Magento/Test/Integrity</exclude>
             <exclude>testsuite/Magento/MemoryUsageTest.php</exclude>
         </testsuite>
@@ -27,6 +28,7 @@
         <whitelist addUncoveredFilesFromWhiteList="true">
             <directory suffix=".php">../../../app/code/Magento</directory>
             <directory suffix=".php">../../../lib/internal/Magento</directory>
+            <directory suffix=".php">../../../update/app/code</directory>
         </whitelist>
     </filter>
     <!-- PHP INI settings and constants definition -->
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt
index fc890c803aae34f8ca74f4c00a541415b426034a..dc07065f7be1e4b2db53584c769bb52abb79de21 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt
+++ b/dev/tests/integration/testsuite/Magento/Backend/Block/_files/menu/expected.txt
@@ -1 +1 @@
-<ul id="nav" role="menubar" ><li  data-ui-id="magento-backend-system"  class="item-system  parent last level-0" id="magento-backend-system" aria-haspopup="true" role="menu-item"><a href="#"  onclick="return false;" class=""><span>System</span></a><div class="submenu" aria-labelledby="magento-backend-system"><ul role="menu" ><li  data-ui-id="magento-backend-system-report"  class="item-system-report  parent  level-1" role="menu-item"><strong class="submenu-group-title" role="presentation"><span>Report</span></strong><div class="submenu"><ul role="menu" ><li  data-ui-id="magento-backend-system-report-private-sales"  class="item-system-report-private-sales    level-2" role="menu-item"><a href="#"  onclick="return false;" class=""><span>Private Sales</span></a></li><li  data-ui-id="magento-backend-system-report-magento-invite-general"  class="item-system-report-magento-invite-general    level-2" role="menu-item"><a href="#"  onclick="return false;" class=""><span>Invite</span></a></li><li  data-ui-id="magento-backend-system-report-magento-invite-customer"  class="item-system-report-magento-invite-customer    level-2" role="menu-item"><a href="#"  onclick="return false;" class=""><span>Invited Customers</span></a></li></ul></div></li></ul></div></li></ul>
\ No newline at end of file
+<ul id="nav" role="menubar" ><li  data-ui-id="magento-backend-system"  class="item-system  parent last level-0" id="magento-backend-system" aria-haspopup="true" role="menu-item"><a href="#"  onclick="return false;" class=""><span>System</span></a><div class="submenu" aria-labelledby="magento-backend-system"><ul role="menu" ><div class="submenu"><ul role="menu" ><li  data-ui-id="magento-backend-system-report-private-sales"  class="item-system-report-private-sales    level-2" role="menu-item"><a href="#"  onclick="return false;" class=""><span>Private Sales</span></a></li><li  data-ui-id="magento-backend-system-report-magento-invite-general"  class="item-system-report-magento-invite-general    level-2" role="menu-item"><a href="#"  onclick="return false;" class=""><span>Invite</span></a></li><li  data-ui-id="magento-backend-system-report-magento-invite-customer"  class="item-system-report-magento-invite-customer    level-2" role="menu-item"><a href="#"  onclick="return false;" class=""><span>Invited Customers</span></a></li></ul></div></ul></div></li></ul>
\ No newline at end of file
diff --git a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php
index 09c21758cba336cbd15982d610875cbc0aa19117..c0cc101fd36bfe94dd5dcc19481d81d14b926132 100644
--- a/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Backend/Model/Locale/ResolverTest.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Backend\Model\Locale;
 
+use Magento\Framework\Locale\Resolver;
+
 /**
  * @magentoAppArea adminhtml
  */
@@ -28,7 +30,7 @@ class ResolverTest extends \PHPUnit_Framework_TestCase
      */
     public function testSetLocaleWithDefaultLocale()
     {
-        $this->_checkSetLocale(\Magento\Framework\Locale\ResolverInterface::DEFAULT_LOCALE);
+        $this->_checkSetLocale(Resolver::DEFAULT_LOCALE);
     }
 
     /**
diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/data_object.xml b/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/service_data_attributes.xml
similarity index 63%
rename from dev/tests/integration/testsuite/Magento/Catalog/_files/etc/data_object.xml
rename to dev/tests/integration/testsuite/Magento/Catalog/_files/etc/service_data_attributes.xml
index c2888a64ee00c0e6f18a7d1126920e4c85a8b091..93cf7bfb484a6de773ab318549385ad2436f2fa4 100644
--- a/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/data_object.xml
+++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/etc/service_data_attributes.xml
@@ -5,22 +5,22 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
-    </custom_attributes>
-    <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
         <attribute code="stock_item" type="Magento\CatalogInventory\Service\Data\V1\StockItem" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Catalog\Api\Data\CategoryInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Catalog\Api\Data\CategoryInterface">
         <attribute code="category_attribute_1" type="Magento\Catalog\Api\Data\CategoryAttributeType1" />
         <attribute code="category_attribute_2" type="Magento\Catalog\Api\Data\CategoryAttributeType2" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
         <attribute code="customer_attribute_1" type="Magento\Customer\Api\Data\CustomerAttributeType1" />
         <attribute code="customer_attribute_2" type="Magento\Customer\Api\Data\CustomerAttributeType2" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Customer\Api\Data\Address">
+    </extension_attributes>
+    <extension_attributes for="Magento\Customer\Api\Data\Address">
         <attribute code="address_attribute_1" type="Magento\Customer\Api\Data\AddressAttributeType1" />
         <attribute code="address_attribute_2" type="Magento\Customer\Api\Data\AddressAttributeType2" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
index def5584983e613573b91eeff95d766599b82655a..a83bb8be1c9feec57cb2e722d69021549a786615 100644
--- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
+++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Export/ProductTest.php
@@ -125,4 +125,50 @@ class ProductTest extends \PHPUnit_Framework_TestCase
             );
         }
     }
+
+    /**
+     * Verifies if exception processing works properly
+     *
+     * @magentoDataFixture Magento/CatalogImportExport/_files/product_export_data.php
+     */
+    public function testExceptionInGetExportData()
+    {
+        $exception = new \Exception('Error');
+
+        $rowCustomizerMock = $this->getMockBuilder('Magento\CatalogImportExport\Model\Export\RowCustomizerInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $loggerMock = $this->getMockBuilder('\Psr\Log\LoggerInterface')->getMock();
+
+        $directoryMock = $this->getMock('Magento\Framework\Filesystem\Directory\Write', [], [], '', false);
+        $directoryMock->expects($this->any())->method('getParentDirectory')->will($this->returnValue('some#path'));
+        $directoryMock->expects($this->any())->method('isWritable')->will($this->returnValue(true));
+
+        $filesystemMock = $this->getMock('Magento\Framework\Filesystem', [], [], '', false);
+        $filesystemMock->expects($this->once())->method('getDirectoryWrite')->will($this->returnValue($directoryMock));
+
+        $exportAdapter = new \Magento\ImportExport\Model\Export\Adapter\Csv($filesystemMock);
+
+        $rowCustomizerMock->expects($this->once())->method('prepareData')->willThrowException($exception);
+        $loggerMock->expects($this->once())->method('critical')->with($exception);
+
+        $collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+            '\Magento\Catalog\Model\Resource\Product\Collection'
+        );
+
+        /** @var \Magento\CatalogImportExport\Model\Export\Product $model */
+        $model = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
+            'Magento\CatalogImportExport\Model\Export\Product',
+            [
+                'rowCustomizer' => $rowCustomizerMock,
+                'logger' => $loggerMock,
+                'collection' => $collection
+            ]
+        );
+
+
+        $data = $model->setWriter($exportAdapter)->export();
+        $this->assertEmpty($data);
+    }
 }
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php
index 2a99dede484227e4b7bc38fd9040e78573584869..ab40348430e024c0d58e42d8ee649321c0ec754b 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/Model/Product/Type/ConfigurableTest.php
@@ -79,7 +79,6 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
     public function testSetUsedProductAttributeIds()
     {
         $testConfigurable = $this->_getAttributeByCode('test_configurable');
-        $this->assertEmpty($this->_product->getData('_cache_instance_configurable_attributes'));
         $this->_model->setUsedProductAttributeIds([$testConfigurable->getId()], $this->_product);
         $attributes = $this->_product->getData('_cache_instance_configurable_attributes');
         $this->assertArrayHasKey(0, $attributes);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php
new file mode 100644
index 0000000000000000000000000000000000000000..5772edc9317ae5a0a41a9d4aafd419ea982e53fa
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/configurable_attribute_rollback.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/** @var \Magento\Framework\Registry $registry */
+$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Framework\Registry');
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$eavConfig = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get('Magento\Eav\Model\Config');
+$attribute = $eavConfig->getAttribute('catalog_product', 'test_configurable');
+if ($attribute instanceof \Magento\Eav\Model\Entity\Attribute\AbstractAttribute
+    && $attribute->getId()
+) {
+    $attribute->delete();
+}
+$eavConfig->clear();
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php
index b083d7e52dfb293b687d837f145b54909240e617..cc615bf03ea85243af323bf7844bc3d33acac7c9 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable.php
@@ -12,17 +12,16 @@ $installer = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create
     ['resourceName' => 'catalog_setup']
 );
 
-/* Create simple products per each option */
-/** @var $options \Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection */
-$options = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
-    'Magento\Eav\Model\Resource\Entity\Attribute\Option\Collection'
-);
-$options->setAttributeFilter($attribute->getId());
+/* Create simple products per each option value*/
+
+/** @var \Magento\Eav\Api\Data\AttributeOptionInterface[] $options */
+$options = $attribute->getOptions();
 
 $attributeValues = [];
 $productIds = [];
 $attributeSetId = $installer->getAttributeSetId('catalog_product', 'Default');
 $productIds = [10, 20];
+array_shift($options); //remove the first option which is empty
 foreach ($options as $option) {
     /** @var $product \Magento\Catalog\Model\Product */
     $product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create('Magento\Catalog\Model\Product');
@@ -36,13 +35,13 @@ foreach ($options as $option) {
     )->setWebsiteIds(
         [1]
     )->setName(
-        'Configurable Option' . $option->getId()
+        'Configurable Option' . $option->getLabel()
     )->setSku(
         'simple_' . $productId
     )->setPrice(
         10
     )->setTestConfigurable(
-        $option->getId()
+        $option->getValue()
     )->setVisibility(
         \Magento\Catalog\Model\Product\Visibility::VISIBILITY_NOT_VISIBLE
     )->setStatus(
@@ -54,7 +53,7 @@ foreach ($options as $option) {
     $attributeValues[] = [
         'label' => 'test',
         'attribute_id' => $attribute->getId(),
-        'value_index' => $option->getId(),
+        'value_index' => $option->getValue(),
         'is_percent' => false,
         'pricing_value' => 5,
     ];
diff --git a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php
index c1b23ce233c9e09240533d5bc7ee97f90a7510da..f1e17b724c8b476f76a622837d2120b0df1c96cd 100644
--- a/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php
+++ b/dev/tests/integration/testsuite/Magento/ConfigurableProduct/_files/product_configurable_rollback.php
@@ -26,6 +26,11 @@ $product->load(1);
 if ($product->getId()) {
     $product->delete();
 }
+\Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+    ->get('Magento\ConfigurableProduct\Model\Resource\Product\Type\Configurable\Attribute\Price\Data')
+    ->setProductPrice(1, null);
+
+require __DIR__ . '/configurable_attribute_rollback.php';
 
 $registry->unregister('isSecureArea');
 $registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php b/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php
index 48d81d30eb84119c45e440be2e57bd48b4c3c8e9..9ee9ed911f7c092aac6034ed441e5c96f2e46aa2 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php
+++ b/dev/tests/integration/testsuite/Magento/Customer/Model/FileResolverStub.php
@@ -21,7 +21,7 @@ class FileResolverStub implements \Magento\Framework\Config\FileResolverInterfac
                 'path' => realpath(__DIR__ . '/../_files/etc'),
             ]
         );
-        $paths = ['data_object.xml'];
+        $paths = ['service_data_attributes.xml'];
         return new \Magento\Framework\Config\FileIterator($readDirectory, $paths);
     }
 }
diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/etc/data_object.xml b/dev/tests/integration/testsuite/Magento/Customer/_files/etc/service_data_attributes.xml
similarity index 63%
rename from dev/tests/integration/testsuite/Magento/Customer/_files/etc/data_object.xml
rename to dev/tests/integration/testsuite/Magento/Customer/_files/etc/service_data_attributes.xml
index a75fa2a653213ade29aa7ea81e8482e752ed9210..a7ae041eda7683ce99dd168befc50c2ae5a46f3c 100644
--- a/dev/tests/integration/testsuite/Magento/Customer/_files/etc/data_object.xml
+++ b/dev/tests/integration/testsuite/Magento/Customer/_files/etc/service_data_attributes.xml
@@ -5,22 +5,22 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
-    </custom_attributes>
-    <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
         <attribute code="stock_item" type="Magento\CatalogInventory\Service\Data\V1\StockItem" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Catalog\Api\Data\CategoryInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Catalog\Api\Data\CategoryInterface">
         <attribute code="category_attribute_1" type="Magento\Catalog\Api\Data\CategoryAttributeType1" />
         <attribute code="category_attribute_2" type="Magento\Catalog\Api\Data\CategoryAttributeType2" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
         <attribute code="customer_attribute_1" type="Magento\Customer\Api\Data\CustomerAttributeType1" />
         <attribute code="customer_attribute_2" type="Magento\Customer\Api\Data\CustomerAttributeType2" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Customer\Api\Data\AddressInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Customer\Api\Data\AddressInterface">
         <attribute code="address_attribute_1" type="Magento\Customer\Api\Data\AddressAttributeType1" />
         <attribute code="address_attribute_2" type="Magento\Customer\Api\Data\AddressAttributeType2" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php
index 351a0ba03144a218481d9ab798cca18989ca8db8..07fceddb6424b3bc01d7e4448833cb92a709d6c9 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/ReaderTest.php
@@ -76,12 +76,24 @@ class ReaderTest extends \PHPUnit_Framework_TestCase
         $expectedArray = [
             'Magento\Tax\Api\Data\TaxRateInterface' => [],
             'Magento\Catalog\Api\Data\Product' => [
-                'stock_item' => "Magento\CatalogInventory\Api\Data\StockItem",
+                'stock_item' => [
+                    "type" => "Magento\CatalogInventory\Api\Data\StockItem",
+                    "resourceRefs" => [],
+                ],
             ],
             'Magento\Customer\Api\Data\CustomerInterface' => [
-                'custom_1' => "Magento\Customer\Api\Data\CustomerCustom",
-                'custom_2' => "Magento\CustomerExtra\Api\Data\CustomerCustom22",
-                'custom_3' => "Magento\Customer\Api\Data\CustomerCustom3",
+                'custom_1' => [
+                    "type" => "Magento\Customer\Api\Data\CustomerCustom",
+                    "resourceRefs" => [],
+                ],
+                'custom_2' => [
+                    "type" => "Magento\CustomerExtra\Api\Data\CustomerCustom22",
+                    "resourceRefs" => [],
+                ],
+                'custom_3' => [
+                    "type" => "Magento\Customer\Api\Data\CustomerCustom3",
+                    "resourceRefs" => [],
+                ],
             ],
         ];
 
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml
index f5a3d522454ab487f461f697e6108f118f1769e9..82aa641d5e8bd9040d9fa26689d95a09b74e19f3 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml
+++ b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_one.xml
@@ -5,14 +5,14 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
-    </custom_attributes>
-    <custom_attributes for="Magento\Catalog\Api\Data\Product">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Catalog\Api\Data\Product">
         <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItem" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
         <attribute code="custom_1" type="Magento\Customer\Api\Data\CustomerCustom" />
         <attribute code="custom_2" type="Magento\CustomerExtra\Service\V1\Data\CustomerCustom21" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml
index 629efb9b7a30a400dad479894f71d0711da8f24d..5596f61e572f4b076a1d850b38002b6a448a7297 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml
+++ b/dev/tests/integration/testsuite/Magento/Framework/Api/Config/_files/config_two.xml
@@ -5,9 +5,9 @@
  * See COPYING.txt for license details.
  */
 -->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface">
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
         <attribute code="custom_2" type="Magento\CustomerExtra\Api\Data\CustomerCustom22" />
         <attribute code="custom_3" type="Magento\Customer\Api\Data\CustomerCustom3" />
-    </custom_attributes>
+    </extension_attributes>
 </config>
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php b/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php
index c8379e5e9c39f37bb5d4edd46ed4720aa250b788..18a4314710c3d9a19f51bf5688afcc7087b90f93 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Session/SessionManagerTest.php
@@ -3,153 +3,214 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
-namespace Magento\Framework\Session;
-
-class SessionManagerTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * @var \Magento\Framework\Session\SessionManagerInterface
-     */
-    protected $_model;
-
-    /**
-     * @var \Magento\Framework\Session\SidResolverInterface
-     */
-    protected $_sidResolver;
-
-    /**
-     * @var string
-     */
-    protected $sessionName;
-
-    /**
-     * @var \Magento\Framework\App\RequestInterface
-     */
-    protected $request;
-
-    protected function setUp()
-    {
-        $this->sessionName = 'frontEndSession';
-
-        ini_set('session.use_only_cookies', '0');
-        ini_set('session.name', $this->sessionName);
-
-        $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-
-        /** @var \Magento\Framework\Session\SidResolverInterface $sidResolver */
-        $this->_sidResolver = $objectManager->get('Magento\Framework\Session\SidResolverInterface');
-
-        $this->request = $objectManager->get('Magento\Framework\App\RequestInterface');
-
-        /** @var \Magento\Framework\Session\SessionManager _model */
-        $this->_model = $objectManager->create(
-            'Magento\Framework\Session\SessionManager',
-            [
-                $this->request,
-                $this->_sidResolver,
-                $objectManager->get('Magento\Framework\Session\Config\ConfigInterface'),
-                $objectManager->get('Magento\Framework\Session\SaveHandlerInterface'),
-                $objectManager->get('Magento\Framework\Session\ValidatorInterface'),
-                $objectManager->get('Magento\Framework\Session\StorageInterface')
-            ]
-        );
-    }
-
-    public function testSessionNameFromIni()
-    {
-        $this->_model->start();
-        $this->assertSame($this->sessionName, $this->_model->getName());
-        $this->_model->destroy();
-    }
-
-    public function testSessionUseOnlyCookies()
-    {
-        $expectedValue = '1';
-        $sessionUseOnlyCookies = ini_get('session.use_only_cookies');
-        $this->assertSame($expectedValue, $sessionUseOnlyCookies);
-    }
-
-    public function testGetData()
-    {
-        $this->_model->setData(['test_key' => 'test_value']);
-        $this->assertEquals('test_value', $this->_model->getData('test_key', true));
-        $this->assertNull($this->_model->getData('test_key'));
-    }
-
-    public function testGetSessionId()
-    {
-        $this->assertEquals(session_id(), $this->_model->getSessionId());
-    }
-
-    public function testGetName()
-    {
-        $this->assertEquals(session_name(), $this->_model->getName());
-    }
-
-    public function testSetName()
-    {
-        $this->_model->setName('test');
-        $this->assertEquals('test', $this->_model->getName());
-    }
-
-    public function testDestroy()
-    {
-        $data = ['key' => 'value'];
-        $this->_model->setData($data);
-
-        $this->assertEquals($data, $this->_model->getData());
-        $this->_model->destroy();
-
-        $this->assertEquals([], $this->_model->getData());
-    }
+// @codingStandardsIgnoreStart
+namespace {
+    $mockPHPFunctions = false;
+}
 
-    public function testSetSessionId()
-    {
-        $sessionId = $this->_model->getSessionId();
-        $this->_model->setSessionId($this->_sidResolver->getSid($this->_model));
-        $this->assertEquals($sessionId, $this->_model->getSessionId());
-
-        $this->_model->setSessionId('test');
-        $this->assertEquals('test', $this->_model->getSessionId());
-    }
+namespace Magento\Framework\Session {
+    // @codingStandardsIgnoreEnd
 
     /**
-     * @magentoConfigFixture current_store web/session/use_frontend_sid 1
+     * Mock session_status if in test mode, or continue normal execution otherwise
+     *
+     * @return int Session status code
      */
-    public function testSetSessionIdFromParam()
+    function session_status()
     {
-        $this->assertNotEquals('test_id', $this->_model->getSessionId());
-        $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test-id');
-        $this->_model->setSessionId($this->_sidResolver->getSid($this->_model));
-
-        $this->assertEquals('test-id', $this->_model->getSessionId());
-
-        /* Use not valid identifier */
-        $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test_id');
-        $this->_model->setSessionId($this->_sidResolver->getSid($this->_model));
-        $this->assertEquals('test-id', $this->_model->getSessionId());
+        global $mockPHPFunctions;
+        if ($mockPHPFunctions) {
+            return PHP_SESSION_NONE;
+        }
+        return call_user_func_array('\session_status', func_get_args());
     }
 
-    public function testGetSessionIdForHost()
+    function headers_sent()
     {
-        $this->request->getServer()->set('HTTP_HOST', 'localhost');
-        $this->_model->start();
-        $this->assertEmpty($this->_model->getSessionIdForHost('localhost'));
-        $this->assertNotEmpty($this->_model->getSessionIdForHost('test'));
-        $this->_model->destroy();
+        global $mockPHPFunctions;
+        if ($mockPHPFunctions) {
+            return false;
+        }
+        return call_user_func_array('\headers_sent', func_get_args());
     }
 
-    public function testIsValidForHost()
+    class SessionManagerTest extends \PHPUnit_Framework_TestCase
     {
-        $this->request->getServer()->set('HTTP_HOST', 'localhost');
-        $this->_model->start();
-
-        $reflection = new \ReflectionMethod($this->_model, '_addHost');
-        $reflection->setAccessible(true);
-        $reflection->invoke($this->_model);
-
-        $this->assertFalse($this->_model->isValidForHost('test.com'));
-        $this->assertTrue($this->_model->isValidForHost('localhost'));
-        $this->_model->destroy();
+        /**
+         * @var \Magento\Framework\Session\SessionManagerInterface
+         */
+        protected $_model;
+
+        /**
+         * @var \Magento\Framework\Session\SidResolverInterface
+         */
+        protected $_sidResolver;
+
+        /**
+         * @var string
+         */
+        protected $sessionName;
+
+        /**
+         * @var \Magento\Framework\ObjectManagerInterface
+         */
+        protected $objectManager;
+
+        protected function setUp()
+        {
+            $this->sessionName = 'frontEndSession';
+
+            ini_set('session.use_only_cookies', '0');
+            ini_set('session.name', $this->sessionName);
+
+            $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+            /** @var \Magento\Framework\Session\SidResolverInterface $sidResolver */
+            $this->_sidResolver = $this->objectManager->get('Magento\Framework\Session\SidResolverInterface');
+
+            $this->request = $this->objectManager->get('Magento\Framework\App\RequestInterface');
+
+            /** @var \Magento\Framework\Session\SessionManager _model */
+            $this->_model = $this->objectManager->create(
+                'Magento\Framework\Session\SessionManager',
+                [
+                    $this->objectManager->get('Magento\Framework\App\Request\Http'),
+                    $this->_sidResolver,
+                    $this->objectManager->get('Magento\Framework\Session\Config\ConfigInterface'),
+                    $this->objectManager->get('Magento\Framework\Session\SaveHandlerInterface'),
+                    $this->objectManager->get('Magento\Framework\Session\ValidatorInterface'),
+                    $this->objectManager->get('Magento\Framework\Session\StorageInterface')
+                ]
+            );
+        }
+
+        public function testSessionNameFromIni()
+        {
+            $this->_model->start();
+            $this->assertSame($this->sessionName, $this->_model->getName());
+            $this->_model->destroy();
+        }
+
+        public function testSessionUseOnlyCookies()
+        {
+            $expectedValue = '1';
+            $sessionUseOnlyCookies = ini_get('session.use_only_cookies');
+            $this->assertSame($expectedValue, $sessionUseOnlyCookies);
+        }
+
+        public function testGetData()
+        {
+            $this->_model->setData(['test_key' => 'test_value']);
+            $this->assertEquals('test_value', $this->_model->getData('test_key', true));
+            $this->assertNull($this->_model->getData('test_key'));
+        }
+
+        public function testGetSessionId()
+        {
+            $this->assertEquals(session_id(), $this->_model->getSessionId());
+        }
+
+        public function testGetName()
+        {
+            $this->assertEquals(session_name(), $this->_model->getName());
+        }
+
+        public function testSetName()
+        {
+            $this->_model->setName('test');
+            $this->assertEquals('test', $this->_model->getName());
+        }
+
+        public function testDestroy()
+        {
+            $data = ['key' => 'value'];
+            $this->_model->setData($data);
+
+            $this->assertEquals($data, $this->_model->getData());
+            $this->_model->destroy();
+
+            $this->assertEquals([], $this->_model->getData());
+        }
+
+        public function testSetSessionId()
+        {
+            $sessionId = $this->_model->getSessionId();
+            $this->_model->setSessionId($this->_sidResolver->getSid($this->_model));
+            $this->assertEquals($sessionId, $this->_model->getSessionId());
+
+            $this->_model->setSessionId('test');
+            $this->assertEquals('test', $this->_model->getSessionId());
+        }
+
+        /**
+         * @magentoConfigFixture current_store web/session/use_frontend_sid 1
+         */
+        public function testSetSessionIdFromParam()
+        {
+            $this->assertNotEquals('test_id', $this->_model->getSessionId());
+            $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test-id');
+            $this->_model->setSessionId($this->_sidResolver->getSid($this->_model));
+            $this->assertEquals('test-id', $this->_model->getSessionId());
+            /* Use not valid identifier */
+            $this->request->getQuery()->set($this->_sidResolver->getSessionIdQueryParam($this->_model), 'test_id');
+            $this->_model->setSessionId($this->_sidResolver->getSid($this->_model));
+            $this->assertEquals('test-id', $this->_model->getSessionId());
+        }
+
+        public function testGetSessionIdForHost()
+        {
+            $_SERVER['HTTP_HOST'] = 'localhost';
+            $this->_model->start();
+            $this->assertEmpty($this->_model->getSessionIdForHost('localhost'));
+            $this->assertNotEmpty($this->_model->getSessionIdForHost('test'));
+            $this->_model->destroy();
+        }
+
+        public function testIsValidForHost()
+        {
+            $_SERVER['HTTP_HOST'] = 'localhost';
+            $this->_model->start();
+
+            $reflection = new \ReflectionMethod($this->_model, '_addHost');
+            $reflection->setAccessible(true);
+            $reflection->invoke($this->_model);
+
+            $this->assertFalse($this->_model->isValidForHost('test.com'));
+            $this->assertTrue($this->_model->isValidForHost('localhost'));
+            $this->_model->destroy();
+        }
+
+
+        /**
+         * @expectedException \Magento\Framework\Exception\SessionException
+         * @expectedExceptionMessage Area code not set: Area code must be set before starting a session.
+         */
+        public function testStartAreaNotSet()
+        {
+            $scope = $this->objectManager->get('Magento\Framework\Config\ScopeInterface');
+            $appState = new \Magento\Framework\App\State($scope);
+
+            /**
+             * Must be created by "new" in order to get a real Magento\Framework\App\State object that
+             * is not overridden in the TestFramework
+             *
+             * @var \Magento\Framework\Session\SessionManager _model
+             */
+            $this->_model = new \Magento\Framework\Session\SessionManager(
+                $this->objectManager->get('Magento\Framework\App\Request\Http'),
+                $this->_sidResolver,
+                $this->objectManager->get('Magento\Framework\Session\Config\ConfigInterface'),
+                $this->objectManager->get('Magento\Framework\Session\SaveHandlerInterface'),
+                $this->objectManager->get('Magento\Framework\Session\ValidatorInterface'),
+                $this->objectManager->get('Magento\Framework\Session\StorageInterface'),
+                $this->objectManager->get('Magento\Framework\Stdlib\CookieManagerInterface'),
+                $this->objectManager->get('Magento\Framework\Stdlib\Cookie\CookieMetadataFactory'),
+                $appState
+            );
+
+            global $mockPHPFunctions;
+            $mockPHPFunctions = true;
+            $this->_model->start();
+        }
     }
 }
diff --git a/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php b/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php
index 14f306766a10720ac021fc9ce9b94ecff8ee08cd..f83337a307eb201a8d5fd2fc8ef9dc8d6005880a 100644
--- a/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php
+++ b/dev/tests/integration/testsuite/Magento/Payment/Model/ObserverTest.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Payment\Model;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 /**
  * @magentoAppArea adminhtml
  */
@@ -68,7 +70,7 @@ class ObserverTest extends \PHPUnit_Framework_TestCase
         $config->saveConfig(
             'payment/checkmo/order_status',
             $statusCode,
-            \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+            ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
             0
         );
 
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
index 1313db5e278a098d9687b95ed80501862295b0ec..0a35f0488948b5d142693ee2a423f600eab01e7b 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Controller/Adminhtml/Order/CreateTest.php
@@ -7,6 +7,7 @@ namespace Magento\Sales\Controller\Adminhtml\Order;
 
 /**
  * @magentoAppArea adminhtml
+ * @magentoDbIsolation enabled
  */
 class CreateTest extends \Magento\Backend\Utility\Controller
 {
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Block/Adminhtml/Rate/FormTest.php b/dev/tests/integration/testsuite/Magento/Tax/Block/Adminhtml/Rate/FormTest.php
deleted file mode 100644
index 0bc80a86389f29b09fc9d4b80b001347662d97f8..0000000000000000000000000000000000000000
--- a/dev/tests/integration/testsuite/Magento/Tax/Block/Adminhtml/Rate/FormTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-namespace Magento\Tax\Block\Adminhtml\Rate;
-
-use Magento\TestFramework\Helper\Bootstrap;
-
-class FormTest extends \PHPUnit_Framework_TestCase
-{
-    /**
-     * @var \Magento\Framework\ObjectManagerInterface
-     */
-    protected $_objectManager;
-
-    /** @var \Magento\Tax\Block\Adminhtml\Rate\Form */
-    protected $_block;
-
-    protected function setUp()
-    {
-        $this->_objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
-        $this->_block = $this->_objectManager->create(
-            'Magento\Tax\Block\Adminhtml\Rate\Form'
-        );
-    }
-
-    public function testGetRateCollection()
-    {
-        /** @var \Magento\Tax\Model\Resource\Calculation\Rate\Collection $collection */
-        $collection = Bootstrap::getObjectManager()->get('Magento\Tax\Model\Resource\Calculation\Rate\Collection');
-        $dbTaxRatesQty = $collection->count();
-        if (($dbTaxRatesQty == 0) || ($collection->getFirstItem()->getId() != 1)) {
-            $this->fail("Preconditions failed.");
-        }
-
-        $ratesCollection = $this->_block->getRateCollection();
-
-        $collectionTaxRatesQty = count($ratesCollection);
-        $this->assertEquals($dbTaxRatesQty, $collectionTaxRatesQty, 'Tax rates quantity is invalid.');
-        $taxRate = $ratesCollection[0];
-        $expectedTaxRateData = [
-            'tax_calculation_rate_id' => '1',
-            'code' => 'US-CA-*-Rate 1',
-            'tax_country_id' => 'US',
-            'tax_region_id' => '12',
-            'region_name' => 'CA',
-            'tax_postcode' => '*',
-            'rate' => '8.25',
-            'zip_is_range' => null,
-            'zip_from' => null,
-            'zip_to' => null,
-            'rate' => '8.25',
-        ];
-        $this->assertEquals($taxRate, $expectedTaxRateData, 'Tax rate data is invalid.');
-    }
-}
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php b/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php
index b6e6a11859231e98d4f303fcec041ff6f690ad38..ab5c6cd2ba0d57b05bd90e250dd4550a254010e1 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Controller/Adminhtml/RateTest.php
@@ -44,6 +44,11 @@ class RateTest extends \Magento\Backend\Utility\Controller
         $this->assertEquals($expectedData['tax_postcode'], $rate->getTaxPostcode());
     }
 
+    /**
+     * Data provider for testAjaxSaveAction
+     *
+     * @return array
+     */
     public function ajaxSaveActionDataProvider()
     {
         $postData = ['rate' => '10', 'tax_country_id' => 'US', 'tax_region_id' => '1'];
@@ -193,4 +198,102 @@ class RateTest extends \Magento\Backend\Utility\Controller
             ]
         ];
     }
+
+    /**
+     * @dataProvider ajaxSaveActionDataProvider
+     * @magentoDbIsolation enabled
+     *
+     * @param array $rateClassData
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    public function testAjaxLoadAction($rateClassData)
+    {
+        /** @var \Magento\Tax\Api\Data\TaxRateInterfaceFactory $rateClassFactory */
+        $rateClassFactory = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+            'Magento\Tax\Api\Data\TaxRateInterfaceFactory'
+        );
+
+        $rateClass = $rateClassFactory->create();
+        $rateClass->setRate($rateClassData['rate'])
+                  ->setTaxCountryId($rateClassData['tax_country_id'])
+                  ->setTaxRegionId($rateClassData['tax_region_id'])
+                  ->setCode($rateClassData['code'])
+                  ->setZipFrom($rateClassData['zip_from'])
+                  ->setZipIsRange($rateClassData['zip_is_range'])
+                  ->setZipFrom($rateClassData['zip_from'])
+                  ->setZipTo($rateClassData['zip_to'])
+                  ->setTaxPostcode($rateClassData['tax_postcode']);
+
+        $rateClass->save($rateClass);
+
+        $rateClassId=$rateClass->getTaxCalculationRateId();
+        /** @var $class \Magento\Tax\Model\Calculation\Rate */
+        $class = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
+            ->create('Magento\Tax\Model\Calculation\Rate')
+            ->load($rateClassId, 'tax_calculation_rate_id');
+
+        $this->assertEquals($rateClassData['tax_country_id'], $class->getTaxCountryId());
+        $this->assertEquals($rateClassData['tax_region_id'], $class->getTaxRegionId());
+        $this->assertEquals($rateClassData['code'], $class->getCode());
+        $this->assertEquals($rateClassData['rate'], $class->getRate());
+        $this->assertEquals($rateClassData['zip_is_range']==1 ? 1 : 0, $class->getZipIsRange() ? 1 : 0);
+        if ($rateClassData['zip_is_range']=='1') {
+            $this->assertEquals($rateClassData['zip_from'], $class->getZipFrom());
+            $this->assertEquals($rateClassData['zip_to'], $class->getZipTo());
+        }
+
+        $postData = [ 'id' => $rateClassId ];
+        $this->getRequest()->setPostValue($postData);
+        $this->dispatch('backend/tax/rate/ajaxLoad');
+        $jsonBody = $this->getResponse()->getBody();
+
+        $result = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+            'Magento\Framework\Json\Helper\Data'
+        )->jsonDecode(
+            $jsonBody
+        );
+
+        $this->assertTrue(is_array($result));
+        $this->assertArrayHasKey('success', $result);
+        $this->assertTrue($result['success'] == true);
+        $this->assertArrayHasKey('result', $result);
+        $this->assertTrue(is_array($result['result']));
+        $this->assertEquals($result['result']['tax_country_id'], $class->getTaxCountryId());
+        $this->assertEquals($result['result']['tax_region_id'], $class->getTaxRegionId());
+        $this->assertEquals($result['result']['tax_postcode'], $class->getTaxPostcode());
+        $this->assertEquals($result['result']['code'], $class->getCode());
+        $this->assertEquals($result['result']['rate'], $class->getRate());
+
+        $expectedZipIsRange=$result['result']['zip_is_range'] == 1  ? 1 : 0;
+        $this->assertEquals($expectedZipIsRange, $class->getZipIsRange() ? 1 : 0);
+        if ($expectedZipIsRange) {
+            $this->assertEquals($result['result']['zip_from'], $class->getZipFrom());
+            $this->assertEquals($result['result']['zip_to'], $class->getZipTo());
+        }
+    }
+
+    /**
+     * @magentoDbIsolation enabled
+     *
+     */
+    public function testAjaxNonLoadAction()
+    {
+        $postData = [ 'id' => 99999999 ];
+        $this->getRequest()->setPostValue($postData);
+        $this->dispatch('backend/tax/rate/ajaxLoad');
+        $jsonBody = $this->getResponse()->getBody();
+
+        $result = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
+            'Magento\Framework\Json\Helper\Data'
+        )->jsonDecode(
+            $jsonBody
+        );
+
+        $this->assertTrue(is_array($result));
+        $this->assertArrayHasKey('success', $result);
+        $this->assertTrue($result['success'] == false);
+        $this->assertTrue(!array_key_exists('result', $result));
+        $this->assertArrayHasKey('error_message', $result);
+        $this->assertTrue(strlen($result['error_message'])>0);
+    }
 }
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php
index 1e9c43ca7fd49621116be74791c4a180c2c4763d..2b0d311757846900c2a9b1f9ccffb29cd221d4c1 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Calculation/RateRepositoryTest.php
@@ -9,6 +9,7 @@ namespace Magento\Tax\Model\Calculation;
 use Magento\Framework\Exception\InputException;
 use Magento\Framework\Exception\NoSuchEntityException;
 use Magento\Tax\Api\Data\TaxRateInterface;
+use Magento\Tax\Model\Calculation\Rate;
 use Magento\Tax\Model\TaxRuleFixtureFactory;
 use Magento\TestFramework\Helper\Bootstrap;
 
@@ -566,14 +567,14 @@ class RateRepositoryTest extends \PHPUnit_Framework_TestCase
 
         return [
             'eq' => [
-                [$filterBuilder->setField(TaxRateInterface::KEY_REGION_ID)->setValue(42)->create()],
+                [$filterBuilder->setField(Rate::KEY_REGION_ID)->setValue(42)->create()],
                 null,
                 ['US - 42 - 7.5', 'US - 42 - 22'],
             ],
             'and' => [
                 [
-                    $filterBuilder->setField(TaxRateInterface::KEY_REGION_ID)->setValue(42)->create(),
-                    $filterBuilder->setField(TaxRateInterface::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(),
+                    $filterBuilder->setField(Rate::KEY_REGION_ID)->setValue(42)->create(),
+                    $filterBuilder->setField(Rate::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(),
                 ],
                 [],
                 ['US - 42 - 22'],
@@ -581,15 +582,14 @@ class RateRepositoryTest extends \PHPUnit_Framework_TestCase
             'or' => [
                 [],
                 [
-                    $filterBuilder->setField(TaxRateInterface::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(),
-                    $filterBuilder->setField(TaxRateInterface::KEY_PERCENTAGE_RATE)->setValue(10.0)->create(),
+                    $filterBuilder->setField(Rate::KEY_PERCENTAGE_RATE)->setValue(22.0)->create(),
+                    $filterBuilder->setField(Rate::KEY_PERCENTAGE_RATE)->setValue(10.0)->create(),
                 ],
                 ['US - 42 - 22', 'US - 12 - 10'],
             ],
             'like' => [
                 [
-                    $filterBuilder->setField(TaxRateInterface::KEY_CODE)->setValue('%7.5')->setConditionType('like')
-                        ->create(),
+                    $filterBuilder->setField(Rate::KEY_CODE)->setValue('%7.5')->setConditionType('like')->create(),
                 ],
                 [],
                 ['US - 42 - 7.5', 'US - 12 - 7.5'],
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
index 861079efbac7df145af34557037fa6b79bb886e0..331bfebab31ad4078259e912e982010cb44302e6 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/Sales/Total/Quote/SetupUtil.php
@@ -8,6 +8,7 @@
 
 namespace Magento\Tax\Model\Sales\Total\Quote;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Tax\Model\Config;
 use Magento\Tax\Model\Calculation;
 
@@ -221,7 +222,7 @@ class SetupUtil
             $config->saveConfig(
                 $path,
                 $value,
-                \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+                ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
                 0
             );
         }
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php
index 0c3af306bdeaa8f70d4a0b9e3e0d4bda252693e1..4f8e5da64ba2d3f77397905e9a470127626a84a6 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxCalculationTest.php
@@ -5,8 +5,8 @@
  */
 namespace Magento\Tax\Model;
 
-use Magento\Framework\Model\AbstractExtensibleModel;
 use Magento\Tax\Api\Data\TaxClassKeyInterface;
+use Magento\Tax\Model\TaxClass\Key;
 use Magento\TestFramework\Helper\Bootstrap;
 
 /**
@@ -121,8 +121,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase
             'quantity' => 2,
             'unit_price' => 10,
             'tax_class_key' => [
-                TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
-                TaxClassKeyInterface::KEY_VALUE => 'DefaultProductClass',
+                Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
+                Key::KEY_VALUE => 'DefaultProductClass',
             ],
         ];
         $oneProductResults = [
@@ -649,8 +649,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase
                     ],
                 ],
                 'customer_tax_class_key' => [
-                    TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
-                    TaxClassKeyInterface::KEY_VALUE => 'DefaultCustomerClass',
+                    Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
+                    Key::KEY_VALUE => 'DefaultCustomerClass',
                 ],
             ],
             'expected_tax_details' => [
@@ -1064,8 +1064,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase
             'quantity' => 9,
             'unit_price' => 0.33, // this is including the store tax of 10%. Pre tax is 0.3
             'tax_class_key' => [
-                TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
-                TaxClassKeyInterface::KEY_VALUE => 'HigherProductClass',
+                Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
+                Key::KEY_VALUE => 'HigherProductClass',
             ],
             'tax_included' => true,
         ];
@@ -1817,8 +1817,8 @@ class TaxCalculationTest extends \PHPUnit_Framework_TestCase
                     && is_string($value)
                 ) {
                     $value = [
-                        TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
-                        TaxClassKeyInterface::KEY_VALUE => $this->taxClassIds[$value],
+                        Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
+                        Key::KEY_VALUE => $this->taxClassIds[$value],
                     ];
                 }
             }
diff --git a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php
index 606540aa09c90b91c3b41a2c6345447a247bfd38..853a42f225c85c999748b0763388cc58d48a83e9 100644
--- a/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php
+++ b/dev/tests/integration/testsuite/Magento/Tax/Model/TaxClass/ManagementTest.php
@@ -8,6 +8,7 @@ namespace Magento\Tax\Model\TaxClass;
 use Magento\Tax\Api\Data\TaxClassInterfaceFactory;
 use Magento\Tax\Api\Data\TaxClassKeyInterface;
 use Magento\Tax\Api\TaxClassManagementInterface;
+use Magento\Tax\Model\TaxClass\Key;
 use Magento\TestFramework\Helper\Bootstrap;
 
 class ManagementTest extends \PHPUnit_Framework_TestCase
@@ -63,8 +64,8 @@ class ManagementTest extends \PHPUnit_Framework_TestCase
         $this->dataObjectHelper->populateWithArray(
             $taxClassKeyTypeId,
             [
-                TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
-                TaxClassKeyInterface::KEY_VALUE => $taxClassId,
+                Key::KEY_TYPE => TaxClassKeyInterface::TYPE_ID,
+                Key::KEY_VALUE => $taxClassId,
             ],
             '\Magento\Tax\Api\Data\TaxClassKeyInterface'
         );
@@ -76,8 +77,8 @@ class ManagementTest extends \PHPUnit_Framework_TestCase
         $this->dataObjectHelper->populateWithArray(
             $taxClassKeyTypeName,
             [
-                TaxClassKeyInterface::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
-                TaxClassKeyInterface::KEY_VALUE => $taxClassName,
+                Key::KEY_TYPE => TaxClassKeyInterface::TYPE_NAME,
+                Key::KEY_VALUE => $taxClassName,
             ],
             '\Magento\Tax\Api\Data\TaxClassKeyInterface'
         );
diff --git a/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml b/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml
index 911f2482ebcec61f1d6bf914463511ffd1252359..0a0b04ba6836b74eb5d7584a9290136e540f8d8b 100644
--- a/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml
+++ b/dev/tests/integration/testsuite/Magento/ToolkitFramework/_files/small.xml
@@ -29,6 +29,8 @@
         <!-- The price rule condition: minimum products amount in shopping cart for price rule to be applied -->
         <customers>20</customers>
         <!-- Number of customers to generate -->
+        <orders>80</orders>
+        <!-- Orders count -->
         <configs> <!-- Config variables and values for change -->
             <config>
                 <path>admin/security/use_form_key</path>
@@ -79,5 +81,39 @@
                 <value>8080</value>
             </config>
         </configs>
+        <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) -->
+            <indexer>
+                <id>catalog_category_product</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_category</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_price</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_attribute</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>cataloginventory_stock</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_rule</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_product</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogsearch_fulltext</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+        </indexers>
     </profile>
-</config>
\ No newline at end of file
+</config>
diff --git a/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php b/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php
index 00cd8ce5cb1d148ff81ebcad4ad463fd1933df2f..056fe99b815516c7bb1b20181297a78013312268 100644
--- a/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php
+++ b/dev/tests/performance/testsuite/fixtures/catalog_category_flat_enabled.php
@@ -3,6 +3,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+use Magento\Framework\App\Config\ScopeConfigInterface;
 
 /** @var \Magento\TestFramework\Application $this */
 
@@ -13,7 +14,7 @@ $configData = $this->getObjectManager()->create('Magento\Framework\App\Config\Va
 $configData->setPath(
     'catalog/frontend/flat_catalog_category'
 )->setScope(
-    \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+    ScopeConfigInterface::SCOPE_TYPE_DEFAULT
 )->setScopeId(
     0
 )->setValue(
diff --git a/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php b/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php
index 2258e72023d4ad642e1e799134e8b32cbb94d1ab..e06ed0e26c053f2742d6a132f17b4a0f4b8558ce 100644
--- a/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php
+++ b/dev/tests/performance/testsuite/fixtures/catalog_product_flat_enabled.php
@@ -3,6 +3,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+use Magento\Framework\App\Config\ScopeConfigInterface;
 
 /** @var \Magento\TestFramework\Application $this */
 
@@ -13,7 +14,7 @@ $configData = $this->getObjectManager()->create('Magento\Framework\App\Config\Va
 $configData->setPath(
     'catalog/frontend/flat_catalog_product'
 )->setScope(
-    \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+    ScopeConfigInterface::SCOPE_TYPE_DEFAULT
 )->setScopeId(
     0
 )->setValue(
diff --git a/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php b/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php
index e0f50505c4adcf397a358a5d8d31e336bd00a3e6..15b445ce1f8b5ca75cf4b6eb1ae20a179bcfa356 100644
--- a/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php
+++ b/dev/tests/performance/testsuite/fixtures/shipping_flatrate_enabled.php
@@ -3,6 +3,7 @@
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
+use Magento\Framework\App\Config\ScopeConfigInterface;
 
 /** @var \Magento\TestFramework\Application $this */
 
@@ -13,7 +14,7 @@ $configData = $this->getObjectManager()->create('Magento\Framework\App\Config\Va
 $configData->setPath(
     'carriers/flatrate/active'
 )->setScope(
-    \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+    ScopeConfigInterface::SCOPE_TYPE_DEFAULT
 )->setScopeId(
     0
 )->setValue(
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt b/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt
index 2fd6ad123621cb9b830d5d02b3840463d4520fbc..564d756255d7e6b7be5db625ccf8ebb0e634a026 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/Readme/_files/blacklist/ce.txt
@@ -35,5 +35,6 @@ lib/internal/Magento/Framework/System
 lib/internal/Magento/Framework/Test
 lib/internal/Magento/Framework/App/Utility
 lib/internal/Magento/Framework/Url
+lib/internal/Magento/Framework/UrlInterface
 lib/internal/Magento/Framework/Xml
 lib/internal/Magento/Framework
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php
index a3fa40a7f43a6718bc2949067c23dd314b874c11..b0e6f2627029a8da911b3e6a4358c3aff4f133af 100644
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_constants.php
@@ -702,4 +702,26 @@ return [
         'Use \Magento\Eav\Model\Entity\Type::getDefaultAttributeSetId() method instead',
     ],
     ['CONFIG_PATH_WSDL_CACHE_ENABLED', 'Magento\Webapi\Model\Soap\Server'],
+    ['ENTITY', 'Magento\Framework\App\Config\ValueInterface'],
+    ['XML_PATH_ALLOW_CURRENCIES_INSTALLED', 'Magento\Framework\Locale\CurrencyInterface'],
+    [
+        'DEFAULT_CURRENCY',
+        'Magento\Framework\Locale\CurrencyInterface',
+        'Magento\Framework\Locale\Currency::DEFAULT_CURRENCY'
+    ],
+    [
+        'DEFAULT_LOCALE',
+        'Magento\Framework\Locale\ResolverInterface',
+        'Magento\Framework\Locale\Resolver::DEFAULT_LOCALE'
+    ],
+    [
+        'DEFAULT_GROUP',
+        'Magento\Framework\Message\ManagerInterface',
+        'Magento\Framework\Message\Manager::DEFAULT_GROUP'
+    ],
+    [
+        'SCOPE_DEFAULT',
+        'Magento\Framework\App\ScopeInterface',
+        'Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT'
+    ]
 ];
diff --git a/dev/tests/unit/framework/bootstrap.php b/dev/tests/unit/framework/bootstrap.php
index b30486b50a17dc3a6e0b67adaa072868b1747736..45ae70bc21fbb39fc79b347cc4749a6243d18d7f 100755
--- a/dev/tests/unit/framework/bootstrap.php
+++ b/dev/tests/unit/framework/bootstrap.php
@@ -6,6 +6,11 @@
 
 require_once __DIR__ . '/../../../../app/autoload.php';
 
+$updateAppBootstrap = __DIR__ . '/../../../../update/app/bootstrap.php';
+if (file_exists($updateAppBootstrap)) {
+    require_once $updateAppBootstrap;
+}
+
 if (!defined('TESTS_TEMP_DIR')) {
     define('TESTS_TEMP_DIR', dirname(__DIR__) . '/tmp');
 }
diff --git a/dev/tests/unit/phpunit.xml.dist b/dev/tests/unit/phpunit.xml.dist
index 5c785e252e48aa8c82aa8bcfe817c761b38ecc23..4c5587187da794847e7e0e6c47157106193a14fd 100644
--- a/dev/tests/unit/phpunit.xml.dist
+++ b/dev/tests/unit/phpunit.xml.dist
@@ -17,6 +17,7 @@
         <directory suffix="Test.php">../../../lib/internal/*/*/Test/Unit</directory>
         <directory suffix="Test.php">../../../lib/internal/*/*/*/Test/Unit</directory>
         <directory suffix="Test.php">../../../setup/src/*/*/Test/Unit</directory>
+        <directory suffix="Test.php">../../../update/dev/tests/unit/testsuite</directory>
     </testsuite>
     <php>
         <ini name="date.timezone" value="America/Los_Angeles"/>
@@ -26,6 +27,7 @@
             <directory suffix=".php">../../../app/code/*</directory>
             <directory suffix=".php">../../../lib/internal/Magento</directory>
             <directory suffix=".php">../../../setup/src/*</directory>
+            <directory suffix=".php">../../../update/app/code/*</directory>
             <exclude>
                 <directory>../../../app/code/*/*/Test</directory>
                 <directory>../../../lib/internal/*/*/Test</directory>
diff --git a/dev/tools/performance-toolkit/fixtures/indexers_states_apply.php b/dev/tools/performance-toolkit/fixtures/indexers_states_apply.php
new file mode 100644
index 0000000000000000000000000000000000000000..8b072e2b68995399e051ab77cf868efb15638630
--- /dev/null
+++ b/dev/tools/performance-toolkit/fixtures/indexers_states_apply.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+/**
+ * Class IndexersStatesApplyFixture
+ */
+class IndexersStatesApplyFixture extends \Magento\ToolkitFramework\Fixture
+{
+    /**
+     * @var int
+     */
+    protected $priority = 170;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function execute()
+    {
+        $indexers = \Magento\ToolkitFramework\Config::getInstance()->getValue('indexers', []);
+        if (!isset($indexers["indexer"]) || empty($indexers["indexer"])) {
+            return;
+        }
+        $this->application->resetObjectManager();
+        foreach ($indexers["indexer"] as $indexer) {
+            $this->application->indexersStates[$indexer['id']] = ($indexer['set_scheduled'] == "true");
+        }
+        $this->application->getObjectManager()->get('Magento\Framework\App\CacheInterface')
+            ->clean([\Magento\Framework\App\Config::CACHE_TAG]);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getActionTitle()
+    {
+        return 'Indexers Mode Changes';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function introduceParamLabels()
+    {
+        return [];
+    }
+}
+
+return new IndexersStatesApplyFixture($this);
diff --git a/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php b/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php
index 083ac9fefbc6f619d4e0f2fecc1c007e23ae1cb5..d1df2a830d5d1b7a1df8785c278e3147ffe8d28a 100644
--- a/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php
+++ b/dev/tools/performance-toolkit/framework/Magento/ToolkitFramework/Application.php
@@ -69,6 +69,13 @@ class Application
      */
     protected $_initArguments;
 
+    /**
+     * Indexers states values
+     *
+     * @var array
+     */
+    public $indexersStates;
+
     /**
      * @param string $applicationBaseDir
      * @param \Magento\Framework\Shell $shell
diff --git a/dev/tools/performance-toolkit/generate.php b/dev/tools/performance-toolkit/generate.php
index 861abee07ef81a10ceee9e80fc2aa9ef91ccb20d..3eae0197757f726c3e509d244dcf0c9ea88aab66 100644
--- a/dev/tools/performance-toolkit/generate.php
+++ b/dev/tools/performance-toolkit/generate.php
@@ -11,7 +11,8 @@ try {
 
     $shell = new Zend_Console_Getopt(
         [
-            'profile-s' => 'Profile configuration file',
+            'profile=s' => 'Profile configuration file',
+            'skip-reindex-i' => 'Skip reindex (Default - 0)',
         ]
     );
 
@@ -46,10 +47,10 @@ try {
     $indexerListIds = $config->getIndexers();
     /** @var $indexerRegistry \Magento\Indexer\Model\IndexerRegistry */
     $indexerRegistry = $application->getObjectManager()->create('Magento\Indexer\Model\IndexerRegistry');
-    $indexersState = [];
+    $application->indexerStates = [];
     foreach ($indexerListIds as $key => $indexerId) {
         $indexer = $indexerRegistry->get($indexerId['indexer_id']);
-        $indexersState[$indexerId['indexer_id']] = $indexer->isScheduled();
+        $application->indexersStates[$indexerId['indexer_id']] = $indexer->isScheduled();
         $indexer->setScheduled(true);
     }
 
@@ -65,10 +66,12 @@ try {
     foreach ($indexerListIds as $indexerId) {
         /** @var $indexer \Magento\Indexer\Model\Indexer */
         $indexer = $indexerRegistry->get($indexerId['indexer_id']);
-        $indexer->setScheduled($indexersState[$indexerId['indexer_id']]);
+        $indexer->setScheduled($application->indexersStates[$indexerId['indexer_id']]);
     }
 
-    $application->reindex();
+    if (!\Magento\ToolkitFramework\Helper\Cli::getOption('skip-reindex')) {
+        $application->reindex();
+    }
     $totalEndTime = microtime(true);
     $totalResultTime = $totalEndTime - $totalStartTime;
 
diff --git a/dev/tools/performance-toolkit/profiles/ce/extra_large.xml b/dev/tools/performance-toolkit/profiles/ce/extra_large.xml
index 758fbef97f8730233c826ba009a5e24424b663fa..9d5767fbfba33f62963bed898213dc28ae30d95c 100644
--- a/dev/tools/performance-toolkit/profiles/ce/extra_large.xml
+++ b/dev/tools/performance-toolkit/profiles/ce/extra_large.xml
@@ -34,5 +34,39 @@
                 <value>1</value>
             </config>
         </configs>
+        <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) -->
+            <indexer>
+                <id>catalog_category_product</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_category</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_price</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_attribute</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>cataloginventory_stock</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_rule</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_product</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogsearch_fulltext</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+        </indexers>
     </profile>
 </config>
diff --git a/dev/tools/performance-toolkit/profiles/ce/large.xml b/dev/tools/performance-toolkit/profiles/ce/large.xml
index f89fa10faddc16f5e05f7be400b71f9ff94a8f1d..bdbce2f2b8b7ca464606e3b5b351cc658f5ce624 100644
--- a/dev/tools/performance-toolkit/profiles/ce/large.xml
+++ b/dev/tools/performance-toolkit/profiles/ce/large.xml
@@ -34,5 +34,39 @@
                 <value>1</value>
             </config>
         </configs>
+        <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) -->
+            <indexer>
+                <id>catalog_category_product</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_category</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_price</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_attribute</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>cataloginventory_stock</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_rule</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_product</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogsearch_fulltext</id>
+                <set_scheduled>true</set_scheduled>
+            </indexer>
+        </indexers>
     </profile>
 </config>
diff --git a/dev/tools/performance-toolkit/profiles/ce/medium.xml b/dev/tools/performance-toolkit/profiles/ce/medium.xml
index fbcb4168bce8dbe2eb1b4f2bbe90a63c29bee4dd..8c91400d50bd697ab080985541bf35c760067642 100644
--- a/dev/tools/performance-toolkit/profiles/ce/medium.xml
+++ b/dev/tools/performance-toolkit/profiles/ce/medium.xml
@@ -34,5 +34,39 @@
                 <value>1</value>
             </config>
         </configs>
+        <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) -->
+            <indexer>
+                <id>catalog_category_product</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_category</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_price</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_attribute</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>cataloginventory_stock</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_rule</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_product</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogsearch_fulltext</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+        </indexers>
     </profile>
 </config>
diff --git a/dev/tools/performance-toolkit/profiles/ce/small.xml b/dev/tools/performance-toolkit/profiles/ce/small.xml
index 6507aa52f618843fac56a130461696f4342ac99f..79f2605bc775d7c3fdb0fe92106df966c6330c0c 100644
--- a/dev/tools/performance-toolkit/profiles/ce/small.xml
+++ b/dev/tools/performance-toolkit/profiles/ce/small.xml
@@ -34,5 +34,39 @@
                 <value>1</value>
             </config>
         </configs>
+        <indexers> <!-- Indexer mode value (true - Update by Schedule, false - Update on Save) -->
+            <indexer>
+                <id>catalog_category_product</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_category</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_price</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalog_product_attribute</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>cataloginventory_stock</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_rule</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogrule_product</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+            <indexer>
+                <id>catalogsearch_fulltext</id>
+                <set_scheduled>false</set_scheduled>
+            </indexer>
+        </indexers>
     </profile>
 </config>
diff --git a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php
index 98f5dc5d5e07250fe7b427ca68286cb99c22ee35..aa91250b41fb3ce83f95c8d3df94e144c79a9bfb 100644
--- a/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php
+++ b/lib/internal/Magento/Framework/Api/Code/Generator/ExtensionAttributesGenerator.php
@@ -8,6 +8,7 @@ namespace Magento\Framework\Api\Code\Generator;
 use Magento\Framework\Code\Generator\DefinedClasses;
 use Magento\Framework\Code\Generator\Io;
 use Magento\Framework\Api\SimpleDataObjectConverter;
+use Magento\Framework\Api\Config\Converter;
 
 /**
  * Code generator for data object extensions.
@@ -79,14 +80,15 @@ class ExtensionAttributesGenerator extends \Magento\Framework\Code\Generator\Ent
     protected function _getClassMethods()
     {
         $methods = [];
-        foreach ($this->getCustomAttributes() as $attributeName => $attributeType) {
+        foreach ($this->getCustomAttributes() as $attributeName => $attributeMetadata) {
+            $attributeType = $attributeMetadata[Converter::DATA_TYPE];
             $propertyName = SimpleDataObjectConverter::snakeCaseToCamelCase($attributeName);
             $getterName = 'get' . ucfirst($propertyName);
             $setterName = 'set' . ucfirst($propertyName);
             $methods[] = [
                 'name' => $getterName,
                 'body' => "return \$this->_get('{$attributeName}');",
-                'docblock' => ['tags' => [['name' => 'return', 'description' => $attributeType]]],
+                'docblock' => ['tags' => [['name' => 'return', 'description' => $attributeType . '|null']]],
             ];
             $methods[] = [
                 'name' => $setterName,
@@ -150,11 +152,12 @@ class ExtensionAttributesGenerator extends \Magento\Framework\Code\Generator\Ent
         }
         $dataInterface = ltrim($this->getSourceClassName(), '\\');
         if (isset($this->allCustomAttributes[$dataInterface])) {
-            foreach ($this->allCustomAttributes[$dataInterface] as $attributeName => $attributeType) {
+            foreach ($this->allCustomAttributes[$dataInterface] as $attributeName => $attributeMetadata) {
+                $attributeType = $attributeMetadata[Converter::DATA_TYPE];
                 if (strpos($attributeType, '\\') !== false) {
                     /** Add preceding slash to class names, while leaving primitive types as is */
                     $attributeType = $this->_getFullyQualifiedClassName($attributeType);
-                    $this->allCustomAttributes[$dataInterface][$attributeName] =
+                    $this->allCustomAttributes[$dataInterface][$attributeName][Converter::DATA_TYPE] =
                         $this->_getFullyQualifiedClassName($attributeType);
                 }
             }
diff --git a/lib/internal/Magento/Framework/Api/Config/Converter.php b/lib/internal/Magento/Framework/Api/Config/Converter.php
index f21db3bbd9b224b1c29eaa36be3f6043dcdf4ae9..68914a98b5c77ab1bc452c90fc26dfbee64797d8 100644
--- a/lib/internal/Magento/Framework/Api/Config/Converter.php
+++ b/lib/internal/Magento/Framework/Api/Config/Converter.php
@@ -7,6 +7,9 @@ namespace Magento\Framework\Api\Config;
 
 class Converter implements \Magento\Framework\Config\ConverterInterface
 {
+    const RESOURCE_PERMISSIONS = "resourceRefs";
+    const DATA_TYPE = "type";
+
     /**
      * Convert dom node tree to array
      *
@@ -21,7 +24,7 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
         }
 
         /** @var \DOMNodeList $types */
-        $types = $source->getElementsByTagName('custom_attributes');
+        $types = $source->getElementsByTagName('extension_attributes');
         /** @var \DOMNode $type */
         foreach ($types as $type) {
             $typeConfig = [];
@@ -32,9 +35,22 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
                 $code = $attribute->getAttribute('code');
                 $codeType = $attribute->getAttribute('type');
 
-                if ($code && $codeType) {
-                    $typeConfig[$code] = $codeType;
+                $resourcesElement = $attribute->getElementsByTagName('resources')->item(0);
+                $resourceRefs = [];
+                if ($resourcesElement && $resourcesElement->nodeType === XML_ELEMENT_NODE) {
+                    $singleResourceElements = $resourcesElement->getElementsByTagName('resource');
+                    foreach ($singleResourceElements as $element) {
+                        if ($element->nodeType != XML_ELEMENT_NODE) {
+                            continue;
+                        }
+                        $resourceRefs[] = $element->attributes->getNamedItem('ref')->nodeValue;
+                    }
                 }
+
+                $typeConfig[$code] = [
+                    self::DATA_TYPE => $codeType,
+                    self::RESOURCE_PERMISSIONS => $resourceRefs,
+                ];
             }
 
             $output[$typeName] = $typeConfig;
diff --git a/lib/internal/Magento/Framework/Api/Config/Reader.php b/lib/internal/Magento/Framework/Api/Config/Reader.php
index 8cd1a8ac3293568553c30f0e126ab1ee61b78a38..6bc97c71c11563cfb46efeaaedf2ecd03fd3bf83 100644
--- a/lib/internal/Magento/Framework/Api/Config/Reader.php
+++ b/lib/internal/Magento/Framework/Api/Config/Reader.php
@@ -13,8 +13,8 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem
      * @var array
      */
     protected $_idAttributes = [
-        '/config/custom_attributes' => 'for',
-        '/config/custom_attributes/attribute' => 'code',
+        '/config/extension_attributes' => 'for',
+        '/config/extension_attributes/attribute' => 'code',
     ];
 
     /**
@@ -32,7 +32,7 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem
         \Magento\Framework\Api\Config\Converter $converter,
         \Magento\Framework\Api\Config\SchemaLocator $schemaLocator,
         \Magento\Framework\Config\ValidationStateInterface $validationState,
-        $fileName = 'data_object.xml',
+        $fileName = 'service_data_attributes.xml',
         $idAttributes = [],
         $domDocumentClass = 'Magento\Framework\Config\Dom',
         $defaultScope = 'global'
diff --git a/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php b/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php
index 86662ab2528136ecc4c8182d650a55b9e80f0a2f..1e8334c643def347b9392eba52d624b5fdaab556 100644
--- a/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php
+++ b/lib/internal/Magento/Framework/Api/Config/SchemaLocator.php
@@ -16,7 +16,7 @@ class SchemaLocator implements \Magento\Framework\Config\SchemaLocatorInterface
      */
     public function getSchema()
     {
-        return realpath(__DIR__ . '/../etc/data_object.xsd');
+        return realpath(__DIR__ . '/../etc/service_data_attributes.xsd');
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Api/DataObjectHelper.php b/lib/internal/Magento/Framework/Api/DataObjectHelper.php
index d0a22a194be444bbc4b71f5d60dd6bf29fd1de42..fc3a3b0920c60430f28de95db7cd6a6ab84f6868 100644
--- a/lib/internal/Magento/Framework/Api/DataObjectHelper.php
+++ b/lib/internal/Magento/Framework/Api/DataObjectHelper.php
@@ -6,6 +6,8 @@
 
 namespace Magento\Framework\Api;
 
+use Magento\Framework\Reflection\MethodsMap;
+
 class DataObjectHelper
 {
     /**
@@ -28,22 +30,30 @@ class DataObjectHelper
      */
     protected $extensionFactory;
 
+    /**
+     * @var MethodsMap
+     */
+    protected $methodsMapProcessor;
+
     /**
      * @param ObjectFactory $objectFactory
      * @param \Magento\Framework\Reflection\DataObjectProcessor $objectProcessor
      * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor
      * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
+     * @param MethodsMap $methodsMapProcessor
      */
     public function __construct(
         ObjectFactory $objectFactory,
         \Magento\Framework\Reflection\DataObjectProcessor $objectProcessor,
         \Magento\Framework\Reflection\TypeProcessor $typeProcessor,
-        \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
+        \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
+        MethodsMap $methodsMapProcessor
     ) {
         $this->objectFactory = $objectFactory;
         $this->objectProcessor = $objectProcessor;
         $this->typeProcessor = $typeProcessor;
         $this->extensionFactory = $extensionFactory;
+        $this->methodsMapProcessor = $methodsMapProcessor;
     }
 
     /**
@@ -128,7 +138,7 @@ class DataObjectHelper
         if ($interfaceName == null) {
             $interfaceName = get_class($dataObject);
         }
-        $returnType = $this->objectProcessor->getMethodReturnType($interfaceName, $getterMethodName);
+        $returnType = $this->methodsMapProcessor->getMethodReturnType($interfaceName, $getterMethodName);
         if ($this->typeProcessor->isTypeSimple($returnType)) {
             $dataObject->$methodName($value);
             return $this;
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php
index 5207a20588c73dcbf83aa767b907f585db8bb114..efdeb4ae914348fa51302891271d018b06c70067 100644
--- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesGeneratorTest.php
@@ -6,6 +6,8 @@
 // @codingStandardsIgnoreFile
 namespace Magento\Framework\Api\Test\Unit\Code\Generator;
 
+use Magento\Framework\Api\Config\Converter;
+
 class ExtensionAttributesGeneratorTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -44,11 +46,21 @@ class ExtensionAttributesGeneratorTest extends \PHPUnit_Framework_TestCase
             ->willReturn(
                 [
                     'Magento\Catalog\Api\Data\ProductInterface' => [
-                        'string_attribute' => 'string',
-                        'complex_object_attribute' => '\Magento\Bundle\Api\Data\OptionInterface[]'
+                        'string_attribute' => [
+                            Converter::DATA_TYPE => 'string',
+                            Converter::RESOURCE_PERMISSIONS => [],
+
+                        ],
+                        'complex_object_attribute' => [
+                            Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\OptionInterface[]',
+                            Converter::RESOURCE_PERMISSIONS => [],
+                        ],
                     ],
                     'Magento\Catalog\Api\Data\Product' => [
-                        'should_not_include' => 'string',
+                        'should_not_include' => [
+                            Converter::DATA_TYPE => 'string',
+                            Converter::RESOURCE_PERMISSIONS => [],
+                        ],
                     ],
                 ]
             );
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php
index 8f80baf39beeeec1955800e7ea4a51ac12d02382..33d4ecf9e7906c2abd4bfe15ef429c539405b059 100644
--- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/ExtensionAttributesInterfaceGeneratorTest.php
@@ -6,6 +6,8 @@
 // @codingStandardsIgnoreFile
 namespace Magento\Framework\Api\Test\Unit\Code\Generator;
 
+use Magento\Framework\Api\Config\Converter;
+
 class ExtensionAttributesInterfaceGeneratorTest extends \PHPUnit_Framework_TestCase
 {
     public function testGenerate()
@@ -19,11 +21,20 @@ class ExtensionAttributesInterfaceGeneratorTest extends \PHPUnit_Framework_TestC
             ->willReturn(
                 [
                     'Magento\Catalog\Api\Data\ProductInterface' => [
-                        'string_attribute' => 'string',
-                        'complex_object_attribute' => '\Magento\Bundle\Api\Data\OptionInterface[]'
+                        'string_attribute' => [
+                            Converter::DATA_TYPE => 'string',
+                            Converter::RESOURCE_PERMISSIONS => [],
+                        ],
+                        'complex_object_attribute' => [
+                            Converter::DATA_TYPE => '\Magento\Bundle\Api\Data\OptionInterface[]',
+                            Converter::RESOURCE_PERMISSIONS => [],
+                        ],
                     ],
                     'Magento\Catalog\Api\Data\Product' => [
-                        'should_not_include' => 'string',
+                        'should_not_include' => [
+                            Converter::DATA_TYPE => 'string',
+                            Converter::RESOURCE_PERMISSIONS => [],
+                        ],
                     ],
                 ]
             );
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt
index 8b5caad1ecc4456ae51380e25c735be8f2660d37..0f9838bd8736c9c0545b6bb82ab5adbe9762a946 100644
--- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtension.txt
@@ -6,7 +6,7 @@ namespace Magento\Catalog\Api\Data;
 class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject implements \Magento\Catalog\Api\Data\ProductExtensionInterface
 {
     /**
-     * @return string
+     * @return string|null
      */
     public function getStringAttribute()
     {
@@ -24,7 +24,7 @@ class ProductExtension extends \Magento\Framework\Api\AbstractSimpleObject imple
     }
 
     /**
-     * @return \Magento\Bundle\Api\Data\OptionInterface[]
+     * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
      */
     public function getComplexObjectAttribute()
     {
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt
index ec9edd7affc2d68e5444d914b833bff81ccc1d4d..75dde39b2151952b49beb587ce4eecff56418997 100644
--- a/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/Code/Generator/_files/SampleExtensionInterface.txt
@@ -6,7 +6,7 @@ namespace Magento\Catalog\Api\Data;
 interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttributesInterface
 {
     /**
-     * @return string
+     * @return string|null
      */
     public function getStringAttribute();
 
@@ -17,7 +17,7 @@ interface ProductExtensionInterface extends \Magento\Framework\Api\ExtensionAttr
     public function setStringAttribute($stringAttribute);
 
     /**
-     * @return \Magento\Bundle\Api\Data\OptionInterface[]
+     * @return \Magento\Bundle\Api\Data\OptionInterface[]|null
      */
     public function getComplexObjectAttribute();
 
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php
index 94f5a4494a75dd9c57afa409c90eb622ad26827c..d4629213b20be2d7bbca2ff16bacf0b36de608e5 100644
--- a/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/Config/ConverterTest.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Framework\Api\Test\Unit\Config;
 
+use Magento\Framework\Api\Config\Converter;
+
 class ConverterTest extends \PHPUnit_Framework_TestCase
 {
     /**
@@ -47,15 +49,39 @@ class ConverterTest extends \PHPUnit_Framework_TestCase
             'Magento\Tax\Api\Data\TaxRateInterface' => [
             ],
             'Magento\Catalog\Api\Data\ProductInterface' => [
-                'stock_item' => 'Magento\CatalogInventory\Api\Data\StockItemInterface'
+                'stock_item' => [
+                    Converter::DATA_TYPE => 'Magento\CatalogInventory\Api\Data\StockItemInterface',
+                    Converter::RESOURCE_PERMISSIONS => [],
+                ],
             ],
             'Magento\Customer\Api\Data\CustomerInterface' => [
-                'custom_1' => 'Magento\Customer\Api\Data\CustomerCustom',
-                'custom_2' => 'Magento\CustomerExtra\Api\Data\CustomerCustom2'
+                'custom_1' => [
+                    Converter::DATA_TYPE => 'Magento\Customer\Api\Data\CustomerCustom',
+                    Converter::RESOURCE_PERMISSIONS => [],
+                ],
+                'custom_2' => [
+                    Converter::DATA_TYPE => 'Magento\CustomerExtra\Api\Data\CustomerCustom2',
+                    Converter::RESOURCE_PERMISSIONS => [],
+                ],
+            ],
+            'Magento\Customer\Api\Data\CustomerInterface2' => [
+                'custom_with_permission' => [
+                    Converter::DATA_TYPE => 'Magento\Customer\Api\Data\CustomerCustom',
+                    Converter::RESOURCE_PERMISSIONS => [
+                        'Magento_Customer::manage',
+                    ],
+                ],
+                'custom_with_multiple_permissions' => [
+                    Converter::DATA_TYPE => 'Magento\CustomerExtra\Api\Data\CustomerCustom2',
+                    Converter::RESOURCE_PERMISSIONS => [
+                        'Magento_Customer::manage',
+                        'Magento_Customer::manage2',
+                    ],
+                ],
             ],
         ];
 
-        $xmlFile = __DIR__ . '/_files/data_object_valid.xml';
+        $xmlFile = __DIR__ . '/_files/service_data_attributes.xml';
         $dom = new \DOMDocument();
         $dom->loadXML(file_get_contents($xmlFile));
         $result = $this->_converter->convert($dom);
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php
index f0df38d59a070958d77d6ddccb0d339162d256da..f05583db0490194e949261bce0a2c176932dbdb5 100644
--- a/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/Config/SchemaLocatorTest.php
@@ -22,7 +22,7 @@ class SchemaLocatorTest extends \PHPUnit_Framework_TestCase
 
     public function testGetSchema()
     {
-        $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/data_object.xsd');
+        $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd');
         $actual = str_replace('\\', '/', $this->_model->getSchema());
         $this->assertEquals($expected, $actual);
     }
@@ -30,7 +30,7 @@ class SchemaLocatorTest extends \PHPUnit_Framework_TestCase
     public function testGetPerFileSchema()
     {
         $actual = str_replace('\\', '/', $this->_model->getPerFileSchema());
-        $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/data_object.xsd');
+        $expected = str_replace('\\', '/', BP . '/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd');
         $this->assertEquals($expected, $actual);
     }
 }
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/data_object_valid.xml b/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/data_object_valid.xml
deleted file mode 100644
index 67606deab2eb46e78dc3022a723b41a1bb25bfa3..0000000000000000000000000000000000000000
--- a/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/data_object_valid.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0"?>
-<!--
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
--->
-<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/data_object.xsd">
-    <custom_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
-    </custom_attributes>
-    <custom_attributes for="Magento\Catalog\Api\Data\ProductInterface">
-        <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItemInterface" />
-    </custom_attributes>
-    <custom_attributes for="Magento\Customer\Api\Data\CustomerInterface">
-        <attribute code="custom_1" type="Magento\Customer\Api\Data\CustomerCustom" />
-        <attribute code="custom_2" type="Magento\CustomerExtra\Api\Data\CustomerCustom2" />
-    </custom_attributes>
-</config>
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/service_data_attributes.xml b/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/service_data_attributes.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4f6a9838bfcdb6bd9dbfa20b18ffcfde80bd625d
--- /dev/null
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/Config/_files/service_data_attributes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../../../lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd">
+    <extension_attributes for="Magento\Tax\Api\Data\TaxRateInterface">
+    </extension_attributes>
+    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
+        <attribute code="stock_item" type="Magento\CatalogInventory\Api\Data\StockItemInterface" />
+    </extension_attributes>
+    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface">
+        <attribute code="custom_1" type="Magento\Customer\Api\Data\CustomerCustom" />
+        <attribute code="custom_2" type="Magento\CustomerExtra\Api\Data\CustomerCustom2" />
+    </extension_attributes>
+    <extension_attributes for="Magento\Customer\Api\Data\CustomerInterface2">
+        <attribute code="custom_with_permission" type="Magento\Customer\Api\Data\CustomerCustom">
+            <resources>
+                <resource ref="Magento_Customer::manage"/>
+            </resources>
+        </attribute>
+        <attribute code="custom_with_multiple_permissions" type="Magento\CustomerExtra\Api\Data\CustomerCustom2">
+            <resources>
+                <resource ref="Magento_Customer::manage"/>
+                <resource ref="Magento_Customer::manage2"/>
+            </resources>
+        </attribute>
+    </extension_attributes>
+</config>
diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php
index ff229abd7d22e9be0f810da4034336e5e9631f63..65c7cb906d1f2f15b8f251145633cdb6e85848c5 100644
--- a/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php
+++ b/lib/internal/Magento/Framework/Api/Test/Unit/DataObjectHelperTest.php
@@ -43,6 +43,11 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase
      */
     protected $attributeValueFactoryMock;
 
+    /**
+     * @var \Magento\Framework\Reflection\MethodsMap|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $methodsMapProcessor;
+
     public function setUp()
     {
         $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
@@ -53,6 +58,9 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase
         $this->objectProcessorMock = $this->getMockBuilder('\Magento\Framework\Reflection\DataObjectProcessor')
             ->disableOriginalConstructor()
             ->getMock();
+        $this->methodsMapProcessor = $this->getMockBuilder('\Magento\Framework\Reflection\MethodsMap')
+            ->disableOriginalConstructor()
+            ->getMock();
         $this->attributeValueFactoryMock = $this->getMockBuilder('\Magento\Framework\Api\AttributeValueFactory')
             ->disableOriginalConstructor()
             ->getMock();
@@ -63,6 +71,7 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase
                 'objectFactory' => $this->objectFactoryMock,
                 'typeProcessor' => $this->typeProcessor,
                 'objectProcessor' => $this->objectProcessorMock,
+                'methodsMapProcessor' => $this->methodsMapProcessor,
             ]
         );
     }
@@ -103,11 +112,11 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase
             ],
         ];
 
-        $this->objectProcessorMock->expects($this->at(0))
+        $this->methodsMapProcessor->expects($this->at(0))
             ->method('getMethodReturnType')
             ->with('\Magento\Customer\Api\Data\AddressInterface', 'getStreet')
             ->willReturn('string[]');
-        $this->objectProcessorMock->expects($this->at(1))
+        $this->methodsMapProcessor->expects($this->at(1))
             ->method('getMethodReturnType')
             ->with('\Magento\Customer\Api\Data\AddressInterface', 'getRegion')
             ->willReturn('\Magento\Customer\Api\Data\RegionInterface');
@@ -317,11 +326,11 @@ class DataObjectHelperTest extends \PHPUnit_Framework_TestCase
             ->method('buildOutputDataArray')
             ->with($secondAddressDataObject, get_class($firstAddressDataObject))
             ->willReturn($data2);
-        $this->objectProcessorMock->expects($this->at(1))
+        $this->methodsMapProcessor->expects($this->at(0))
             ->method('getMethodReturnType')
             ->with('Magento\Customer\Model\Data\Address', 'getStreet')
             ->willReturn('string[]');
-        $this->objectProcessorMock->expects($this->at(2))
+        $this->methodsMapProcessor->expects($this->at(1))
             ->method('getMethodReturnType')
             ->with('Magento\Customer\Model\Data\Address', 'getRegion')
             ->willReturn('\Magento\Customer\Api\Data\RegionInterface');
diff --git a/lib/internal/Magento/Framework/Api/etc/data_object.xsd b/lib/internal/Magento/Framework/Api/etc/data_object.xsd
deleted file mode 100644
index 0983dbbf912385c5a83dd1b8db18d768071c42fc..0000000000000000000000000000000000000000
--- a/lib/internal/Magento/Framework/Api/etc/data_object.xsd
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
--->
-<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
-  <xs:element name="config">
-      <xs:complexType>
-          <xs:sequence>
-              <xs:element name="custom_attributes" type="custom_attributesType" minOccurs="1" maxOccurs="unbounded">
-                  <xs:annotation>
-                      <xs:documentation>Main schema element. Extended Attributes</xs:documentation>
-                  </xs:annotation>
-              </xs:element>
-          </xs:sequence>
-      </xs:complexType>
-  </xs:element>
-  <xs:complexType name="attributeType">
-    <xs:simpleContent>
-      <xs:extension base="xs:string">
-        <xs:attribute type="xs:string" name="code" use="required"/>
-        <xs:attribute type="xs:string" name="type" use="required"/>
-      </xs:extension>
-    </xs:simpleContent>
-  </xs:complexType>
-  <xs:complexType name="custom_attributesType">
-    <xs:sequence>
-      <xs:element type="attributeType" name="attribute" minOccurs="0" maxOccurs="unbounded"/>
-    </xs:sequence>
-    <xs:attribute type="xs:string" name="for" use="required"/>
-  </xs:complexType>
-</xs:schema>
diff --git a/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd b/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd
new file mode 100644
index 0000000000000000000000000000000000000000..24590e3d758c79afeef7ce85c8cb9f6547e6f524
--- /dev/null
+++ b/lib/internal/Magento/Framework/Api/etc/service_data_attributes.xsd
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+-->
+<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <xs:element name="config">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="extension_attributes" type="extension_attributesType" minOccurs="1"
+                            maxOccurs="unbounded">
+                    <xs:annotation>
+                        <xs:documentation>Main schema element. Extended Attributes</xs:documentation>
+                    </xs:annotation>
+                </xs:element>
+            </xs:sequence>
+        </xs:complexType>
+    </xs:element>
+    <xs:complexType name="attributeType">
+        <xs:sequence>
+            <xs:element name="resources" type="resourcesType" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="code" use="required"/>
+        <xs:attribute type="xs:string" name="type" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="extension_attributesType">
+        <xs:sequence>
+            <xs:element type="attributeType" name="attribute" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute type="xs:string" name="for" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="resourcesType">
+        <xs:sequence>
+            <xs:element name="resource" type="resourceType" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="resourceType">
+        <xs:attribute name="ref" use="required">
+            <xs:simpleType>
+                <xs:restriction base="xs:string">
+                    <xs:pattern value=".+(, ?.+)*"/>
+                </xs:restriction>
+            </xs:simpleType>
+        </xs:attribute>
+    </xs:complexType>
+</xs:schema>
diff --git a/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php b/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php
index bbdd3787b1825ffa9c35882776a9173c03b9607a..e8a3e92263e7d9b50226911e4d445b1a663171f7 100644
--- a/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php
+++ b/lib/internal/Magento/Framework/App/Area/FrontNameResolverInterface.php
@@ -7,6 +7,10 @@
  */
 namespace Magento\Framework\App\Area;
 
+/**
+ * Interface FrontNameResolverInterface
+ * @api
+ */
 interface FrontNameResolverInterface
 {
     /**
diff --git a/lib/internal/Magento/Framework/App/AreaInterface.php b/lib/internal/Magento/Framework/App/AreaInterface.php
index 806a7983424599690e3b7f02443fb7e9b4b328a5..544ac56e19e6f10dd13c5a284d56dba2d6e4b010 100644
--- a/lib/internal/Magento/Framework/App/AreaInterface.php
+++ b/lib/internal/Magento/Framework/App/AreaInterface.php
@@ -6,6 +6,9 @@
  */
 namespace Magento\Framework\App;
 
+/**
+ * Interface AreaInterface
+ */
 interface AreaInterface
 {
     const PART_CONFIG = 'config';
diff --git a/lib/internal/Magento/Framework/App/AreaList.php b/lib/internal/Magento/Framework/App/AreaList.php
index 270fb22e36675b08576b06500e7cd6da38f39466..c010388797d870fa25e7aec9a192edef8ed0a73b 100644
--- a/lib/internal/Magento/Framework/App/AreaList.php
+++ b/lib/internal/Magento/Framework/App/AreaList.php
@@ -63,6 +63,7 @@ class AreaList
      *
      * @param string $frontName
      * @return null|string
+     * @api
      */
     public function getCodeByFrontName($frontName)
     {
@@ -84,6 +85,7 @@ class AreaList
      *
      * @param string $areaCode
      * @return string
+     * @api
      */
     public function getFrontName($areaCode)
     {
@@ -94,6 +96,7 @@ class AreaList
      * Retrieve area codes
      *
      * @return string[]
+     * @api
      */
     public function getCodes()
     {
@@ -105,6 +108,7 @@ class AreaList
      *
      * @param string $areaCode
      * @return string
+     * @api
      */
     public function getDefaultRouter($areaCode)
     {
diff --git a/lib/internal/Magento/Framework/App/Cache/Type/Config.php b/lib/internal/Magento/Framework/App/Cache/Type/Config.php
index ded73329969b9f7faec6409ed73715cab06390eb..506eff32a8e099edc9a4a9193b7e42bafdc2ac58 100644
--- a/lib/internal/Magento/Framework/App/Cache/Type/Config.php
+++ b/lib/internal/Magento/Framework/App/Cache/Type/Config.php
@@ -25,10 +25,40 @@ class Config extends TagScope implements CacheInterface
     const CACHE_TAG = 'CONFIG';
 
     /**
-     * @param FrontendPool $cacheFrontendPool
+     * @var \Magento\Framework\App\Cache\Type\FrontendPool
      */
-    public function __construct(FrontendPool $cacheFrontendPool)
+    private $cacheFrontendPool;
+
+    /**
+     * @param \Magento\Framework\App\Cache\Type\FrontendPool $cacheFrontendPool
+     */
+    public function __construct(\Magento\Framework\App\Cache\Type\FrontendPool $cacheFrontendPool)
+    {
+        $this->cacheFrontendPool = $cacheFrontendPool;
+    }
+
+    /**
+     * Retrieve cache frontend instance being decorated
+     *
+     * @return \Magento\Framework\Cache\FrontendInterface
+     */
+    protected function _getFrontend()
+    {
+        $frontend = parent::_getFrontend();
+        if (!$frontend) {
+            $frontend = $this->cacheFrontendPool->get(self::TYPE_IDENTIFIER);
+            $this->setFrontend($frontend);
+        }
+        return $frontend;
+    }
+
+    /**
+     * Retrieve cache tag name
+     *
+     * @return string
+     */
+    public function getTag()
     {
-        parent::__construct($cacheFrontendPool->get(self::TYPE_IDENTIFIER), self::CACHE_TAG);
+        return self::CACHE_TAG;
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Config.php b/lib/internal/Magento/Framework/App/Config.php
index 047849f0034bde6164c2a054c81817e2daccdd2a..898a19a00ea5f20ff98bce5a7cb5eaa322972869 100644
--- a/lib/internal/Magento/Framework/App/Config.php
+++ b/lib/internal/Magento/Framework/App/Config.php
@@ -7,6 +7,8 @@
  */
 namespace Magento\Framework\App;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class Config implements \Magento\Framework\App\Config\ScopeConfigInterface
 {
     /**
@@ -37,7 +39,7 @@ class Config implements \Magento\Framework\App\Config\ScopeConfigInterface
      */
     public function getValue(
         $path = null,
-        $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+        $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
         $scopeCode = null
     ) {
         return $this->_scopePool->getScope($scope, $scopeCode)->getValue($path);
@@ -51,7 +53,7 @@ class Config implements \Magento\Framework\App\Config\ScopeConfigInterface
      * @param null|string $scopeCode
      * @return bool
      */
-    public function isSetFlag($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeCode = null)
+    public function isSetFlag($path, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null)
     {
         return (bool) $this->getValue($path, $scope, $scopeCode);
     }
diff --git a/lib/internal/Magento/Framework/App/Config/Data.php b/lib/internal/Magento/Framework/App/Config/Data.php
index 3b5f17fe71639559575a77bc431fccc7071388f0..38389e71e40aaf297d4983bdde8377db615d5493 100644
--- a/lib/internal/Magento/Framework/App/Config/Data.php
+++ b/lib/internal/Magento/Framework/App/Config/Data.php
@@ -42,10 +42,7 @@ class Data implements DataInterface
     }
 
     /**
-     * Retrieve configuration value by path
-     *
-     * @param null|string $path
-     * @return array|string
+     * @inheritdoc
      */
     public function getValue($path = null)
     {
@@ -65,11 +62,7 @@ class Data implements DataInterface
     }
 
     /**
-     * Set configuration value
-     *
-     * @param string $path
-     * @param mixed $value
-     * @return void
+     * @inheritdoc
      */
     public function setValue($path, $value)
     {
diff --git a/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php b/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php
index 8f473e2a37b2ba3c9cf3a0e707dd43f9bbe70902..8084dcd5d08f45c755a9c5b0490bf1fea78f8894 100644
--- a/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php
+++ b/lib/internal/Magento/Framework/App/Config/Data/ProcessorFactory.php
@@ -30,21 +30,22 @@ class ProcessorFactory
     /**
      * Get concrete Processor Interface instance
      *
-     * @param string $model
+     * @param string $processorModel Classname of the instance to get
      * @return ProcessorInterface
-     * @throws \InvalidArgumentException
+     * @throws \InvalidArgumentException In case the given classname is not an instance of ProcessorInterface
+     * @api
      */
-    public function get($model)
+    public function get($processorModel)
     {
-        if (!isset($this->_pool[$model])) {
-            $instance = $this->_objectManager->create($model);
+        if (!isset($this->_pool[$processorModel])) {
+            $instance = $this->_objectManager->create($processorModel);
             if (!$instance instanceof ProcessorInterface) {
                 throw new \InvalidArgumentException(
-                    $model . ' is not instance of \Magento\Framework\App\Config\Data\ProcessorInterface'
+                    $processorModel . ' is not instance of \Magento\Framework\App\Config\Data\ProcessorInterface'
                 );
             }
-            $this->_pool[$model] = $instance;
+            $this->_pool[$processorModel] = $instance;
         }
-        return $this->_pool[$model];
+        return $this->_pool[$processorModel];
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php b/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php
index 71102131f42cf4071b6d213d225ac8178956945a..d4467339676739d2e92250b986d992458772cc68 100644
--- a/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/Data/ProcessorInterface.php
@@ -1,19 +1,22 @@
 <?php
 /**
- * Processor interface
- *
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
 namespace Magento\Framework\App\Config\Data;
 
+/**
+ * Processes data from admin store configuration fields
+ *
+ * @api
+ */
 interface ProcessorInterface
 {
     /**
      * Process config value
      *
-     * @param string $value
-     * @return string
+     * @param string $value Raw value of the configuration field
+     * @return string Processed value
      */
     public function processValue($value);
 }
diff --git a/lib/internal/Magento/Framework/App/Config/DataInterface.php b/lib/internal/Magento/Framework/App/Config/DataInterface.php
index bbdaf54d1c342a11803527f92160ebacef27a0a6..b07ca0e0df1fc3ce8085227057148619c371f0f9 100644
--- a/lib/internal/Magento/Framework/App/Config/DataInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/DataInterface.php
@@ -5,11 +5,27 @@
  */
 namespace Magento\Framework\App\Config;
 
+/**
+ * Configuration data storage
+ *
+ * @api
+ */
 interface DataInterface
 {
     /**
+     * Retrieve configuration value by path
+     *
      * @param string|null $path
-     * @return string|array
+     * @return mixed
      */
     public function getValue($path);
+
+    /**
+     * Set configuration value by path
+     *
+     * @param string $path
+     * @param mixed $value
+     * @return void
+     */
+    public function setValue($path, $value);
 }
diff --git a/lib/internal/Magento/Framework/App/Config/Initial.php b/lib/internal/Magento/Framework/App/Config/Initial.php
index 68db7b1eb84d42c669f3874b180038292a087983..3b2beb5ca37423ee054d94d2d3289bdacd21bca5 100644
--- a/lib/internal/Magento/Framework/App/Config/Initial.php
+++ b/lib/internal/Magento/Framework/App/Config/Initial.php
@@ -7,6 +7,8 @@
  */
 namespace Magento\Framework\App\Config;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class Initial
 {
     /**
@@ -50,14 +52,14 @@ class Initial
     /**
      * Get initial data by given scope
      *
-     * @param string $scope
+     * @param string $scope Format is scope type and scope code separated by pipe: e.g. "type|code"
      * @return array
      */
     public function getData($scope)
     {
         list($scopeType, $scopeCode) = array_pad(explode('|', $scope), 2, null);
 
-        if (\Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT == $scopeType) {
+        if (ScopeConfigInterface::SCOPE_TYPE_DEFAULT == $scopeType) {
             return isset($this->_data[$scopeType]) ? $this->_data[$scopeType] : [];
         } elseif ($scopeCode) {
             return isset($this->_data[$scopeType][$scopeCode]) ? $this->_data[$scopeType][$scopeCode] : [];
diff --git a/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php b/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php
index 37a371b547f40439addcfad205d29dd9c178fb07..f68048ddc94611267e17df9c85eb203bf28097a2 100644
--- a/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/MutableScopeConfigInterface.php
@@ -8,6 +8,9 @@
 
 namespace Magento\Framework\App\Config;
 
+/**
+ * @api
+ */
 interface MutableScopeConfigInterface extends \Magento\Framework\App\Config\ScopeConfigInterface
 {
     /**
@@ -15,14 +18,14 @@ interface MutableScopeConfigInterface extends \Magento\Framework\App\Config\Scop
      *
      * @param string $path
      * @param mixed $value
-     * @param string $scope
+     * @param string $scopeType
      * @param null|string $scopeCode
      * @return void
      */
     public function setValue(
         $path,
         $value,
-        $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+        $scopeType = \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
         $scopeCode = null
     );
 }
diff --git a/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php b/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php
index 5b9d9d78eb3b4789a2ac6477e5e686ff3e4f0c6f..f9fc887a878e0ec0b73e2ad956ae0ff3b1ae166a 100644
--- a/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/ReinitableConfigInterface.php
@@ -8,6 +8,9 @@
 
 namespace Magento\Framework\App\Config;
 
+/**
+ * @api
+ */
 interface ReinitableConfigInterface extends \Magento\Framework\App\Config\MutableScopeConfigInterface
 {
     /**
diff --git a/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php b/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php
index aa0e06048980767a44d1f0688ebe6956bbb13355..8084c21ab0d9cd26be2319dd9cfad4e00992dae7 100644
--- a/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/Resource/ConfigInterface.php
@@ -6,12 +6,12 @@
 namespace Magento\Framework\App\Config\Resource;
 
 /**
- * Resource Config Interface
+ * Resource for storing store configuration values
  */
 interface ConfigInterface
 {
     /**
-     * Save config value
+     * Save config value to the storage resource
      *
      * @param string $path
      * @param string $value
@@ -22,7 +22,7 @@ interface ConfigInterface
     public function saveConfig($path, $value, $scope, $scopeId);
 
     /**
-     * Delete config value
+     * Delete config value from the storage resource
      *
      * @param string $path
      * @param string $scope
diff --git a/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php b/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php
index bf20ecd1d530881e76749275afcff6dbd87939d1..cdcbb42828e51d4ff2f3e13c9449037c64ac9ad5 100644
--- a/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/Scope/ReaderInterface.php
@@ -12,7 +12,9 @@ interface ReaderInterface
     /**
      * Read configuration scope
      *
+     * @param string|null $scopeType
+     * @throws \Exception May throw an exception if the given scope is invalid
      * @return array
      */
-    public function read();
+    public function read($scopeType = null);
 }
diff --git a/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php b/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php
index ac84e7358b5d2599a531ea45047d92626370bd44..e98fe284024f1780d731500d810516011b7528ba 100644
--- a/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/ScopeConfigInterface.php
@@ -8,25 +8,33 @@
 
 namespace Magento\Framework\App\Config;
 
+/**
+ * @api
+ */
 interface ScopeConfigInterface
 {
     /**
-     * Retrieve config value by path and scope
+     * Default scope type
+     */
+    const SCOPE_TYPE_DEFAULT = 'default';
+
+    /**
+     * Retrieve config value by path and scope.
      *
-     * @param string $path
-     * @param string $scope
+     * @param string $path The path through the tree of configuration values, e.g., 'general/store_information/name'
+     * @param string $scopeType The scope to use to determine config value, e.g., 'store' or 'default'
      * @param null|string $scopeCode
      * @return mixed
      */
-    public function getValue($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeCode = null);
+    public function getValue($path, $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null);
 
     /**
      * Retrieve config flag by path and scope
      *
-     * @param string $path
-     * @param string $scope
+     * @param string $path The path through the tree of configuration values, e.g., 'general/store_information/name'
+     * @param string $scopeType The scope to use to determine config value, e.g., 'store' or 'default'
      * @param null|string $scopeCode
      * @return bool
      */
-    public function isSetFlag($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeCode = null);
+    public function isSetFlag($path, $scopeType = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeCode = null);
 }
diff --git a/lib/internal/Magento/Framework/App/Config/ScopePool.php b/lib/internal/Magento/Framework/App/Config/ScopePool.php
index 34b24deb0028b63402dc2d169f2dbb47566dae9d..1a15575b1a514a0a0a04b728e454c2c18ef0645e 100644
--- a/lib/internal/Magento/Framework/App/Config/ScopePool.php
+++ b/lib/internal/Magento/Framework/App/Config/ScopePool.php
@@ -5,6 +5,8 @@
  */
 namespace Magento\Framework\App\Config;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class ScopePool
 {
     const CACHE_TAG = 'config_scopes';
@@ -78,7 +80,7 @@ class ScopePool
                 $data = unserialize($data);
             } else {
                 $reader = $this->_readerPool->getReader($scopeType);
-                if ($scopeType === \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT) {
+                if ($scopeType === ScopeConfigInterface::SCOPE_TYPE_DEFAULT) {
                     $data = $reader->read();
                 } else {
                     $data = $reader->read($scopeCode);
@@ -111,7 +113,7 @@ class ScopePool
     protected function _getScopeCode($scopeType, $scopeCode)
     {
         if (($scopeCode === null || is_numeric($scopeCode))
-            && $scopeType !== \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+            && $scopeType !== ScopeConfigInterface::SCOPE_TYPE_DEFAULT
         ) {
             $scopeResolver = $this->_scopeResolverPool->get($scopeType);
             $scopeCode = $scopeResolver->getScope($scopeCode);
diff --git a/lib/internal/Magento/Framework/App/Config/Storage/Writer.php b/lib/internal/Magento/Framework/App/Config/Storage/Writer.php
index b98e4074efa1b953fac38e02be2bbe435568d8c5..63b5ae83dfec68c85f61ebf17ae11396b485373e 100644
--- a/lib/internal/Magento/Framework/App/Config/Storage/Writer.php
+++ b/lib/internal/Magento/Framework/App/Config/Storage/Writer.php
@@ -7,6 +7,8 @@
  */
 namespace Magento\Framework\App\Config\Storage;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 class Writer implements \Magento\Framework\App\Config\Storage\WriterInterface
 {
     /**
@@ -32,7 +34,7 @@ class Writer implements \Magento\Framework\App\Config\Storage\WriterInterface
      * @param   int $scopeId
      * @return  void
      */
-    public function delete($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0)
+    public function delete($path, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0)
     {
         $this->_resource->deleteConfig(rtrim($path, '/'), $scope, $scopeId);
     }
@@ -46,7 +48,7 @@ class Writer implements \Magento\Framework\App\Config\Storage\WriterInterface
      * @param int $scopeId
      * @return void
      */
-    public function save($path, $value, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0)
+    public function save($path, $value, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0)
     {
         $this->_resource->saveConfig(rtrim($path, '/'), $value, $scope, $scopeId);
     }
diff --git a/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php b/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php
index 3a3a3af6241361ca757699bf1d1e37ecfab6ad45..67b2aebd5d4a6da8d19eaad74175ca26726a57e0 100644
--- a/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php
+++ b/lib/internal/Magento/Framework/App/Config/Storage/WriterInterface.php
@@ -7,6 +7,8 @@
  */
 namespace Magento\Framework\App\Config\Storage;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
+
 interface WriterInterface
 {
     /**
@@ -17,7 +19,7 @@ interface WriterInterface
      * @param   int $scopeId
      * @return void
      */
-    public function delete($path, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0);
+    public function delete($path, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0);
 
     /**
      * Save config value to storage
@@ -28,5 +30,5 @@ interface WriterInterface
      * @param int $scopeId
      * @return void
      */
-    public function save($path, $value, $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT, $scopeId = 0);
+    public function save($path, $value, $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT, $scopeId = 0);
 }
diff --git a/lib/internal/Magento/Framework/App/Config/Value.php b/lib/internal/Magento/Framework/App/Config/Value.php
index 17c50d20b8b3a854cb7f61a319ba5d21b21e1dac..92ee527a9c48eb77a35f527c2c471c2507d1b546 100644
--- a/lib/internal/Magento/Framework/App/Config/Value.php
+++ b/lib/internal/Magento/Framework/App/Config/Value.php
@@ -91,7 +91,7 @@ class Value extends \Magento\Framework\Model\AbstractModel implements \Magento\F
     {
         return (string)$this->_config->getValue(
             $this->getPath(),
-            $this->getScope() ?: \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+            $this->getScope() ?: ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
             $this->getScopeCode()
         );
     }
diff --git a/lib/internal/Magento/Framework/App/MutableScopeConfig.php b/lib/internal/Magento/Framework/App/MutableScopeConfig.php
index c94fb73f9d5e4cb35ddf246710bf464a932a25ad..418817465bd6f11af59b2ae5f4eaf53eea7559cd 100644
--- a/lib/internal/Magento/Framework/App/MutableScopeConfig.php
+++ b/lib/internal/Magento/Framework/App/MutableScopeConfig.php
@@ -9,6 +9,7 @@
 namespace Magento\Framework\App;
 
 use Magento\Framework\App\Config\MutableScopeConfigInterface;
+use Magento\Framework\App\Config\ScopeConfigInterface;
 
 class MutableScopeConfig extends Config implements MutableScopeConfigInterface
 {
@@ -24,7 +25,7 @@ class MutableScopeConfig extends Config implements MutableScopeConfigInterface
     public function setValue(
         $path,
         $value,
-        $scope = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT,
+        $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
         $scopeCode = null
     ) {
         if (empty($scopeCode)) {
diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php
index c09ead13054e791d5e6bff403623545b4e24f9e6..e6e502cf38171387aef13cdf606845d082f8664d 100644
--- a/lib/internal/Magento/Framework/App/Request/Http.php
+++ b/lib/internal/Magento/Framework/App/Request/Http.php
@@ -7,6 +7,7 @@
  */
 namespace Magento\Framework\App\Request;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\App\RequestInterface;
 use Magento\Framework\App\Route\ConfigInterface\Proxy as ConfigInterface;
 use Magento\Framework\HTTP\PhpEnvironment\Request;
@@ -384,7 +385,7 @@ class Http extends Request implements RequestInterface
         $offLoaderHeader = trim(
             (string)$config->getValue(
                 self::XML_PATH_OFFLOADER_HEADER,
-                \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT
+                ScopeConfigInterface::SCOPE_TYPE_DEFAULT
             )
         );
 
diff --git a/lib/internal/Magento/Framework/App/Resource.php b/lib/internal/Magento/Framework/App/Resource.php
index b38629a460a4fa8a7ed78c1a9e77450ec23c1c8a..b498ec9d349fea88b942b42d2ff6277c23a2449a 100644
--- a/lib/internal/Magento/Framework/App/Resource.php
+++ b/lib/internal/Magento/Framework/App/Resource.php
@@ -126,6 +126,7 @@ class Resource
      * @param   string|string[] $modelEntity
      * @param   string $connectionName
      * @return  string
+     * @api
      */
     public function getTableName($modelEntity, $connectionName = self::DEFAULT_READ_RESOURCE)
     {
diff --git a/lib/internal/Magento/Framework/App/Route/ConfigInterface.php b/lib/internal/Magento/Framework/App/Route/ConfigInterface.php
index c6798f7ce4ab659e813488a155d0aa467ac41449..d96a4af89eac0d2a4c3932995643c1cbccec25e5 100644
--- a/lib/internal/Magento/Framework/App/Route/ConfigInterface.php
+++ b/lib/internal/Magento/Framework/App/Route/ConfigInterface.php
@@ -1,12 +1,15 @@
 <?php
 /**
- * Routes configuration interface
- *
  * Copyright © 2015 Magento. All rights reserved.
  * See COPYING.txt for license details.
  */
 namespace Magento\Framework\App\Route;
 
+/**
+ * Routes configuration interface
+ *
+ * @api
+ */
 interface ConfigInterface
 {
     /**
@@ -32,7 +35,7 @@ interface ConfigInterface
      *
      * @param string $frontName
      * @param string $scope
-     * @return array
+     * @return string[]
      */
     public function getModulesByFrontName($frontName, $scope = null);
 }
diff --git a/lib/internal/Magento/Framework/App/ScopeInterface.php b/lib/internal/Magento/Framework/App/ScopeInterface.php
index d244ec5bf3a1f612722795b72e938fef77f1102f..77c8a8b890ac209400fdf8005636ee869d8ecfd0 100644
--- a/lib/internal/Magento/Framework/App/ScopeInterface.php
+++ b/lib/internal/Magento/Framework/App/ScopeInterface.php
@@ -7,11 +7,6 @@ namespace Magento\Framework\App;
 
 interface ScopeInterface
 {
-    /**
-     * Default scope type
-     */
-    const SCOPE_DEFAULT = 'default';
-
     /**
      * Retrieve scope code
      *
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/Type/ConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Type/ConfigTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6887709da50fbd2bf4fdb303f3e256749a4e197
--- /dev/null
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/Type/ConfigTest.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\App\Test\Unit\Cache\Type;
+
+use Magento\Framework\App\Cache\Type\FrontendPool;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
+
+class ConfigTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var \Magento\Framework\App\Cache\Type\Config
+     */
+    protected $model;
+
+    /**
+     * @var \Magento\Framework\Cache\FrontendInterface|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $frontendMock;
+
+    protected function setUp()
+    {
+        $cacheFrontendPoolMock = $this->getMockBuilder('Magento\Framework\App\Cache\Type\FrontendPool')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->model = (new ObjectManager($this))->getObject(
+            'Magento\Framework\App\Cache\Type\Config',
+            ['cacheFrontendPool' => $cacheFrontendPoolMock]
+        );
+        $this->frontendMock = $this->getMock('Magento\Framework\Cache\FrontendInterface');
+        $cacheFrontendPoolMock->expects($this->once())
+            ->method('get')
+            ->with(\Magento\Framework\App\Cache\Type\Config::TYPE_IDENTIFIER)
+            ->willReturn($this->frontendMock);
+    }
+
+    /**
+     * @param string $method
+     * @param array $params
+     * @param mixed $expectedResult
+     * @dataProvider proxyMethodDataProvider
+     */
+    public function testProxyMethod($method, $params, $expectedResult)
+    {
+        $helper = new \Magento\Framework\TestFramework\Unit\Helper\ProxyTesting();
+        $result = $helper->invokeWithExpectations($this->model, $this->frontendMock, $method, $params, $expectedResult);
+        $this->assertSame($expectedResult, $result);
+    }
+
+    /**
+     * @return array
+     */
+    public function proxyMethodDataProvider()
+    {
+        return [
+            ['test', ['record_id'], 111],
+            ['load', ['record_id'], '111'],
+            ['remove', ['record_id'], true],
+            ['getBackend', [], $this->getMock('Zend_Cache_Backend')],
+            ['getLowLevelFrontend', [], $this->getMock('Zend_Cache_Core')],
+        ];
+    }
+
+    public function testSave()
+    {
+        $expectedResult = new \stdClass();
+        $this->frontendMock->expects(
+            $this->once()
+        )->method(
+            'save'
+        )->with(
+            'test_value',
+            'test_id',
+            ['test_tag_one', 'test_tag_two', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG],
+            111
+        )->will(
+            $this->returnValue($expectedResult)
+        );
+        $actualResult = $this->model->save('test_value', 'test_id', ['test_tag_one', 'test_tag_two'], 111);
+        $this->assertSame($expectedResult, $actualResult);
+    }
+
+    public function testCleanModeAll()
+    {
+        $expectedResult = new \stdClass();
+        $this->frontendMock->expects(
+            $this->once()
+        )->method(
+            'clean'
+        )->with(
+            \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
+            [\Magento\Framework\App\Cache\Type\Config::CACHE_TAG]
+        )->will(
+            $this->returnValue($expectedResult)
+        );
+        $actualResult = $this->model->clean(
+            \Zend_Cache::CLEANING_MODE_ALL,
+            ['ignored_tag_one', 'ignored_tag_two']
+        );
+        $this->assertSame($expectedResult, $actualResult);
+    }
+
+    public function testCleanModeMatchingTag()
+    {
+        $expectedResult = new \stdClass();
+        $this->frontendMock->expects(
+            $this->once()
+        )->method(
+            'clean'
+        )->with(
+            \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
+            ['test_tag_one', 'test_tag_two', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG]
+        )->will(
+            $this->returnValue($expectedResult)
+        );
+        $actualResult = $this->model->clean(
+            \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
+            ['test_tag_one', 'test_tag_two']
+        );
+        $this->assertSame($expectedResult, $actualResult);
+    }
+
+    /**
+     * @param bool $fixtureResultOne
+     * @param bool $fixtureResultTwo
+     * @param bool $expectedResult
+     * @dataProvider cleanModeMatchingAnyTagDataProvider
+     */
+    public function testCleanModeMatchingAnyTag($fixtureResultOne, $fixtureResultTwo, $expectedResult)
+    {
+        $this->frontendMock->expects(
+            $this->at(0)
+        )->method(
+            'clean'
+        )->with(
+            \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
+            ['test_tag_one', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG]
+        )->will(
+            $this->returnValue($fixtureResultOne)
+        );
+        $this->frontendMock->expects(
+            $this->at(1)
+        )->method(
+            'clean'
+        )->with(
+            \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
+            ['test_tag_two', \Magento\Framework\App\Cache\Type\Config::CACHE_TAG]
+        )->will(
+            $this->returnValue($fixtureResultTwo)
+        );
+        $actualResult = $this->model->clean(
+            \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG,
+            ['test_tag_one', 'test_tag_two']
+        );
+        $this->assertEquals($expectedResult, $actualResult);
+    }
+
+    public function cleanModeMatchingAnyTagDataProvider()
+    {
+        return [
+            'failure, failure' => [false, false, false],
+            'failure, success' => [false, true, true],
+            'success, failure' => [true, false, true],
+            'success, success' => [true, true, true]
+        ];
+    }
+}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php
index 57040f09ee4dd82eb4ec80df99f0b827cc859573..887a1cf477663f00253816aed0bfdd273cd6fe5f 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Config/Storage/WriterTest.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Framework\App\Test\Unit\Config\Storage;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\App\ScopeInterface;
 
 class WriterTest extends \PHPUnit_Framework_TestCase
@@ -40,7 +41,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
     {
         $this->resource->expects($this->once())
             ->method('deleteConfig')
-            ->with('path', ScopeInterface::SCOPE_DEFAULT, 0);
+            ->with('path', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0);
         $this->model->delete('path');
     }
 
@@ -58,7 +59,7 @@ class WriterTest extends \PHPUnit_Framework_TestCase
     {
         $this->resource->expects($this->once())
             ->method('saveConfig')
-            ->with('path', 'value', ScopeInterface::SCOPE_DEFAULT, 0);
+            ->with('path', 'value', ScopeConfigInterface::SCOPE_TYPE_DEFAULT, 0);
         $this->model->save('path', 'value');
     }
 }
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php
index 090580da2726653983a7ed76ec9f16405393092f..8bfb9be2f3bb253932ba96c59d11d9819b704b50 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php
@@ -9,6 +9,7 @@
 
 namespace Magento\Framework\App\Test\Unit\Request;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use \Magento\Framework\App\Request\Http;
 use Magento\Framework\App\ScopeInterface;
 use Zend\Stdlib\Parameters;
@@ -325,7 +326,7 @@ class HttpTest extends \PHPUnit_Framework_TestCase
             ->getMock();
         $configMock->expects($this->exactly($configCall))
             ->method('getValue')
-            ->with(\Magento\Framework\App\Request\Http::XML_PATH_OFFLOADER_HEADER, ScopeInterface::SCOPE_DEFAULT)
+            ->with(\Magento\Framework\App\Request\Http::XML_PATH_OFFLOADER_HEADER, ScopeConfigInterface::SCOPE_TYPE_DEFAULT)
             ->willReturn($configOffloadHeader);
         $this->objectManager->expects($this->exactly($configCall))
             ->method('get')
diff --git a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php
index b856e8608aa726ebf0f32e07ecb01ad7ac8d7d6f..cae35e66ff83d2b40b562b77aaaeaed4d030221e 100644
--- a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php
+++ b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/Bare.php
@@ -27,6 +27,18 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
         $this->_frontend = $frontend;
     }
 
+    /**
+     * Set frontend
+     *
+     * @param \Magento\Framework\Cache\FrontendInterface $frontend
+     * @return $this
+     */
+    protected function setFrontend(\Magento\Framework\Cache\FrontendInterface $frontend)
+    {
+        $this->_frontend = $frontend;
+        return $this;
+    }
+
     /**
      * Retrieve cache frontend instance being decorated
      *
@@ -42,7 +54,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
      */
     public function test($identifier)
     {
-        return $this->_frontend->test($identifier);
+        return $this->_getFrontend()->test($identifier);
     }
 
     /**
@@ -50,7 +62,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
      */
     public function load($identifier)
     {
-        return $this->_frontend->load($identifier);
+        return $this->_getFrontend()->load($identifier);
     }
 
     /**
@@ -60,7 +72,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
      */
     public function save($data, $identifier, array $tags = [], $lifeTime = null)
     {
-        return $this->_frontend->save($data, $identifier, $tags, $lifeTime);
+        return $this->_getFrontend()->save($data, $identifier, $tags, $lifeTime);
     }
 
     /**
@@ -68,7 +80,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
      */
     public function remove($identifier)
     {
-        return $this->_frontend->remove($identifier);
+        return $this->_getFrontend()->remove($identifier);
     }
 
     /**
@@ -76,7 +88,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
      */
     public function clean($mode = \Zend_Cache::CLEANING_MODE_ALL, array $tags = [])
     {
-        return $this->_frontend->clean($mode, $tags);
+        return $this->_getFrontend()->clean($mode, $tags);
     }
 
     /**
@@ -84,7 +96,7 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
      */
     public function getBackend()
     {
-        return $this->_frontend->getBackend();
+        return $this->_getFrontend()->getBackend();
     }
 
     /**
@@ -92,6 +104,6 @@ class Bare implements \Magento\Framework\Cache\FrontendInterface
      */
     public function getLowLevelFrontend()
     {
-        return $this->_frontend->getLowLevelFrontend();
+        return $this->_getFrontend()->getLowLevelFrontend();
     }
 }
diff --git a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php
index 32c9e4422417cf220a28db98137ef954a26ba705..0f74b1894865754521bd9b595f0b14a5572e1e8c 100644
--- a/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php
+++ b/lib/internal/Magento/Framework/Cache/Frontend/Decorator/TagScope.php
@@ -45,7 +45,7 @@ class TagScope extends \Magento\Framework\Cache\Frontend\Decorator\Bare
      */
     public function save($data, $identifier, array $tags = [], $lifeTime = null)
     {
-        $tags[] = $this->_tag;
+        $tags[] = $this->getTag();
         return parent::save($data, $identifier, $tags, $lifeTime);
     }
 
@@ -59,16 +59,16 @@ class TagScope extends \Magento\Framework\Cache\Frontend\Decorator\Bare
         if ($mode == \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG) {
             $result = false;
             foreach ($tags as $tag) {
-                if (parent::clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [$tag, $this->_tag])) {
+                if (parent::clean(\Zend_Cache::CLEANING_MODE_MATCHING_TAG, [$tag, $this->getTag()])) {
                     $result = true;
                 }
             }
         } else {
             if ($mode == \Zend_Cache::CLEANING_MODE_ALL) {
                 $mode = \Zend_Cache::CLEANING_MODE_MATCHING_TAG;
-                $tags = [$this->_tag];
+                $tags = [$this->getTag()];
             } else {
-                $tags[] = $this->_tag;
+                $tags[] = $this->getTag();
             }
             $result = parent::clean($mode, $tags);
         }
diff --git a/lib/internal/Magento/Framework/Data/Collection/Filesystem.php b/lib/internal/Magento/Framework/Data/Collection/Filesystem.php
index 817c4b420519697de1ad27984ca0d286e151a125..cec62bc94a82b655866f71fe5118c13336868bef 100644
--- a/lib/internal/Magento/Framework/Data/Collection/Filesystem.php
+++ b/lib/internal/Magento/Framework/Data/Collection/Filesystem.php
@@ -284,6 +284,7 @@ class Filesystem extends \Magento\Framework\Data\Collection
      * @param bool $printQuery
      * @param bool $logQuery
      * @return $this
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      * @throws \Exception
      */
     public function loadData($printQuery = false, $logQuery = false)
@@ -668,6 +669,7 @@ class Filesystem extends \Magento\Framework\Data\Collection
      * @param string $value
      * @param string $type
      * @return $this
+     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
     public function addFilter($field, $value, $type = 'and')
     {
@@ -696,7 +698,7 @@ class Filesystem extends \Magento\Framework\Data\Collection
      */
     public function filterCallbackLike($field, $filterValue, $row)
     {
-        $filterValueRegex = str_replace('%', '(.*?)', preg_quote($filterValue, '/'));
+        $filterValueRegex = str_replace('%', '(.*?)', str_replace('\'', '', preg_quote($filterValue, '/')));
         return (bool)preg_match("/^{$filterValueRegex}\$/i", $row[$field]);
     }
 
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Collection/FilesystemTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Collection/FilesystemTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b764079a841b7f07f35dc9a692b9ffeb6968fe5f
--- /dev/null
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Collection/FilesystemTest.php
@@ -0,0 +1,30 @@
+<?php
+/***
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Data\Test\Unit\Collection;
+
+class FilesystemTest extends \PHPUnit_Framework_TestCase
+{
+    /** @var \Magento\Framework\Data\Collection\Filesystem */
+    private $model;
+
+    public function setUp()
+    {
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject('Magento\Framework\Data\Collection\Filesystem');
+    }
+
+    public function testFilterCallbackLike()
+    {
+        $field = 'field';
+        $row = [$field => 'beginning_filter_target_end',];
+        $filterValueSuccess = new \Zend_Db_Expr('%filter_target%');
+        $filterValueFailure = new \Zend_Db_Expr('%not_found_in_the_row%');
+
+        $this->assertTrue($this->model->filterCallbackLike($field, $filterValueSuccess, $row));
+        $this->assertFalse($this->model->filterCallbackLike($field, $filterValueFailure, $row));
+    }
+}
diff --git a/lib/internal/Magento/Framework/Locale/ConfigInterface.php b/lib/internal/Magento/Framework/Locale/ConfigInterface.php
index 3539f09409a800e83665f16d58871797f96161d0..e0f28fd015c33675eaed0445956c7a728a5e0d84 100644
--- a/lib/internal/Magento/Framework/Locale/ConfigInterface.php
+++ b/lib/internal/Magento/Framework/Locale/ConfigInterface.php
@@ -5,19 +5,24 @@
  */
 namespace Magento\Framework\Locale;
 
+/**
+ * Provides access to locale-related config information
+ *
+ * @api
+ */
 interface ConfigInterface
 {
     /**
      * Get list pre-configured allowed locales
      *
-     * @return array
+     * @return string[]
      */
     public function getAllowedLocales();
 
     /**
      * Get list pre-configured allowed currencies
      *
-     * @return array
+     * @return string[]
      */
     public function getAllowedCurrencies();
 }
diff --git a/lib/internal/Magento/Framework/Locale/Currency.php b/lib/internal/Magento/Framework/Locale/Currency.php
index 5d3c5a9ec9b7e961d870e4a458bc4b3fa69b5b89..f0be6c12172278b8e3a32b86848f296cd606d96a 100644
--- a/lib/internal/Magento/Framework/Locale/Currency.php
+++ b/lib/internal/Magento/Framework/Locale/Currency.php
@@ -7,6 +7,10 @@ namespace Magento\Framework\Locale;
 
 class Currency implements \Magento\Framework\Locale\CurrencyInterface
 {
+    /**
+     * Default currency
+     */
+    const DEFAULT_CURRENCY = 'USD';
     /**
      * @var array
      */
@@ -45,20 +49,15 @@ class Currency implements \Magento\Framework\Locale\CurrencyInterface
     }
 
     /**
-     * Retrieve currency code
-     *
-     * @return string
+     * @inheritdoc
      */
     public function getDefaultCurrency()
     {
-        return \Magento\Framework\Locale\CurrencyInterface::DEFAULT_CURRENCY;
+        return self::DEFAULT_CURRENCY;
     }
 
     /**
-     * Create \Zend_Currency object for current locale
-     *
-     * @param   string $currency
-     * @return  \Magento\Framework\Currency
+     * @inheritdoc
      */
     public function getCurrency($currency)
     {
diff --git a/lib/internal/Magento/Framework/Locale/CurrencyInterface.php b/lib/internal/Magento/Framework/Locale/CurrencyInterface.php
index 774f7d5120f3cead8f11e9d7545928e62634faa2..00bbc4452eedd604d19cb9144d1f38e9417e9568 100644
--- a/lib/internal/Magento/Framework/Locale/CurrencyInterface.php
+++ b/lib/internal/Magento/Framework/Locale/CurrencyInterface.php
@@ -5,27 +5,22 @@
  */
 namespace Magento\Framework\Locale;
 
+/**
+ * Provides access to currency config information
+ *
+ * @api
+ */
 interface CurrencyInterface
 {
     /**
-     * Default currency
-     */
-    const DEFAULT_CURRENCY = 'USD';
-
-    /**
-     * XML path to installed currencies
-     */
-    const XML_PATH_ALLOW_CURRENCIES_INSTALLED = 'system/currency/installed';
-
-    /**
-     * Retrieve currency code
+     * Retrieve default currency code
      *
      * @return string
      */
     public function getDefaultCurrency();
 
     /**
-     * Create \Magento\Framework\Currency object for current locale
+     * Create Currency object for current locale
      *
      * @param   string $currency
      * @return  \Magento\Framework\Currency
diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php
index b78be470920ec0638dd4a8a786d13b97470277f3..6a81f60cc5a0cda5d7d55a31f7f9b599e905c94a 100644
--- a/lib/internal/Magento/Framework/Locale/Resolver.php
+++ b/lib/internal/Magento/Framework/Locale/Resolver.php
@@ -9,6 +9,10 @@ use Magento\Framework\App\Config\ScopeConfigInterface;
 
 class Resolver implements ResolverInterface
 {
+    /**
+     * Default locale
+     */
+    const DEFAULT_LOCALE = 'en_US';
     /**
      * Default locale code
      *
@@ -85,7 +89,7 @@ class Resolver implements ResolverInterface
         if (!$this->defaultLocale) {
             $locale = $this->scopeConfig->getValue($this->getDefaultLocalePath(), $this->scopeType);
             if (!$locale) {
-                $locale = ResolverInterface::DEFAULT_LOCALE;
+                $locale = self::DEFAULT_LOCALE;
             }
             $this->defaultLocale = $locale;
         }
diff --git a/lib/internal/Magento/Framework/Locale/ResolverInterface.php b/lib/internal/Magento/Framework/Locale/ResolverInterface.php
index 08add444266f073aaa354935b6857be2c7edd34f..a17932ed8628870f7ce9f086946807498c836807 100644
--- a/lib/internal/Magento/Framework/Locale/ResolverInterface.php
+++ b/lib/internal/Magento/Framework/Locale/ResolverInterface.php
@@ -5,13 +5,13 @@
  */
 namespace Magento\Framework\Locale;
 
+/**
+ * Manages locale config information
+ *
+ * @api
+ */
 interface ResolverInterface
 {
-    /**
-     * Default locale
-     */
-    const DEFAULT_LOCALE = 'en_US';
-
     /**
      * Return path to default locale
      *
diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php
index 01552d3a972294bd649141074cc91f985829743b..226e8d4d8508be64e2a9a9454aeaa94cbb3de243 100644
--- a/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php
+++ b/lib/internal/Magento/Framework/Locale/Test/Unit/CurrencyTest.php
@@ -8,6 +8,7 @@
 
 namespace Magento\Framework\Locale\Test\Unit;
 
+use Magento\Framework\Locale\Currency;
 use Magento\Framework\Locale\CurrencyInterface;
 
 class CurrencyTest extends \PHPUnit_Framework_TestCase
@@ -68,7 +69,7 @@ class CurrencyTest extends \PHPUnit_Framework_TestCase
 
     public function testGetDefaultCurrency()
     {
-        $expectedDefaultCurrency = CurrencyInterface::DEFAULT_CURRENCY;
+        $expectedDefaultCurrency = Currency::DEFAULT_CURRENCY;
         $retrievedDefaultCurrency = $this->testCurrencyObject->getDefaultCurrency();
         $this->assertEquals($expectedDefaultCurrency, $retrievedDefaultCurrency);
     }
diff --git a/lib/internal/Magento/Framework/Locale/Validator.php b/lib/internal/Magento/Framework/Locale/Validator.php
index b30727562c45755db8b67dfb00be40c18bae09fc..0cdf265af01b329f3a87eca97ac8de8c723902b6 100644
--- a/lib/internal/Magento/Framework/Locale/Validator.php
+++ b/lib/internal/Magento/Framework/Locale/Validator.php
@@ -29,10 +29,12 @@ class Validator
     }
 
     /**
-     * Validate locale code
+     * Validate locale code. Code must be in the list of allowed locales.
      *
      * @param string $localeCode
      * @return bool
+     *
+     * @api
      */
     public function isValid($localeCode)
     {
diff --git a/lib/internal/Magento/Framework/Message/Factory.php b/lib/internal/Magento/Framework/Message/Factory.php
index 4c16064b5b6599dbb98f48307601dfff7eda1ee3..be7e3c9bb566a041c026a5916f4e51364e06b07d 100644
--- a/lib/internal/Magento/Framework/Message/Factory.php
+++ b/lib/internal/Magento/Framework/Message/Factory.php
@@ -45,11 +45,12 @@ class Factory
     }
 
     /**
-     * Create message instance with specified parameters
+     * Create a message instance of a given type with given text.
      *
-     * @param string $type
-     * @param string $text
-     * @throws \InvalidArgumentException
+     * @param string $type The message type to create, must correspond to a message type under the
+     * namespace Magento\Framework\Message\
+     * @param string $text The text to inject into the message
+     * @throws \InvalidArgumentException Exception gets thrown if type does not correspond to a valid Magento message
      * @return MessageInterface
      */
     public function create($type, $text)
diff --git a/lib/internal/Magento/Framework/Message/Manager.php b/lib/internal/Magento/Framework/Message/Manager.php
index 73b212a58e3ee88769bd653a815c15ecafde2578..815296c80bad2d8453564f10797ade913a2e5c7f 100644
--- a/lib/internal/Magento/Framework/Message/Manager.php
+++ b/lib/internal/Magento/Framework/Message/Manager.php
@@ -14,6 +14,10 @@ use Psr\Log\LoggerInterface as Logger;
  */
 class Manager implements ManagerInterface
 {
+    /**
+     * Default message group
+     */
+    const DEFAULT_GROUP = 'default';
     /**
      * @var Session
      */
@@ -74,9 +78,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Retrieve default message group
-     *
-     * @return string
+     * {@inheritdoc}
      */
     public function getDefaultGroup()
     {
@@ -95,7 +97,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Retrieve messages
+     * @inheritdoc
      *
      * @param string|null $group
      * @param bool $clear
@@ -118,7 +120,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Adding new message to message collection
+     * @inheritdoc
      *
      * @param MessageInterface $message
      * @param string|null $group
@@ -133,7 +135,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Adding messages array to message collection
+     * @inheritdoc
      *
      * @param MessageInterface[] $messages
      * @param string|null $group
@@ -148,7 +150,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Adding new error message
+     * @inheritdoc
      *
      * @param string $message
      * @param string|null $group
@@ -161,7 +163,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Adding new warning message
+     * @inheritdoc
      *
      * @param string $message
      * @param string|null $group
@@ -174,7 +176,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Adding new notice message
+     * @inheritdoc
      *
      * @param string $message
      * @param string|null $group
@@ -187,7 +189,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Adding new success message
+     * @inheritdoc
      *
      * @param string $message
      * @param string|null $group
@@ -200,7 +202,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Adds messages array to message collection, but doesn't add duplicates to it
+     * @inheritdoc
      *
      * @param MessageInterface[]|MessageInterface $messages
      * @param string|null $group
@@ -244,7 +246,7 @@ class Manager implements ManagerInterface
     }
 
     /**
-     * Not Magento exception handling
+     * @inheritdoc
      *
      * @param \Exception $exception
      * @param string $alternativeText
diff --git a/lib/internal/Magento/Framework/Message/ManagerInterface.php b/lib/internal/Magento/Framework/Message/ManagerInterface.php
index 36851dc866c49be7c9e0f0ea15490c79b776db4a..dd73d2a2fcfbc8acbd9aabdef42a3f5d59616f4d 100644
--- a/lib/internal/Magento/Framework/Message/ManagerInterface.php
+++ b/lib/internal/Magento/Framework/Message/ManagerInterface.php
@@ -6,15 +6,12 @@
 namespace Magento\Framework\Message;
 
 /**
- * Message manager interface
+ * Adds different types of messages to the session, and allows access to existing messages.
+ *
+ * @api
  */
 interface ManagerInterface
 {
-    /**
-     * Default message group
-     */
-    const DEFAULT_GROUP = 'default';
-
     /**
      * Retrieve messages
      *
@@ -32,7 +29,7 @@ interface ManagerInterface
     public function getDefaultGroup();
 
     /**
-     * Adding new message to message collection
+     * Adds new message to message collection
      *
      * @param MessageInterface $message
      * @param string|null $group
@@ -41,7 +38,7 @@ interface ManagerInterface
     public function addMessage(MessageInterface $message, $group = null);
 
     /**
-     * Adding messages array to message collection
+     * Adds messages array to message collection
      *
      * @param array $messages
      * @param string|null $group
@@ -50,7 +47,7 @@ interface ManagerInterface
     public function addMessages(array $messages, $group = null);
 
     /**
-     * Adding new error message
+     * Adds new error message
      *
      * @param string $message
      * @param string|null $group
@@ -59,7 +56,7 @@ interface ManagerInterface
     public function addError($message, $group = null);
 
     /**
-     * Adding new warning message
+     * Adds new warning message
      *
      * @param string $message
      * @param string|null $group
@@ -68,7 +65,7 @@ interface ManagerInterface
     public function addWarning($message, $group = null);
 
     /**
-     * Adding new notice message
+     * Adds new notice message
      *
      * @param string $message
      * @param string|null $group
@@ -77,7 +74,7 @@ interface ManagerInterface
     public function addNotice($message, $group = null);
 
     /**
-     * Adding new success message
+     * Adds new success message
      *
      * @param string $message
      * @param string|null $group
@@ -86,7 +83,7 @@ interface ManagerInterface
     public function addSuccess($message, $group = null);
 
     /**
-     * Adds messages array to message collection, but doesn't add duplicates to it
+     * Adds messages array to message collection, without adding duplicate messages
      *
      * @param array|MessageInterface $messages
      * @param string|null $group
@@ -95,7 +92,7 @@ interface ManagerInterface
     public function addUniqueMessages($messages, $group = null);
 
     /**
-     * Not Magento exception handling
+     * Adds a message describing an exception. Does not contain Exception handling logic.
      *
      * @param \Exception $exception
      * @param string $alternativeText
diff --git a/lib/internal/Magento/Framework/Message/MessageInterface.php b/lib/internal/Magento/Framework/Message/MessageInterface.php
index e5342f49ac042e79fb0b572fca328a1285ab8b22..d9d60e047b1713fd6a6288a86b2a7b74b8ddae0a 100644
--- a/lib/internal/Magento/Framework/Message/MessageInterface.php
+++ b/lib/internal/Magento/Framework/Message/MessageInterface.php
@@ -6,7 +6,9 @@
 namespace Magento\Framework\Message;
 
 /**
- * Interface for message
+ * Represent a message with a type, content text, and an isSticky attribute to prevent message from being cleared.
+ *
+ * @api
  */
 interface MessageInterface
 {
diff --git a/lib/internal/Magento/Framework/Message/Session.php b/lib/internal/Magento/Framework/Message/Session.php
index da86d172577a4f0ae18dce396deaf6688cd42d2e..8ceb66543e370ccf34321cef02be325bedb2cf85 100644
--- a/lib/internal/Magento/Framework/Message/Session.php
+++ b/lib/internal/Magento/Framework/Message/Session.php
@@ -8,6 +8,6 @@ namespace Magento\Framework\Message;
 /**
  * Message session model
  */
-class Session extends \Magento\Framework\Session\Generic
+class Session extends \Magento\Framework\Session\SessionManager
 {
 }
diff --git a/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php b/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php
index c4980974bc289f00167474eb50040197e020cb66..d65a5459a4ee92dae76a266743cb03d39c3220b6 100644
--- a/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php
+++ b/lib/internal/Magento/Framework/Message/Test/Unit/ManagerTest.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Framework\Message\Test\Unit;
 
+use Magento\Framework\Message\Manager;
 use Magento\Framework\Message\MessageInterface;
 use Magento\Framework\Message\ManagerInterface;
 
@@ -86,7 +87,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
 
     public function testGetDefaultGroup()
     {
-        $this->assertEquals(ManagerInterface::DEFAULT_GROUP, $this->model->getDefaultGroup());
+        $this->assertEquals(Manager::DEFAULT_GROUP, $this->model->getDefaultGroup());
 
         $customDefaultGroup = 'some_group';
         $customManager = $this->objectManager->getObject(
@@ -117,7 +118,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
         )->method(
             'getData'
         )->with(
-            ManagerInterface::DEFAULT_GROUP
+            Manager::DEFAULT_GROUP
         )->will(
             $this->returnValue(null)
         );
@@ -126,7 +127,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
         )->method(
             'setData'
         )->with(
-            ManagerInterface::DEFAULT_GROUP,
+            Manager::DEFAULT_GROUP,
             $messageCollection
         )->will(
             $this->returnValue($this->session)
@@ -136,7 +137,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
         )->method(
             'getData'
         )->with(
-            ManagerInterface::DEFAULT_GROUP
+            Manager::DEFAULT_GROUP
         )->will(
             $this->returnValue($messageCollection)
         );
@@ -161,7 +162,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
         )->method(
             'getData'
         )->with(
-            ManagerInterface::DEFAULT_GROUP
+            Manager::DEFAULT_GROUP
         )->will(
             $this->returnValue($messageCollection)
         );
@@ -209,7 +210,7 @@ class ManagerTest extends \PHPUnit_Framework_TestCase
         )->method(
             'getData'
         )->with(
-            ManagerInterface::DEFAULT_GROUP
+            Manager::DEFAULT_GROUP
         )->will(
             $this->returnValue($messageCollection)
         );
diff --git a/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php b/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php
index 831dcf5ae71fb2f832a7372efd8abdfe49a0ac40..c2a9bd81aad960e498303fae9f00a0f333a26272 100644
--- a/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php
+++ b/lib/internal/Magento/Framework/Model/Resource/AbstractResource.php
@@ -61,6 +61,7 @@ abstract class AbstractResource
      * Start resource transaction
      *
      * @return $this
+     * @api
      */
     public function beginTransaction()
     {
@@ -73,6 +74,7 @@ abstract class AbstractResource
      *
      * @param array $callback
      * @return $this
+     * @api
      */
     public function addCommitCallback($callback)
     {
@@ -85,6 +87,7 @@ abstract class AbstractResource
      * Commit resource transaction
      *
      * @return $this
+     * @api
      */
     public function commit()
     {
@@ -109,6 +112,7 @@ abstract class AbstractResource
      * Roll back resource transaction
      *
      * @return $this
+     * @api
      */
     public function rollBack()
     {
diff --git a/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php
index 351365b08d9eb57d45b6b65a50c53edbb93d482c..ba80e64a256222d7d02ac7eaae6cd95f7ec3717a 100644
--- a/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php
+++ b/lib/internal/Magento/Framework/Model/Resource/Db/AbstractDb.php
@@ -233,6 +233,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso
      *
      * @throws LocalizedException
      * @return string
+     * @api
      */
     public function getIdFieldName()
     {
@@ -248,6 +249,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso
      *
      * @throws LocalizedException
      * @return string
+     * @api
      */
     public function getMainTable()
     {
@@ -262,6 +264,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso
      *
      * @param string $tableName
      * @return string
+     * @api
      */
     public function getTable($tableName)
     {
@@ -391,6 +394,7 @@ abstract class AbstractDb extends \Magento\Framework\Model\Resource\AbstractReso
      * @param \Magento\Framework\Model\AbstractModel $object
      * @return $this
      * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @api
      */
     public function save(\Magento\Framework\Model\AbstractModel $object)
     {
diff --git a/lib/internal/Magento/Framework/Object/Copy.php b/lib/internal/Magento/Framework/Object/Copy.php
index 9145e0aba3bc9b9a106863967d2b7c5a3bd8b124..c0d87722872490ec10c4b15efb33fc3f2385fd33 100644
--- a/lib/internal/Magento/Framework/Object/Copy.php
+++ b/lib/internal/Magento/Framework/Object/Copy.php
@@ -47,6 +47,8 @@ class Copy
      * @param array|\Magento\Framework\Object $target
      * @param string $root
      * @return array|\Magento\Framework\Object|null the value of $target
+     *
+     * @api
      */
     public function copyFieldsetToTarget($fieldset, $aspect, $source, $target, $root = 'global')
     {
@@ -91,6 +93,8 @@ class Copy
      * @param array|\Magento\Framework\Object $source
      * @param string $root
      * @return array $data
+     *
+     * @api
      */
     public function getDataFromFieldset($fieldset, $aspect, $source, $root = 'global')
     {
diff --git a/lib/internal/Magento/Framework/Object/Copy/Config/Data/Proxy.php b/lib/internal/Magento/Framework/Object/Copy/Config/Data/Proxy.php
new file mode 100644
index 0000000000000000000000000000000000000000..ea974307333d3da54ea344c57d8b72331f30a44f
--- /dev/null
+++ b/lib/internal/Magento/Framework/Object/Copy/Config/Data/Proxy.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Object\Copy\Config\Data;
+
+/**
+ * Proxy class for @see \Magento\Framework\Object\Copy\Config\Data
+ */
+class Proxy extends \Magento\Framework\Object\Copy\Config\Data
+{
+    /**
+     * Object Manager instance
+     *
+     * @var \Magento\Framework\ObjectManagerInterface
+     */
+    protected $_objectManager = null;
+
+    /**
+     * Proxied instance name
+     *
+     * @var string
+     */
+    protected $_instanceName = null;
+
+    /**
+     * Proxied instance
+     *
+     * @var \Magento\Framework\Object\Copy\Config\Data
+     */
+    protected $_subject = null;
+
+    /**
+     * Instance shareability flag
+     *
+     * @var bool
+     */
+    protected $_isShared = null;
+
+    /**
+     * Proxy constructor
+     *
+     * @param \Magento\Framework\ObjectManagerInterface $objectManager
+     * @param string $instanceName
+     * @param bool $shared
+     */
+    public function __construct(
+        \Magento\Framework\ObjectManagerInterface $objectManager,
+        $instanceName = '\\Magento\\Framework\\Object\\Copy\\Config\\Data',
+        $shared = true
+    ) {
+        $this->_objectManager = $objectManager;
+        $this->_instanceName = $instanceName;
+        $this->_isShared = $shared;
+    }
+
+    /**
+     * @return array
+     */
+    public function __sleep()
+    {
+        return ['_subject', '_isShared'];
+    }
+
+    /**
+     * Retrieve ObjectManager from global scope
+     *
+     * @return void
+     */
+    public function __wakeup()
+    {
+        $this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    }
+
+    /**
+     * Clone proxied instance
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->_subject = clone $this->_getSubject();
+    }
+
+    /**
+     * Get proxied instance
+     *
+     * @return \Magento\Framework\Object\Copy\Config\Data
+     */
+    protected function _getSubject()
+    {
+        if (!$this->_subject) {
+            $this->_subject = true === $this->_isShared
+                ? $this->_objectManager->get($this->_instanceName)
+                : $this->_objectManager->create($this->_instanceName);
+        }
+        return $this->_subject;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function merge(array $config)
+    {
+        return $this->_getSubject()->merge($config);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($path = null, $default = null)
+    {
+        return $this->_getSubject()->get($path, $default);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function reset()
+    {
+        return $this->_getSubject()->reset();
+    }
+}
diff --git a/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php b/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php
index ad3864500bbc9502a1d8d6ecbaa296c76ac82d19..895377327281b908c2a8d0944a9415bbdb400275 100644
--- a/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php
+++ b/lib/internal/Magento/Framework/ObjectManager/Profiler/FactoryDecorator.php
@@ -8,6 +8,11 @@ namespace Magento\Framework\ObjectManager\Profiler;
 
 class FactoryDecorator implements \Magento\Framework\ObjectManager\FactoryInterface
 {
+    /**
+     * Name of the class that generates logging wrappers
+     */
+    const GENERATOR_NAME = 'Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger';
+
     /**
      * @var \Magento\Framework\ObjectManager\FactoryInterface
      */
@@ -45,9 +50,12 @@ class FactoryDecorator implements \Magento\Framework\ObjectManager\FactoryInterf
     {
         $this->log->startCreating($requestedType);
         $result = $this->subject->create($requestedType, $arguments);
-        $loggerClassName = get_class($result) . "\\Logger";
-        $wrappedResult = new $loggerClassName($result, $this->log);
-        $this->log->stopCreating($result);
-        return $wrappedResult;
+        if ($requestedType !== self::GENERATOR_NAME) {
+            $loggerClassName = get_class($result) . "\\Logger";
+            $wrappedResult = new $loggerClassName($result, $this->log);
+            $this->log->stopCreating($result);
+            $result = $wrappedResult;
+        }
+        return $result;
     }
 }
diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Profiler/FactoryDecoratorTest.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Profiler/FactoryDecoratorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1d0fa91e42286c72588946788f40491de72e925e
--- /dev/null
+++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/Profiler/FactoryDecoratorTest.php
@@ -0,0 +1,75 @@
+<?php
+/***
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\ObjectManager\Test\Unit\Profiler;
+
+class FactoryDecoratorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * Name of the base class to wrap in logger
+     */
+    const CLASS_NAME = 'Magento\Test\Di\WrappedClass';
+
+    /**
+     * Name of the wrapper class that does logging
+     */
+    const LOGGER_NAME = 'Magento\Test\Di\WrappedClass\Logger';
+
+    /**
+     * Name of the class that generates wrappers - should not be wrapped by logger
+     */
+    const GENERATOR_NAME = 'Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger';
+
+    /** @var  \PHPUnit_Framework_MockObject_MockObject | \Magento\Framework\ObjectManager\FactoryInterface*/
+    private $objectManagerMock;
+
+    /** @var  \Magento\Framework\ObjectManager\Profiler\FactoryDecorator */
+    private $model;
+
+    public function setUp()
+    {
+        require_once __DIR__ . '/../_files/logger_classes.php';
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+        $this->objectManagerMock = $this->getMockBuilder('Magento\Framework\ObjectManager\FactoryInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        // Instantiate SUT
+        $this->model = $objectManager->getObject(
+            'Magento\Framework\ObjectManager\Profiler\FactoryDecorator',
+            ['subject' => $this->objectManagerMock]
+        );
+    }
+
+    public function testCreate()
+    {
+        $baseObjectName = self::CLASS_NAME;
+        $baseObject = new $baseObjectName();
+
+        $arguments = [1, 2, 3];
+
+        $this->objectManagerMock->expects($this->once())
+            ->method('create')
+            ->with(self::CLASS_NAME, $arguments)
+            ->willReturn($baseObject);
+
+        $this->assertInstanceOf(self::LOGGER_NAME, $this->model->create(self::CLASS_NAME, $arguments));
+    }
+
+    public function testCreateNeglectGenerator()
+    {
+        $arguments = [1, 2, 3];
+        $loggerMock = $this->getMockBuilder(self::GENERATOR_NAME)->disableOriginalConstructor()->getMock();
+
+        $this->objectManagerMock->expects($this->once())
+            ->method('create')
+            ->with(self::GENERATOR_NAME, $arguments)
+            ->willReturn($loggerMock);
+
+        $this->assertSame($loggerMock, $this->model->create(self::GENERATOR_NAME, $arguments));
+    }
+}
diff --git a/lib/internal/Magento/Framework/ObjectManager/Test/Unit/_files/logger_classes.php b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/_files/logger_classes.php
new file mode 100644
index 0000000000000000000000000000000000000000..da593225f2079af53a730190a56afc0cb7715400
--- /dev/null
+++ b/lib/internal/Magento/Framework/ObjectManager/Test/Unit/_files/logger_classes.php
@@ -0,0 +1,26 @@
+<?php
+/***
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+//@codingStandardsIgnoreStart
+namespace Magento\Test\Di {
+
+    /**
+     * Test classes used for \Magento\Framework\ObjectManager\Test\Unit\Profiler\FactoryDecoratorTest
+     */
+    class WrappedClass
+    {
+
+    }
+}
+
+namespace Magento\Test\Di\WrappedClass {
+
+    class Logger
+    {
+
+    }
+}
+//@codingStandardsIgnoreEnd
diff --git a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php
index 9a0c8b2925b027ea2f72dfe536820fe2d3888181..980a8ff079c9b6f7c369099d801144ada04fdcee 100644
--- a/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php
+++ b/lib/internal/Magento/Framework/Pricing/PriceCurrencyInterface.php
@@ -89,4 +89,11 @@ interface PriceCurrencyInterface
      * @return \Magento\Framework\Model\AbstractModel
      */
     public function getCurrency($scope = null, $currency = null);
+
+    /**
+     * @param null|string|bool|int|\Magento\Framework\App\ScopeInterface $scope
+     * @param \Magento\Framework\Model\AbstractModel|string|null $currency
+     * @return string
+     */
+    public function getCurrencySymbol($scope = null, $currency = null);
 }
diff --git a/lib/internal/Magento/Framework/Pricing/Render/Amount.php b/lib/internal/Magento/Framework/Pricing/Render/Amount.php
index f58510aaf55fcbe87f23b80bcd733fcd614117de..a17c6d08af4c5bf66afd4e9832f5b237c9858ba7 100644
--- a/lib/internal/Magento/Framework/Pricing/Render/Amount.php
+++ b/lib/internal/Magento/Framework/Pricing/Render/Amount.php
@@ -142,6 +142,14 @@ class Amount extends Template implements AmountRenderInterface
         return $this->priceCurrency->getCurrency()->getCurrencyCode();
     }
 
+    /**
+     * @return string
+     */
+    public function getDisplayCurrencySymbol()
+    {
+        return $this->priceCurrency->getCurrencySymbol();
+    }
+
     /**
      * @return bool
      */
diff --git a/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php b/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php
index 66d0dc84a169876c2a7b025697b75fd93194eb4d..3779c69dd31ca6781a6a75b44f78c907518640df 100644
--- a/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php
+++ b/lib/internal/Magento/Framework/Pricing/Render/AmountRenderInterface.php
@@ -50,6 +50,11 @@ interface AmountRenderInterface
      */
     public function getDisplayCurrencyCode();
 
+    /**
+     * @return string
+     */
+    public function getDisplayCurrencySymbol();
+
     /**
      * @return string
      */
diff --git a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php
index 552cee4846656bd5fcdc6ce7d996bcadc0cb9535..fbf8d4f834f2fb7b1cc54af8ac75c6aad527c174 100644
--- a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php
+++ b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/AmountTest.php
@@ -127,6 +127,15 @@ class AmountTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($result, $this->model->formatCurrency($amount, $includeContainer, $precision));
     }
 
+    public function testGetDisplayCurrencySymbol()
+    {
+        $currencySymbol = '$';
+        $this->priceCurrency->expects($this->once())
+            ->method('getCurrencySymbol')
+            ->willReturn($currencySymbol);
+        $this->assertEquals($currencySymbol, $this->model->getDisplayCurrencySymbol());
+    }
+
     /**
      * Test case for getAdjustmentRenders method through toHtml()
      */
diff --git a/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php b/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php
index 8552cba5a5d2458014a60e259857fc5c01becf90..6824dca09e58c813d114dba3f3c720dad27a710d 100644
--- a/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php
+++ b/lib/internal/Magento/Framework/Reflection/AttributeTypeResolver.php
@@ -44,7 +44,7 @@ class AttributeTypeResolver implements AttributeTypeResolverInterface
         $config = isset($data[$context]) ? $data[$context] : [];
         $output = get_class($value);
         if (isset($config[$attributeCode])) {
-            $type = $config[$attributeCode];
+            $type = $config[$attributeCode]['type'];
             $output = $this->typeProcessor->getArrayItemType($type);
             if (!(class_exists($output) || interface_exists($output))) {
                 throw new \LogicException(
diff --git a/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a98c9e39e8c2f135b962bfbf0ef51d44924c183
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/CustomAttributesProcessor.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Reflection;
+
+use Magento\Framework\Phrase;
+use Magento\Framework\Api\AttributeInterface;
+use Magento\Framework\Api\AttributeValue;
+use Magento\Framework\Api\SimpleDataObjectConverter;
+use Zend\Code\Reflection\MethodReflection;
+use Magento\Framework\Api\CustomAttributesDataInterface;
+use Magento\Framework\Api\AttributeTypeResolverInterface;
+
+/**
+ * Processes custom attributes and produces an array for the data.
+ */
+class CustomAttributesProcessor
+{
+    /**
+     * @var DataObjectProcessor
+     */
+    private $dataObjectProcessor;
+
+    /**
+     * @var AttributeTypeResolverInterface
+     */
+    private $attributeTypeResolver;
+
+    /**
+     * @param DataObjectProcessor $dataObjectProcessor
+     * @param AttributeTypeResolverInterface $typeResolver
+     */
+    public function __construct(
+        DataObjectProcessor $dataObjectProcessor,
+        AttributeTypeResolverInterface $typeResolver
+    ) {
+        $this->dataObjectProcessor = $dataObjectProcessor;
+        $this->attributeTypeResolver = $typeResolver;
+    }
+
+    /**
+     * Writes out the custom attributes for a given object into an array.
+     *
+     * @param CustomAttributesDataInterface $objectWithCustomAttributes
+     * @param string $dataObjectType
+     * @return array
+     */
+    public function buildOutputDataArray(CustomAttributesDataInterface $objectWithCustomAttributes, $dataObjectType)
+    {
+        $customAttributes = $objectWithCustomAttributes->getCustomAttributes();
+        $result = [];
+        foreach ($customAttributes as $customAttribute) {
+            $result[] = $this->convertCustomAttribute($customAttribute, $dataObjectType);
+        }
+        return $result;
+    }
+
+    /**
+     * Convert custom_attribute object to use flat array structure
+     *
+     * @param AttributeInterface $customAttribute
+     * @param string $dataObjectType
+     * @return array
+     */
+    private function convertCustomAttribute(AttributeInterface $customAttribute, $dataObjectType)
+    {
+        $data = [];
+        $data[AttributeValue::ATTRIBUTE_CODE] = $customAttribute->getAttributeCode();
+        $value = $customAttribute->getValue();
+        if (is_object($value)) {
+            $type = $this->attributeTypeResolver->resolveObjectType(
+                $customAttribute->getAttributeCode(),
+                $value,
+                $dataObjectType
+            );
+            $value = $this->dataObjectProcessor->buildOutputDataArray($value, $type);
+        } elseif (is_array($value)) {
+            $valueResult = [];
+            foreach ($value as $singleValue) {
+                if (is_object($singleValue)) {
+                    $type = $this->attributeTypeResolver->resolveObjectType(
+                        $customAttribute->getAttributeCode(),
+                        $singleValue,
+                        $dataObjectType
+                    );
+                    $singleValue = $this->dataObjectProcessor->buildOutputDataArray($singleValue, $type);
+                }
+                // Cannot cast to a type because the type is unknown
+                $valueResult[] = $singleValue;
+            }
+            $value = $valueResult;
+        }
+        $data[AttributeValue::VALUE] = $value;
+        return $data;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php
index 2b9977d7355f565e808b15cc539c7c892f38afa6..4adceb83c7720be26a310fe8d7dea3f19726219c 100644
--- a/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php
+++ b/lib/internal/Magento/Framework/Reflection/DataObjectProcessor.php
@@ -14,54 +14,54 @@ use Zend\Code\Reflection\ClassReflection;
 use Zend\Code\Reflection\MethodReflection;
 
 /**
- * Data object processor for de-serialization using class reflection
+ * Data object processor for array serialization using class reflection
  */
 class DataObjectProcessor
 {
-    const IS_METHOD_PREFIX = 'is';
-    const HAS_METHOD_PREFIX = 'has';
-    const GETTER_PREFIX = 'get';
-    const SERVICE_INTERFACE_METHODS_CACHE_PREFIX = 'serviceInterfaceMethodsMap';
-    const BASE_MODEL_CLASS = 'Magento\Framework\Model\AbstractExtensibleModel';
-
     /**
-     * @var \Magento\Framework\Cache\FrontendInterface
+     * @var MethodsMap
      */
-    protected $cache;
+    private $methodsMapProcessor;
 
     /**
-     * @var TypeProcessor
+     * @var TypeCaster
      */
-    protected $typeProcessor;
+    private $typeCaster;
 
     /**
-     * @var array
+     * @var FieldNamer
      */
-    protected $dataInterfaceMethodsMap = [];
+    private $fieldNamer;
 
     /**
-     * @var array
+     * @var ExtensionAttributesProcessor
      */
-    protected $serviceInterfaceMethodsMap = [];
+    private $extensionAttributesProcessor;
 
     /**
-     * @var \Magento\Framework\Api\AttributeTypeResolverInterface
+     * @var CustomAttributesProcessor
      */
-    protected $attributeTypeResolver;
+    private $customAttributesProcessor;
 
     /**
-     * @param \Magento\Framework\Cache\FrontendInterface $cache
-     * @param TypeProcessor $typeProcessor
-     * @param \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver
+     * @param MethodsMap $methodsMapProcessor
+     * @param TypeCaster $typeCaster
+     * @param FieldNamer $fieldNamer
+     * @param CustomAttributesProcessor $customAttributesProcessor
+     * @param ExtensionAttributesProcessor $extensionAttributesProcessor
      */
     public function __construct(
-        \Magento\Framework\Cache\FrontendInterface $cache,
-        TypeProcessor $typeProcessor,
-        \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver
+        MethodsMap $methodsMapProcessor,
+        TypeCaster $typeCaster,
+        FieldNamer $fieldNamer,
+        CustomAttributesProcessor $customAttributesProcessor,
+        ExtensionAttributesProcessor $extensionAttributesProcessor
     ) {
-        $this->cache = $cache;
-        $this->typeProcessor = $typeProcessor;
-        $this->attributeTypeResolver = $typeResolver;
+        $this->methodsMapProcessor = $methodsMapProcessor;
+        $this->typeCaster = $typeCaster;
+        $this->fieldNamer = $fieldNamer;
+        $this->extensionAttributesProcessor = $extensionAttributesProcessor;
+        $this->customAttributesProcessor = $customAttributesProcessor;
     }
 
     /**
@@ -74,43 +74,36 @@ class DataObjectProcessor
      */
     public function buildOutputDataArray($dataObject, $dataObjectType)
     {
-        $methods = $this->getMethodsMap($dataObjectType);
+        $methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType);
         $outputData = [];
 
         /** @var MethodReflection $method */
-        foreach ($methods as $methodName => $methodReflectionData) {
-            // any method with parameter(s) gets ignored because we do not know the type and value of
-            // the parameter(s), so we are not able to process
-            if ($methodReflectionData['parameterCount'] > 0) {
+        foreach (array_keys($methods) as $methodName) {
+            if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) {
                 continue;
             }
-            $returnType = $methodReflectionData['type'];
-            if (substr($methodName, 0, 2) === self::IS_METHOD_PREFIX) {
-                $value = $dataObject->{$methodName}();
-                if ($value === null && !$methodReflectionData['isRequired']) {
-                    continue;
-                }
-                $key = SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 2));
-                $outputData[$key] = $this->castValueToType($value, $returnType);
-            } elseif (substr($methodName, 0, 3) === self::HAS_METHOD_PREFIX) {
-                $value = $dataObject->{$methodName}();
-                if ($value === null && !$methodReflectionData['isRequired']) {
-                    continue;
-                }
-                $key = SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3));
-                $outputData[$key] = $this->castValueToType($value, $returnType);
-            } elseif (substr($methodName, 0, 3) === self::GETTER_PREFIX) {
-                $value = $dataObject->{$methodName}();
-                if ($methodName === 'getCustomAttributes' && $value === []) {
-                    continue;
-                }
-                if ($value === null && !$methodReflectionData['isRequired']) {
-                    continue;
-                }
-                $key = SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3));
-                if ($key === CustomAttributesDataInterface::CUSTOM_ATTRIBUTES) {
-                    $value = $this->convertCustomAttributes($value, $dataObjectType);
-                } elseif (is_object($value) && !($value instanceof Phrase)) {
+
+            $value = $dataObject->{$methodName}();
+            $isMethodReturnValueRequired = $this->methodsMapProcessor->isMethodReturnValueRequired(
+                $dataObjectType,
+                $methodName
+            );
+            if ($value === null && !$isMethodReturnValueRequired) {
+                continue;
+            }
+
+            $returnType = $this->methodsMapProcessor->getMethodReturnType($dataObjectType, $methodName);
+            $key = $this->fieldNamer->getFieldNameForMethodName($methodName);
+            if ($key === CustomAttributesDataInterface::CUSTOM_ATTRIBUTES && $value === []) {
+                continue;
+            }
+
+            if ($key === CustomAttributesDataInterface::CUSTOM_ATTRIBUTES) {
+                $value = $this->customAttributesProcessor->buildOutputDataArray($dataObject, $dataObjectType);
+            } elseif ($key === "extension_attributes") {
+                $value = $this->extensionAttributesProcessor->buildOutputDataArray($value, $returnType);
+            } else {
+                if (is_object($value) && !($value instanceof Phrase)) {
                     $value = $this->buildOutputDataArray($value, $returnType);
                 } elseif (is_array($value)) {
                     $valueResult = [];
@@ -119,192 +112,16 @@ class DataObjectProcessor
                         if (is_object($singleValue) && !($singleValue instanceof Phrase)) {
                             $singleValue = $this->buildOutputDataArray($singleValue, $arrayElementType);
                         }
-                        $valueResult[] = $this->castValueToType($singleValue, $arrayElementType);
+                        $valueResult[] = $this->typeCaster->castValueToType($singleValue, $arrayElementType);
                     }
                     $value = $valueResult;
+                } else {
+                    $value = $this->typeCaster->castValueToType($value, $returnType);
                 }
-                $outputData[$key] = $this->castValueToType($value, $returnType);
             }
-        }
-        return $outputData;
-    }
 
-    /**
-     * Cast the output type to the documented type. This helps for output purposes.
-     *
-     * @param mixed $value
-     * @param string $type
-     * @return mixed
-     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
-     * @SuppressWarnings(PHPMD.NPathComplexity)
-     */
-    protected function castValueToType($value, $type)
-    {
-        if ($value === null) {
-            return null;
+            $outputData[$key] = $value;
         }
-
-        if ($type === "int" || $type === "integer") {
-            return (int)$value;
-        }
-
-        if ($type === "string") {
-            return (string)$value;
-        }
-
-        if ($type === "bool" || $type === "boolean" || $type === "true" || $type == "false") {
-            return (bool)$value;
-        }
-
-        if ($type === "float") {
-            return (float)$value;
-        }
-
-        if ($type === "double") {
-            return (double)$value;
-        }
-
-        return $value;
-    }
-
-    /**
-     * Get return type by interface name and method
-     *
-     * @param string $interfaceName
-     * @param string $methodName
-     * @return string
-     */
-    public function getMethodReturnType($interfaceName, $methodName)
-    {
-        return $this->getMethodsMap($interfaceName)[$methodName]['type'];
-    }
-
-    /**
-     * Convert array of custom_attributes to use flat array structure
-     *
-     * @param \Magento\Framework\Api\AttributeInterface[] $customAttributes
-     * @param string $dataObjectType
-     * @return array
-     */
-    protected function convertCustomAttributes($customAttributes, $dataObjectType)
-    {
-        $result = [];
-        foreach ((array)$customAttributes as $customAttribute) {
-            $result[] = $this->convertCustomAttribute($customAttribute, $dataObjectType);
-        }
-        return $result;
-    }
-
-    /**
-     * Convert custom_attribute object to use flat array structure
-     *
-     * @param \Magento\Framework\Api\AttributeInterface $customAttribute
-     * @param string $dataObjectType
-     * @return array
-     */
-    protected function convertCustomAttribute($customAttribute, $dataObjectType)
-    {
-        $data = [];
-        $data[AttributeValue::ATTRIBUTE_CODE] = $customAttribute->getAttributeCode();
-        $value = $customAttribute->getValue();
-        if (is_object($value)) {
-            $type = $this->attributeTypeResolver->resolveObjectType(
-                $customAttribute->getAttributeCode(),
-                $value,
-                $dataObjectType
-            );
-            $value = $this->buildOutputDataArray($value, $type);
-        } elseif (is_array($value)) {
-            $valueResult = [];
-            foreach ($value as $singleValue) {
-                if (is_object($singleValue)) {
-                    $type = $this->attributeTypeResolver->resolveObjectType(
-                        $customAttribute->getAttributeCode(),
-                        $singleValue,
-                        $dataObjectType
-                    );
-                    $singleValue = $this->buildOutputDataArray($singleValue, $type);
-                }
-                // Cannot cast to a type because the type is unknown
-                $valueResult[] = $singleValue;
-            }
-            $value = $valueResult;
-        }
-        $data[AttributeValue::VALUE] = $value;
-        return $data;
-    }
-
-    /**
-     * Return service interface or Data interface methods loaded from cache
-     *
-     * @param string $interfaceName
-     * @return array
-     * <pre>
-     * Service methods' reflection data stored in cache as 'methodName' => 'returnType'
-     * ex.
-     * [
-     *  'create' => '\Magento\Customer\Api\Data\Customer',
-     *  'validatePassword' => 'boolean'
-     * ]
-     * </pre>
-     */
-    public function getMethodsMap($interfaceName)
-    {
-        $key = self::SERVICE_INTERFACE_METHODS_CACHE_PREFIX . "-" . md5($interfaceName);
-        if (!isset($this->serviceInterfaceMethodsMap[$key])) {
-            $methodMap = $this->cache->load($key);
-            if ($methodMap) {
-                $this->serviceInterfaceMethodsMap[$key] = unserialize($methodMap);
-            } else {
-                $methodMap = $this->getMethodMapViaReflection($interfaceName);
-                $this->serviceInterfaceMethodsMap[$key] = $methodMap;
-                $this->cache->save(serialize($this->serviceInterfaceMethodsMap[$key]), $key);
-            }
-        }
-        return $this->serviceInterfaceMethodsMap[$key];
-    }
-
-    /**
-     * Use reflection to load the method information
-     *
-     * @param string $interfaceName
-     * @return array
-     */
-    protected function getMethodMapViaReflection($interfaceName)
-    {
-        $methodMap = [];
-        $class = new ClassReflection($interfaceName);
-        $baseClassMethods = false;
-        foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
-            // Include all the methods of classes inheriting from AbstractExtensibleObject.
-            // Ignore all the methods of AbstractExtensibleModel's parent classes
-            if ($method->class === self::BASE_MODEL_CLASS) {
-                $baseClassMethods = true;
-            } elseif ($baseClassMethods) {
-                // ReflectionClass::getMethods() sorts the methods by class (lowest in inheritance tree first)
-                // then by the order they are defined in the class definition
-                break;
-            }
-
-            if ($this->isSuitableMethod($method)) {
-                $methodMap[$method->getName()] = $this->typeProcessor->getGetterReturnType($method);
-            }
-        }
-        return $methodMap;
-    }
-
-    /**
-     * Determines if the method is suitable to be used by the processor.
-     *
-     * @param \ReflectionMethod $method
-     * @return bool
-     */
-    protected function isSuitableMethod($method)
-    {
-        $isSuitableMethodType = !($method->isConstructor() || $method->isFinal()
-            || $method->isStatic() || $method->isDestructor());
-
-        $isExcludedMagicMethod = strpos($method->getName(), '__') === 0;
-        return $isSuitableMethodType && !$isExcludedMagicMethod;
+        return $outputData;
     }
 }
diff --git a/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php
new file mode 100644
index 0000000000000000000000000000000000000000..c2d2d69c758f8b4676da91027080fb5736d5c880
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/ExtensionAttributesProcessor.php
@@ -0,0 +1,189 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Reflection;
+
+use Magento\Framework\Api\Config\Reader as ExtensionAttributesConfigReader;
+use Magento\Framework\Api\Config\Converter;
+use Magento\Framework\AuthorizationInterface;
+use Magento\Framework\Phrase;
+use Magento\Framework\Api\SimpleDataObjectConverter;
+use Magento\Framework\Api\ExtensionAttributesInterface;
+use Magento\Framework\Reflection\MethodsMap;
+use Zend\Code\Reflection\MethodReflection;
+
+/**
+ * Processes extension attributes and produces an array for the data.
+ */
+class ExtensionAttributesProcessor
+{
+    /**
+     * @var DataObjectProcessor
+     */
+    private $dataObjectProcessor;
+
+    /**
+     * @var MethodsMap
+     */
+    private $methodsMapProcessor;
+
+    /**
+     * @var AuthorizationInterface
+     */
+    private $authorization;
+
+    /**
+     * @var ExtensionAttributesConfigReader
+     */
+    private $configReader;
+
+    /**
+     * @var bool
+     */
+    private $isPermissionChecked;
+
+    /**
+     * @var FieldNamer
+     */
+    private $fieldNamer;
+
+    /**
+     * @var TypeCaster
+     */
+    private $typeCaster;
+
+    /**
+     * @param DataObjectProcessor $dataObjectProcessor
+     * @param MethodsMap $methodsMapProcessor
+     * @param TypeCaster $typeCaster
+     * @param FieldNamer $fieldNamer
+     * @param AuthorizationInterface $authorization
+     * @param ExtensionAttributesConfigReader $configReader
+     * @param bool $isPermissionChecked
+     */
+    public function __construct(
+        DataObjectProcessor $dataObjectProcessor,
+        MethodsMap $methodsMapProcessor,
+        TypeCaster $typeCaster,
+        FieldNamer $fieldNamer,
+        AuthorizationInterface $authorization,
+        ExtensionAttributesConfigReader $configReader,
+        $isPermissionChecked = false
+    ) {
+        $this->dataObjectProcessor = $dataObjectProcessor;
+        $this->methodsMapProcessor = $methodsMapProcessor;
+        $this->typeCaster = $typeCaster;
+        $this->fieldNamer = $fieldNamer;
+        $this->authorization = $authorization;
+        $this->configReader = $configReader;
+        $this->isPermissionChecked = $isPermissionChecked;
+    }
+
+    /**
+     * Writes out the extension attributes in an array.
+     *
+     * @param ExtensionAttributeInterface $dataObject
+     * @param string $dataObjectType
+     * @return array
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     */
+    public function buildOutputDataArray(ExtensionAttributesInterface $dataObject, $dataObjectType)
+    {
+        $methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType);
+        $outputData = [];
+
+        /** @var MethodReflection $method */
+        foreach (array_keys($methods) as $methodName) {
+            if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) {
+                continue;
+            }
+
+            $key = $this->fieldNamer->getFieldNameForMethodName($methodName);
+            if ($this->isPermissionChecked && !$this->isAttributePermissionValid($dataObjectType, $key)) {
+                continue;
+            }
+
+            $value = $dataObject->{$methodName}();
+            if ($value === null) {
+                // all extension attributes are optional so don't need to check if isRequired
+                continue;
+            }
+
+            $returnType = $this->methodsMapProcessor->getMethodReturnType($dataObjectType, $methodName);
+
+            if (is_object($value) && !($value instanceof Phrase)) {
+                $value = $this->dataObjectProcessor->buildOutputDataArray($value, $returnType);
+            } elseif (is_array($value)) {
+                $valueResult = [];
+                $arrayElementType = substr($returnType, 0, -2);
+                foreach ($value as $singleValue) {
+                    if (is_object($singleValue) && !($singleValue instanceof Phrase)) {
+                        $singleValue = $this->dataObjectProcessor->buildOutputDataArray(
+                            $singleValue,
+                            $arrayElementType
+                        );
+                    }
+                    $valueResult[] = $this->typeCaster->castValueToType($singleValue, $arrayElementType);
+                }
+                $value = $valueResult;
+            } else {
+                $value = $this->typeCaster->castValueToType($value, $returnType);
+            }
+
+            $outputData[$key] = $value;
+        }
+
+        return $outputData;
+    }
+
+    /**
+     * @param string $dataObjectType
+     * @param string $attributeCode
+     * @return bool
+     */
+    private function isAttributePermissionValid($dataObjectType, $attributeCode)
+    {
+        $typeName = $this->getRegularTypeForExtensionAttributesType($dataObjectType);
+        $permissions = $this->getPermissionsForTypeAndMethod($typeName, $attributeCode);
+        foreach ($permissions as $permission) {
+            if (!$this->authorization->isAllowed($permission)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * @param string $name
+     * @return string
+     */
+    private function getRegularTypeForExtensionAttributesType($name)
+    {
+        return ltrim(str_replace('ExtensionInterface', 'Interface', $name), '\\');
+    }
+
+    /**
+     * @param string $typeName
+     * @param string $attributeCode
+     * @return string[] A list of permissions
+     */
+    private function getPermissionsForTypeAndMethod($typeName, $attributeCode)
+    {
+        // TODO: Move function to the Config and hope this is cached
+        $attributes = $this->configReader->read();
+        if (isset($attributes[$typeName]) && isset($attributes[$typeName][$attributeCode])) {
+            $attributeMetadata = $attributes[$typeName][$attributeCode];
+            $permissions = [];
+            foreach ($attributeMetadata[Converter::RESOURCE_PERMISSIONS] as $permission) {
+                $permissions[] = $permission;
+            }
+            return $permissions;
+        }
+
+        return [];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/FieldNamer.php b/lib/internal/Magento/Framework/Reflection/FieldNamer.php
new file mode 100644
index 0000000000000000000000000000000000000000..2e105b9a4f666e2073124e8817a9208762740389
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/FieldNamer.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Reflection;
+
+use Magento\Framework\Phrase;
+use Magento\Framework\Api\AttributeValue;
+use Magento\Framework\Api\CustomAttributesDataInterface;
+use Magento\Framework\Api\SimpleDataObjectConverter;
+use Zend\Code\Reflection\ClassReflection;
+use Zend\Code\Reflection\MethodReflection;
+
+/**
+ * Determines the name to use for fields in a data output array given method metadata.
+ */
+class FieldNamer
+{
+    const IS_METHOD_PREFIX = 'is';
+    const HAS_METHOD_PREFIX = 'has';
+    const GETTER_PREFIX = 'get';
+    
+    /**
+     * Converts a method's name into a data field name.
+     *
+     * @param string $methodName
+     * @return string|null
+     */
+    public function getFieldNameForMethodName($methodName)
+    {
+        if (substr($methodName, 0, 2) === self::IS_METHOD_PREFIX) {
+            return SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 2));
+        } elseif (substr($methodName, 0, 3) === self::HAS_METHOD_PREFIX) {
+            return SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3));
+        } elseif (substr($methodName, 0, 3) === self::GETTER_PREFIX) {
+            return SimpleDataObjectConverter::camelCaseToSnakeCase(substr($methodName, 3));
+        }
+
+        return null;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/MethodsMap.php b/lib/internal/Magento/Framework/Reflection/MethodsMap.php
new file mode 100644
index 0000000000000000000000000000000000000000..36b446d1b870a32d6f468bf77b293a63b024fabe
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/MethodsMap.php
@@ -0,0 +1,180 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Reflection;
+
+use Zend\Code\Reflection\ClassReflection;
+use Zend\Code\Reflection\MethodReflection;
+
+/**
+ * Gathers method metadata information.
+ */
+class MethodsMap
+{
+    const SERVICE_INTERFACE_METHODS_CACHE_PREFIX = 'serviceInterfaceMethodsMap';
+    const BASE_MODEL_CLASS = 'Magento\Framework\Model\AbstractExtensibleModel';
+
+    /**
+     * @var \Magento\Framework\Cache\FrontendInterface
+     */
+    private $cache;
+
+    /**
+     * @var TypeProcessor
+     */
+    private $typeProcessor;
+
+    /**
+     * @var array
+     */
+    private $serviceInterfaceMethodsMap = [];
+
+    /**
+     * @var FieldNamer
+     */
+    private $fieldNamer;
+
+    /**
+     * @param \Magento\Framework\Cache\FrontendInterface $cache
+     * @param TypeProcessor $typeProcessor
+     * @param \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver
+     * @param FieldNamer $fieldNamer
+     */
+    public function __construct(
+        \Magento\Framework\Cache\FrontendInterface $cache,
+        TypeProcessor $typeProcessor,
+        \Magento\Framework\Api\AttributeTypeResolverInterface $typeResolver,
+        FieldNamer $fieldNamer
+    ) {
+        $this->cache = $cache;
+        $this->typeProcessor = $typeProcessor;
+        $this->attributeTypeResolver = $typeResolver;
+        $this->fieldNamer = $fieldNamer;
+    }
+
+    /**
+     * Get return type by type name and method name.
+     *
+     * @param string $typeName
+     * @param string $methodName
+     * @return string
+     */
+    public function getMethodReturnType($typeName, $methodName)
+    {
+        return $this->getMethodsMap($typeName)[$methodName]['type'];
+    }
+
+    /**
+     * Return service interface or Data interface methods loaded from cache
+     *
+     * @param string $interfaceName
+     * @return array
+     * <pre>
+     * Service methods' reflection data stored in cache as 'methodName' => 'returnType'
+     * ex.
+     * [
+     *  'create' => '\Magento\Customer\Api\Data\Customer',
+     *  'validatePassword' => 'boolean'
+     * ]
+     * </pre>
+     */
+    public function getMethodsMap($interfaceName)
+    {
+        $key = self::SERVICE_INTERFACE_METHODS_CACHE_PREFIX . "-" . md5($interfaceName);
+        if (!isset($this->serviceInterfaceMethodsMap[$key])) {
+            $methodMap = $this->cache->load($key);
+            if ($methodMap) {
+                $this->serviceInterfaceMethodsMap[$key] = unserialize($methodMap);
+            } else {
+                $methodMap = $this->getMethodMapViaReflection($interfaceName);
+                $this->serviceInterfaceMethodsMap[$key] = $methodMap;
+                $this->cache->save(serialize($this->serviceInterfaceMethodsMap[$key]), $key);
+            }
+        }
+        return $this->serviceInterfaceMethodsMap[$key];
+    }
+
+    /**
+     * Use reflection to load the method information
+     *
+     * @param string $interfaceName
+     * @return array
+     */
+    private function getMethodMapViaReflection($interfaceName)
+    {
+        $methodMap = [];
+        $class = new ClassReflection($interfaceName);
+        $baseClassMethods = false;
+        foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
+            // Include all the methods of classes inheriting from AbstractExtensibleObject.
+            // Ignore all the methods of AbstractExtensibleModel's parent classes
+            if ($method->class === self::BASE_MODEL_CLASS) {
+                $baseClassMethods = true;
+            } elseif ($baseClassMethods) {
+                // ReflectionClass::getMethods() sorts the methods by class (lowest in inheritance tree first)
+                // then by the order they are defined in the class definition
+                break;
+            }
+
+            if ($this->isSuitableMethod($method)) {
+                $methodMap[$method->getName()] = $this->typeProcessor->getGetterReturnType($method);
+            }
+        }
+        return $methodMap;
+    }
+
+    /**
+     * Determines if the method is suitable to be used by the processor.
+     *
+     * @param \ReflectionMethod $method
+     * @return bool
+     */
+    private function isSuitableMethod($method)
+    {
+        $isSuitableMethodType = !($method->isConstructor() || $method->isFinal()
+            || $method->isStatic() || $method->isDestructor());
+
+        $isExcludedMagicMethod = strpos($method->getName(), '__') === 0;
+        return $isSuitableMethodType && !$isExcludedMagicMethod;
+    }
+
+    /**
+     * Determines if the given method's on the given type is suitable for an output data array.
+     *
+     * @param string $type
+     * @param string $methodName
+     * @return bool
+     */
+    public function isMethodValidForDataField($type, $methodName)
+    {
+        $methods = $this->getMethodsMap($type);
+        if (isset($methods[$methodName])) {
+            $methodMetadata = $methods[$methodName];
+            // any method with parameter(s) gets ignored because we do not know the type and value of
+            // the parameter(s), so we are not able to process
+            if ($methodMetadata['parameterCount'] > 0) {
+                return false;
+            }
+
+            return $this->fieldNamer->getFieldNameForMethodName($methodName) !== null;
+        }
+
+        return false;
+    }
+
+    /**
+     * If the method has only non-null return types
+     *
+     * @param string $type
+     * @param string $methodName
+     * @return bool
+     */
+    public function isMethodReturnValueRequired($type, $methodName)
+    {
+        $methods = $this->getMethodsMap($type);
+        return $methods[$methodName]['isRequired'];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php
index 8391d4643482d17da3c717dbbc732b7c43002b19..68902f3851380d368c88ca8e2357bada00a2de7d 100644
--- a/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php
+++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/AttributeTypeResolverTest.php
@@ -62,7 +62,13 @@ class AttributeTypeResolverTest extends \PHPUnit_Framework_TestCase
         $code = 'some_code';
         $value = new \stdClass();
         $context = '\Some\Class';
-        $config = ['Some\Class' => ['some_code' => '\Magento\Framework\Object']];
+        $config = [
+            'Some\Class' => [
+                'some_code' => [
+                    'type' => '\Magento\Framework\Object',
+                ],
+            ]
+        ];
 
         $this->typeProcessor->expects($this->once())
             ->method('getArrayItemType')
@@ -82,7 +88,13 @@ class AttributeTypeResolverTest extends \PHPUnit_Framework_TestCase
         $code = 'some_code';
         $value = new \stdClass();
         $context = '\Some\Class';
-        $config = ['Some\Class' => ['some_code' => '\Some\Class']];
+        $config = [
+            'Some\Class' => [
+                'some_code' => [
+                    'type' => '\Some\Class',
+                ]
+            ]
+        ];
 
         $this->typeProcessor->expects($this->once())
             ->method('getArrayItemType')
diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesObject.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesObject.php
new file mode 100644
index 0000000000000000000000000000000000000000..66077712a43b337238402748fe9ccf9c4b0db5f2
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesObject.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Reflection\Test\Unit;
+
+use Magento\Framework\Api\ExtensionAttributesInterface;
+
+/**
+ * Dummy data object to be used by ExtensionAttributesProcessorTest
+ */
+class ExtensionAttributesObject implements ExtensionAttributesInterface
+{
+    /**
+     * @return string
+     */
+    public function getAttrName()
+    {
+        return 'attrName';
+    }
+
+    /**
+     * @return bool
+     */
+    public function isActive()
+    {
+        return false;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesProcessorTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesProcessorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8da0c1f734f710da1c69d28c9e9f4ac02999db86
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/ExtensionAttributesProcessorTest.php
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Reflection\Test\Unit;
+
+use Magento\Framework\Api\Config\Converter;
+use Magento\Framework\Api\Config\Reader;
+use Magento\Framework\AuthorizationInterface;
+use Magento\Framework\Reflection\DataObjectProcessor;
+use Magento\Framework\Reflection\ExtensionAttributesProcessor;
+use Magento\Framework\Reflection\FieldNamer;
+use Magento\Framework\Reflection\MethodsMap;
+use Magento\Framework\Reflection\TypeCaster;
+
+/**
+ * ExtensionAttributesProcessor test
+ */
+class ExtensionAttributesProcessorTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var ExtensionAttributesProcessor
+     */
+    private $model;
+
+    /**
+     * @var DataObjectProcessor
+     */
+    private $dataObjectProcessorMock;
+
+    /**
+     * @var MethodsMap
+     */
+    private $methodsMapProcessorMock;
+
+    /**
+     * @var FieldNamer
+     */
+    private $fieldNamerMock;
+
+    /**
+     * @var TypeCaster
+     */
+    private $typeCasterMock;
+
+    /**
+     * @var Reader
+     */
+    private $configReaderMock;
+
+    /**
+     * @var AuthorizationInterface
+     */
+    private $authorizationMock;
+    /**
+     * Set up helper.
+     */
+    public function setUp()
+    {
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+        $this->dataObjectProcessorMock = $this->getMockBuilder('Magento\Framework\Reflection\DataObjectProcessor')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->methodsMapProcessorMock = $this->getMockBuilder('Magento\Framework\Reflection\MethodsMap')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->typeCasterMock = $this->getMockBuilder('Magento\Framework\Reflection\TypeCaster')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->fieldNamerMock = $this->getMockBuilder('Magento\Framework\Reflection\FieldNamer')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->configReaderMock = $this->getMockBuilder('Magento\Framework\Api\Config\Reader')
+            ->disableOriginalConstructor()
+            ->getMock();
+        $this->authorizationMock = $this->getMockBuilder('Magento\Framework\AuthorizationInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->model = $objectManager->getObject(
+            'Magento\Framework\Reflection\ExtensionAttributesProcessor',
+            [
+                'dataObjectProcessor' => $this->dataObjectProcessorMock,
+                'methodsMapProcessor' => $this->methodsMapProcessorMock,
+                'typeCaster' => $this->typeCasterMock,
+                'fieldNamer' => $this->fieldNamerMock,
+                'authorization' => $this->authorizationMock,
+                'configReader' => $this->configReaderMock,
+                'isPermissionChecked' => true,
+            ]
+        );
+    }
+
+    /**
+     * @param bool $isPermissionAllowed
+     * @param array $expectedValue
+     * @dataProvider buildOutputDataArrayWithPermissionProvider
+     */
+    public function testBuildOutputDataArrayWithPermission($isPermissionAllowed, $expectedValue)
+    {
+        $dataObject = new \Magento\Framework\Reflection\Test\Unit\ExtensionAttributesObject();
+        $dataObjectType = 'Magento\Framework\Reflection\Test\Unit\ExtensionAttributesObject';
+        $methodName = 'getAttrName';
+        $attributeName = 'attr_name';
+        $attributeValue = 'attrName';
+
+        $this->methodsMapProcessorMock->expects($this->once())
+            ->method('getMethodsMap')
+            ->with($dataObjectType)
+            ->will($this->returnValue([$methodName => []]));
+        $this->methodsMapProcessorMock->expects($this->once())
+            ->method('isMethodValidForDataField')
+            ->with($dataObjectType, $methodName)
+            ->will($this->returnValue(true));
+        $this->fieldNamerMock->expects($this->once())
+            ->method('getFieldNameForMethodName')
+            ->with($methodName)
+            ->will($this->returnValue($attributeName));
+        $permissionName = 'Magento_Permission';
+        $this->configReaderMock->expects($this->once())
+            ->method('read')
+            ->will($this->returnValue([
+                $dataObjectType => [
+                    $attributeName => [ Converter::RESOURCE_PERMISSIONS => [ $permissionName ] ]
+                ]
+              ]));
+        $this->authorizationMock->expects($this->once())
+            ->method('isAllowed')
+            ->with($permissionName)
+            ->will($this->returnValue($isPermissionAllowed));
+
+        if ($isPermissionAllowed) {
+            $this->methodsMapProcessorMock->expects($this->once())
+                ->method('getMethodReturnType')
+                ->with($dataObjectType, $methodName)
+                ->will($this->returnValue('string'));
+            $this->typeCasterMock->expects($this->once())
+                ->method('castValueToType')
+                ->with($attributeValue, 'string')
+                ->will($this->returnValue($attributeValue));
+        }
+
+        $value = $this->model->buildOutputDataArray(
+            $dataObject,
+            $dataObjectType
+        );
+
+        $this->assertEquals(
+            $value,
+            $expectedValue
+        );
+    }
+
+    public function buildOutputDataArrayWithPermissionProvider()
+    {
+        return [
+            'permission allowed' => [
+                true,
+                [
+                    'attr_name' => 'attrName',
+                ],
+            ],
+            'permission not allowed' => [
+                false,
+                [],
+            ],
+        ];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/FieldNamerTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/FieldNamerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1d498a940ec1a437ccc546e3629df37767748eeb
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/FieldNamerTest.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Reflection\Test\Unit;
+
+use Magento\Framework\Reflection\FieldNamer;
+
+/**
+ * Field namer Test
+ */
+class FieldNamerTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var FieldNamer
+     */
+    private $model;
+
+    /**
+     * Set up helper.
+     */
+    public function setUp()
+    {
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject('Magento\Framework\Reflection\FieldNamer');
+    }
+
+    /**
+     * @param string $methodName
+     * @param string $expectedName
+     * @dataProvider methodNameProvider
+     */
+    public function testGetFieldNameForMethodName($methodName, $expectedName)
+    {
+        $value = $this->model->getFieldNameForMethodName($methodName);
+        $this->assertEquals($value, $expectedName);
+    }
+
+    /**
+     * @return array
+     */
+    public function methodNameProvider()
+    {
+        return [
+            'isMethod' => ['isValid', 'valid'],
+            'getMethod' => ['getValue', 'value'],
+            'hasMethod' => ['hasStuff', 'stuff'],
+            'randomMethod' => ['randomMethod', null],
+        ];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/MethodsMapTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/MethodsMapTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..82f859897da9bfdcdd8f26de0ef7d3201712f699
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/MethodsMapTest.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Reflection\Test\Unit;
+
+use Magento\Framework\Reflection\MethodsMap;
+use Magento\Framework\Reflection\TypeProcessor;
+use Magento\Framework\Reflection\FieldNamer;
+
+/**
+ * MethodsMap test
+ */
+class MethodsMapTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var MethodsMap
+     */
+    private $model;
+
+    /**
+     * Set up helper.
+     */
+    public function setUp()
+    {
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+        $cacheMock = $this->getMockBuilder('Magento\Framework\Cache\FrontendInterface')
+            ->getMockForAbstractClass();
+        $cacheMock->expects($this->any())
+            ->method('save');
+        $cacheMock->expects($this->any())
+            ->method('load')
+            ->will($this->returnValue(null));
+
+        $attributeTypeResolverMock = $this->getMockBuilder('Magento\Framework\Api\AttributeTypeResolverInterface')
+            ->getMockForAbstractClass();
+        $fieldNamerMock = $this->getMockBuilder('Magento\Framework\Reflection\FieldNamer')
+            ->getMockForAbstractClass();
+        $this->model = $objectManager->getObject(
+            'Magento\Framework\Reflection\MethodsMap',
+            [
+                'cache' => $cacheMock,
+                'typeProcessor' => new TypeProcessor(),
+                'typeResolver' => $attributeTypeResolverMock,
+                'fieldNamer' => $fieldNamerMock,
+            ]
+        );
+    }
+
+    public function testGetMethodReturnType()
+    {
+        $this->assertEquals(
+            'string',
+            $this->model->getMethodReturnType('Magento\Framework\Reflection\FieldNamer', 'getFieldNameForMethodName')
+        );
+        $this->assertEquals(
+            'mixed',
+            $this->model->getMethodReturnType('Magento\Framework\Reflection\TypeCaster', 'castValueToType')
+        );
+        $this->assertEquals(
+            'array',
+            $this->model->getMethodReturnType('Magento\Framework\Reflection\MethodsMap', 'getMethodsMap')
+        );
+    }
+
+    public function testGetMethodsMap()
+    {
+        $methodsMap = $this->model->getMethodsMap('Magento\Framework\Reflection\MethodsMap');
+        $this->assertEquals(
+            $methodsMap,
+            [
+                'getMethodReturnType' => [
+                    'type' => 'string',
+                    'isRequired' => true,
+                    'description' => null,
+                    'parameterCount' => 2,
+                ],
+                'getMethodsMap' => [
+                    'type' => 'array',
+                    'isRequired' => true,
+                    'description' => "<pre> Service methods' reflection data stored in cache as 'methodName' => "
+                        . "'returnType' ex. [ 'create' => '\Magento\Customer\Api\Data\Customer', 'validatePassword' "
+                        . "=> 'boolean' ] </pre>",
+                    'parameterCount' => 1,
+                ],
+                'isMethodValidForDataField' => [
+                    'type' => 'bool',
+                    'isRequired' => true,
+                    'description' => null,
+                    'parameterCount' => 2,
+                ],
+                'isMethodReturnValueRequired' => [
+                    'type' => 'bool',
+                    'isRequired' => true,
+                    'description' => null,
+                    'parameterCount' => 2,
+                ],
+            ]
+        );
+    }
+
+    /**
+     * @param string $type
+     * @param string $methodName
+     * @param bool $expectedResult
+     * @dataProvider isMethodValidForDataFieldProvider
+     */
+    public function testIsMethodValidForDataField($type, $methodName, $expectedResult)
+    {
+        $this->assertEquals($this->model->isMethodValidForDataField($type, $methodName), $expectedResult);
+    }
+
+    /**
+     * @return array
+     */
+    public function isMethodValidForDataFieldProvider()
+    {
+        return [
+            'MethodsMap#isMethodValidForDataField' => [
+                'Magento\Framework\Reflection\MethodsMap',
+                'isMethodValidForDataField',
+                false,
+            ],
+            'DataObject#getAttrName' => [
+                'Magento\Framework\Reflection\Test\Unit\DataObject',
+                'getAttrName',
+                true,
+            ],
+            'DataObject#isActive' => [
+                'Magento\Framework\Reflection\Test\Unit\DataObject',
+                'isActive',
+                true,
+            ],
+        ];
+    }
+
+    /**
+     * @param string $type
+     * @param string $methodName
+     * @param bool $expectedResult
+     * @dataProvider isMethodReturnValueRequiredProvider
+     */
+    public function testIsMethodReturnValueRequired($type, $methodName, $expectedResult)
+    {
+        $this->assertEquals($this->model->isMethodValidForDataField($type, $methodName), $expectedResult);
+    }
+
+    /**
+     * @return array
+     */
+    public function isMethodReturnValueRequiredProvider()
+    {
+        return [
+            'DataObject#getAttrName' => [
+                'Magento\Framework\Reflection\Test\Unit\DataObject',
+                'getAttrName',
+                true,
+            ],
+            'DataObject#isActive' => [
+                'Magento\Framework\Reflection\Test\Unit\DataObject',
+                'isActive',
+                true,
+            ],
+            'FieldNamer#getFieldNameForMethodName' => [
+                'Magento\Framework\Reflection\FieldNamer',
+                'getFieldNameForMethodName',
+                false,
+            ],
+        ];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeCasterTest.php b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeCasterTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8f7afdbbedcb05e103f92c6c982d5a0686cf7481
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/Test/Unit/TypeCasterTest.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Reflection\Test\Unit;
+
+use Magento\Framework\Reflection\TypeCaster;
+
+/**
+ * Type caster Test
+ */
+class TypeCasterTest extends \PHPUnit_Framework_TestCase
+{
+    /**
+     * @var TypeCaster
+     */
+    private $model;
+
+    /**
+     * Set up helper.
+     */
+    public function setUp()
+    {
+        $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->model = $objectManager->getObject('Magento\Framework\Reflection\TypeCaster');
+    }
+
+    /**
+     * @param mixed $origValue
+     * @param string $typeToCast
+     * @param mixed $expectedValue
+     * @dataProvider typeCastValueProvider
+     */
+    public function testCastValues($origValue, $typeToCast, $expectedValue)
+    {
+        $value = $this->model->castValueToType($origValue, $typeToCast);
+        $this->assertTrue($value === $expectedValue);
+    }
+
+    /**
+     * @return array
+     */
+    public function typeCastValueProvider()
+    {
+        return [
+            'null' => [null, 'int', null],
+            'int' => ['1', 'int', 1],
+            'integer' => ['1', 'integer', 1],
+            'string' => ['1', 'string', '1'],
+            'bool 0' => ['0', 'bool', false],
+            'bool 1' => ['1', 'bool', true],
+            'boolean 0' => ['0', 'boolean', false],
+            'boolean 1' => ['1', 'boolean', true],
+            'true' => ['1', 'true', true],
+            'false' => ['0', 'false', false],
+            'float' => ['1.03', 'float', 1.03],
+            'double' => ['1.30', 'double', 1.30],
+        ];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Reflection/TypeCaster.php b/lib/internal/Magento/Framework/Reflection/TypeCaster.php
new file mode 100644
index 0000000000000000000000000000000000000000..185b86c1d880b14061f3271da5de4885bf56cf29
--- /dev/null
+++ b/lib/internal/Magento/Framework/Reflection/TypeCaster.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+
+namespace Magento\Framework\Reflection;
+
+/**
+ * Casts values to the type given.
+ */
+class TypeCaster
+{
+    /**
+     * Cast the output type to the documented type. This helps for consistent output (e.g. JSON).
+     *
+     * @param mixed $value
+     * @param string $type
+     * @return mixed
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+     * @SuppressWarnings(PHPMD.NPathComplexity)
+     */
+    public function castValueToType($value, $type)
+    {
+        if ($value === null) {
+            return null;
+        }
+
+        if ($type === "int" || $type === "integer") {
+            return (int)$value;
+        }
+
+        if ($type === "string") {
+            return (string)$value;
+        }
+
+        if ($type === "bool" || $type === "boolean" || $type === "true" || $type == "false") {
+            return (bool)$value;
+        }
+
+        if ($type === "float") {
+            return (float)$value;
+        }
+
+        if ($type === "double") {
+            return (double)$value;
+        }
+
+        return $value;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php b/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php
index 1cfc1477a7957b46fe1f58f752749b3d8a4193a2..1b534b4dbf4991c3c5f71f2b8009bd6efd9f20df 100644
--- a/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php
+++ b/lib/internal/Magento/Framework/Search/Dynamic/DataProviderFactory.php
@@ -33,7 +33,7 @@ class DataProviderFactory
         ScopeConfigInterface $scopeConfig,
         $configPath,
         $dataProviders,
-        $scope = ScopeInterface::SCOPE_DEFAULT
+        $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT
     ) {
         $this->objectManager = $objectManager;
         $configValue = $scopeConfig->getValue($configPath, $scope);
diff --git a/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php b/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php
index bba9824e19083f82aa5c249338bf16d2d4da5575..80ac66bb04e62950ae6a7d4dedc4e0fd8dd148d4 100644
--- a/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php
+++ b/lib/internal/Magento/Framework/Search/Dynamic/IntervalFactory.php
@@ -33,7 +33,7 @@ class IntervalFactory
         ScopeConfigInterface $scopeConfig,
         $configPath,
         $intervals,
-        $scope = ScopeInterface::SCOPE_DEFAULT
+        $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT
     ) {
         $this->objectManager = $objectManager;
         $configValue = $scopeConfig->getValue($configPath, $scope);
diff --git a/lib/internal/Magento/Framework/Search/Request/Mapper.php b/lib/internal/Magento/Framework/Search/Request/Mapper.php
index e2dcbef74cda6ab201ed6bdd7dc6b3dcda916cd7..d69d5e4e6d6689170096767166c1cc44fba09d6d 100644
--- a/lib/internal/Magento/Framework/Search/Request/Mapper.php
+++ b/lib/internal/Magento/Framework/Search/Request/Mapper.php
@@ -14,6 +14,11 @@ use Magento\Framework\Phrase;
  */
 class Mapper
 {
+    /**
+     * @var QueryInterface
+     */
+    private $rootQuery;
+
     /**
      * @var array
      */
@@ -45,9 +50,9 @@ class Mapper
     private $objectManager;
 
     /**
-     * @var QueryInterface
+     * @var string
      */
-    private $rootQuery = null;
+    private $rootQueryName;
 
     /**
      * @param \Magento\Framework\ObjectManagerInterface $objectManager
@@ -70,26 +75,26 @@ class Mapper
         $this->queries = $queries;
         $this->aggregations = $aggregations;
         $this->filters = $filters;
-
-        $this->rootQuery = $this->get($rootQueryName);
+        $this->rootQueryName = $rootQueryName;
     }
 
     /**
      * Get Query Interface by name
      *
-     * @param string $queryName
      * @return QueryInterface
      * @throws \Exception
      * @throws \InvalidArgumentException
      * @throws StateException
      */
-    private function get($queryName)
+    public function getRootQuery()
     {
-        $this->mappedQueries = [];
-        $this->mappedFilters = [];
-        $query = $this->mapQuery($queryName);
-        $this->validate();
-        return $query;
+        if (!$this->rootQuery) {
+            $this->mappedQueries = [];
+            $this->mappedFilters = [];
+            $this->rootQuery = $this->mapQuery($this->rootQueryName);
+            $this->validate();
+        }
+        return $this->rootQuery;
     }
 
     /**
@@ -304,14 +309,6 @@ class Mapper
         $this->validateNotUsed($this->filters, $this->mappedFilters, 'Filter %1 is not used in request hierarchy');
     }
 
-    /**
-     * @return QueryInterface
-     */
-    public function getRootQuery()
-    {
-        return $this->rootQuery;
-    }
-
     /**
      * Build BucketInterface[] from array
      *
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php
index 3b37a0bf3d574e10044e2bdea2f8c4b2c2eccebb..5b0736c5fa2ef890b1362bae14c6d9b70f38bf97 100644
--- a/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/Adapter/Mysql/DimensionsTest.php
@@ -6,6 +6,7 @@
 
 namespace Magento\Framework\Search\Test\Unit\Adapter\Mysql;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use \Magento\Framework\Search\Adapter\Mysql\Dimensions;
 
 use Magento\Framework\Search\Adapter\Mysql\Dimensions as DimensionsBuilder;
@@ -103,7 +104,7 @@ class DimensionsTest extends \PHPUnit_Framework_TestCase
     {
         $tableAlias = 'search_index';
         $name = 'scope';
-        $value = \Magento\Framework\App\ScopeInterface::SCOPE_DEFAULT;
+        $value = ScopeConfigInterface::SCOPE_TYPE_DEFAULT;
         $scopeId = -123456;
 
         $this->dimension->expects($this->once())
diff --git a/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php b/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php
index 0d7b52e00630adde68df4600dd353d1d7e314207..232f789de3ae77014a26ee1e251dbcce6235d0e5 100644
--- a/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php
+++ b/lib/internal/Magento/Framework/Search/Test/Unit/Dynamic/IntervalFactoryTest.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Framework\Search\Test\Unit\Dynamic;
 
+use Magento\Framework\App\Config\ScopeConfigInterface;
 use Magento\Framework\Search\Dynamic\IntervalInterface;
 use Magento\Framework\App\ScopeInterface;
 use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
@@ -63,7 +64,7 @@ class IntervalFactoryTest extends \PHPUnit_Framework_TestCase
     {
         $this->scopeConfig->expects($this->once())
             ->method('getValue')
-            ->with(self::CONFIG_PATH, ScopeInterface::SCOPE_DEFAULT)
+            ->with(self::CONFIG_PATH, ScopeConfigInterface::SCOPE_TYPE_DEFAULT)
             ->willReturn(self::CONFIG_PATH . 't');
         $this->objectManager->expects($this->once())
             ->method('create')
@@ -83,7 +84,7 @@ class IntervalFactoryTest extends \PHPUnit_Framework_TestCase
     {
         $this->scopeConfig->expects($this->once())
             ->method('getValue')
-            ->with(self::CONFIG_PATH, ScopeInterface::SCOPE_DEFAULT)
+            ->with(self::CONFIG_PATH, ScopeConfigInterface::SCOPE_TYPE_DEFAULT)
             ->willReturn('t');
 
         $this->factoryCreate();
@@ -97,7 +98,7 @@ class IntervalFactoryTest extends \PHPUnit_Framework_TestCase
     {
         $this->scopeConfig->expects($this->once())
             ->method('getValue')
-            ->with(self::CONFIG_PATH, ScopeInterface::SCOPE_DEFAULT)
+            ->with(self::CONFIG_PATH, ScopeConfigInterface::SCOPE_TYPE_DEFAULT)
             ->willReturn(self::CONFIG_PATH . 't');
         $this->objectManager->expects($this->once())
             ->method('create')
diff --git a/lib/internal/Magento/Framework/Session/Generic.php b/lib/internal/Magento/Framework/Session/Generic.php
index dd7da87c933a3fff8be209f77e93b77c5eebea1a..bfaf48d74ee9d7f4851e181e8e69f06437da8993 100644
--- a/lib/internal/Magento/Framework/Session/Generic.php
+++ b/lib/internal/Magento/Framework/Session/Generic.php
@@ -7,38 +7,4 @@ namespace Magento\Framework\Session;
 
 class Generic extends SessionManager
 {
-    /**
-     * Constructor
-     *
-     * @param \Magento\Framework\App\Request\Http $request
-     * @param SidResolverInterface $sidResolver
-     * @param \Magento\Framework\Session\Config\ConfigInterface $sessionConfig
-     * @param SaveHandlerInterface $saveHandler
-     * @param ValidatorInterface $validator
-     * @param StorageInterface $storage
-     * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
-     * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
-     */
-    public function __construct(
-        \Magento\Framework\App\Request\Http $request,
-        SidResolverInterface $sidResolver,
-        \Magento\Framework\Session\Config\ConfigInterface $sessionConfig,
-        SaveHandlerInterface $saveHandler,
-        ValidatorInterface $validator,
-        StorageInterface $storage,
-        \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
-        \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
-    ) {
-        parent::__construct(
-            $request,
-            $sidResolver,
-            $sessionConfig,
-            $saveHandler,
-            $validator,
-            $storage,
-            $cookieManager,
-            $cookieMetadataFactory
-        );
-        $this->start();
-    }
 }
diff --git a/lib/internal/Magento/Framework/Session/SessionManager.php b/lib/internal/Magento/Framework/Session/SessionManager.php
index 17636fa0776804b433129011dd25b2edaec33275..c66672e87ca8f6b7098a3ecf17a0df582dad6072 100644
--- a/lib/internal/Magento/Framework/Session/SessionManager.php
+++ b/lib/internal/Magento/Framework/Session/SessionManager.php
@@ -11,6 +11,7 @@ use Magento\Framework\Session\Config\ConfigInterface;
 
 /**
  * Session Manager
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class SessionManager implements SessionManagerInterface
 {
@@ -87,8 +88,11 @@ class SessionManager implements SessionManagerInterface
     protected $cookieMetadataFactory;
 
     /**
-     * Constructor
-     *
+     * @var \Magento\Framework\App\State
+     */
+    private $appState;
+
+    /**
      * @param \Magento\Framework\App\Request\Http $request
      * @param SidResolverInterface $sidResolver
      * @param ConfigInterface $sessionConfig
@@ -97,6 +101,8 @@ class SessionManager implements SessionManagerInterface
      * @param StorageInterface $storage
      * @param \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager
      * @param \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
+     * @param \Magento\Framework\App\State $appState
+     * @throws \Magento\Framework\Exception\SessionException
      */
     public function __construct(
         \Magento\Framework\App\Request\Http $request,
@@ -106,7 +112,8 @@ class SessionManager implements SessionManagerInterface
         ValidatorInterface $validator,
         StorageInterface $storage,
         \Magento\Framework\Stdlib\CookieManagerInterface $cookieManager,
-        \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory
+        \Magento\Framework\Stdlib\Cookie\CookieMetadataFactory $cookieMetadataFactory,
+        \Magento\Framework\App\State $appState
     ) {
         $this->request = $request;
         $this->sidResolver = $sidResolver;
@@ -116,9 +123,11 @@ class SessionManager implements SessionManagerInterface
         $this->storage = $storage;
         $this->cookieManager = $cookieManager;
         $this->cookieMetadataFactory = $cookieMetadataFactory;
+        $this->appState = $appState;
 
         // Enable session.use_only_cookies
         ini_set('session.use_only_cookies', '1');
+        $this->start();
     }
 
     /**
@@ -152,12 +161,25 @@ class SessionManager implements SessionManagerInterface
     /**
      * Configure session handler and start session
      *
+     * @throws \Magento\Framework\Exception\SessionException
      * @return $this
      */
     public function start()
     {
         if (!$this->isSessionExists()) {
             \Magento\Framework\Profiler::start('session_start');
+
+            try {
+                $this->appState->getAreaCode();
+            } catch (\Magento\Framework\Exception\LocalizedException $e) {
+                throw new \Magento\Framework\Exception\SessionException(
+                    new \Magento\Framework\Phrase(
+                        'Area code not set: Area code must be set before starting a session.'
+                    ),
+                    $e
+                );
+            }
+
             // Need to apply the config options so they can be ready by session_start
             $this->initIniOptions();
             $this->registerSaveHandler();
diff --git a/lib/internal/Magento/Framework/Shell.php b/lib/internal/Magento/Framework/Shell.php
index 0652d49d039e0c43d21375d3d87153db78b1e243..2372ba4088571c89d7865d4a9d2b7523fdd6e1eb 100644
--- a/lib/internal/Magento/Framework/Shell.php
+++ b/lib/internal/Magento/Framework/Shell.php
@@ -49,7 +49,7 @@ class Shell implements ShellInterface
         $command = $this->commandRenderer->render($command, $arguments);
         $this->log($command);
 
-        $disabled = explode(',', ini_get('disable_functions'));
+        $disabled = explode(',', str_replace(' ', ',', ini_get('disable_functions')));
         if (in_array('exec', $disabled)) {
             throw new Exception\LocalizedException(new \Magento\Framework\Phrase("exec function is disabled."));
         }
diff --git a/lib/internal/Magento/Framework/Simplexml/Config.php b/lib/internal/Magento/Framework/Simplexml/Config.php
index 4e1265ebd4a4c180d7bbc4a8b84b0334b5b1676a..2f45cd6550b87a9417f2e50355866765c26d2a2b 100644
--- a/lib/internal/Magento/Framework/Simplexml/Config.php
+++ b/lib/internal/Magento/Framework/Simplexml/Config.php
@@ -118,12 +118,12 @@ class Config
      */
     public function getNode($path = null)
     {
-        if (!$this->_xml instanceof Element) {
+        if (!$this->getXml() instanceof Element) {
             return false;
         } elseif ($path === null) {
-            return $this->_xml;
+            return $this->getXml();
         } else {
-            return $this->_xml->descend($path);
+            return $this->getXml()->descend($path);
         }
     }
 
@@ -135,11 +135,12 @@ class Config
      */
     public function getXpath($xpath)
     {
-        if (empty($this->_xml)) {
+        $xml = $this->getXml();
+        if (empty($xml)) {
             return false;
         }
 
-        if (!($result = @$this->_xml->xpath($xpath))) {
+        if (!($result = @$xml->xpath($xpath))) {
             return false;
         }
 
@@ -476,7 +477,7 @@ class Config
         if (!empty($string)) {
             $xml = simplexml_load_string($string, $this->_elementClass);
             if ($xml) {
-                $this->_xml = $xml;
+                $this->setXml($xml);
                 return true;
             }
         }
@@ -493,7 +494,7 @@ class Config
     {
         $xml = simplexml_import_dom($dom, $this->_elementClass);
         if ($xml) {
-            $this->_xml = $xml;
+            $this->setXml($xml);
             return true;
         }
 
@@ -510,7 +511,7 @@ class Config
      */
     public function setNode($path, $value, $overwrite = true)
     {
-        $this->_xml->setNode($path, $value, $overwrite);
+        $this->getXml()->setNode($path, $value, $overwrite);
         return $this;
     }
 
@@ -575,4 +576,14 @@ class Config
     {
         $this->_xml = null;
     }
+
+    /**
+     * Getter for xml element
+     *
+     * @return Element
+     */
+    protected function getXml()
+    {
+        return $this->_xml;
+    }
 }
diff --git a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
index 46d9a13f0bf0a5290fd6086d78aab72a9bf502ca..69e2260002ce05ff621e5b63baf613c7e30a879b 100644
--- a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
+++ b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
@@ -73,9 +73,10 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * @param bool $resolve
      * @return \Magento\Framework\Url\RouteParamsResolverFactory|\PHPUnit_Framework_MockObject_MockObject
      */
-    protected function getRouteParamsResolver()
+    protected function getRouteParamsResolverFactory($resolve = true)
     {
         $routeParamsResolverFactoryMock = $this->getMock(
             'Magento\Framework\Url\RouteParamsResolverFactory',
@@ -84,8 +85,10 @@ class UrlTest extends \PHPUnit_Framework_TestCase
             '',
             false
         );
-        $routeParamsResolverFactoryMock->expects($this->once())->method('create')
-            ->will($this->returnValue($this->routeParamsResolverMock));
+        if ($resolve) {
+            $routeParamsResolverFactoryMock->expects($this->once())->method('create')
+                    ->will($this->returnValue($this->routeParamsResolverMock));
+        }
         return $routeParamsResolverFactoryMock;
     }
 
@@ -152,7 +155,10 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     public function testGetBaseUrlNotLinkType()
     {
         $model = $this->getUrlModel(
-            ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver()]
+            [
+                'scopeResolver' => $this->scopeResolverMock,
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory()
+            ]
         );
 
         $baseUrl = 'base-url';
@@ -184,9 +190,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase
         $requestMock = $this->getRequestMock();
         $routeConfigMock = $this->getMock('Magento\Framework\App\Route\ConfigInterface');
         $model = $this->getUrlModel(
-            ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(),
+            [
+                'scopeResolver' => $this->scopeResolverMock,
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
                 'queryParamsResolver' => $this->queryParamsResolverMock,
-                'request' => $requestMock, 'routeConfig' => $routeConfigMock, ]
+                'request' => $requestMock, 'routeConfig' => $routeConfigMock,
+            ]
         );
 
         $baseUrl = 'http://localhost/index.php/';
@@ -218,7 +227,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     {
         $model = $this->getUrlModel([
             'scopeResolver' => $this->scopeResolverMock,
-            'routeParamsResolver' => $this->getRouteParamsResolver(),
+            'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
         ]);
         $model->setData('route_path', 'catalog/product/view');
 
@@ -233,7 +242,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     {
         $model = $this->getUrlModel([
             'scopeResolver' => $this->scopeResolverMock,
-            'routeParamsResolver' => $this->getRouteParamsResolver(),
+            'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
             'request' => $this->getRequestMock()
         ]);
         $model->setData('route_name', 'catalog');
@@ -251,7 +260,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
             ->will($this->returnValue(['foo' => 'bar', 'true' => false]));
         $model = $this->getUrlModel([
             'scopeResolver' => $this->scopeResolverMock,
-            'routeParamsResolver' => $this->getRouteParamsResolver(),
+            'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
             'request' => $this->getRequestMock()
         ]);
 
@@ -270,7 +279,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
         $request->expects($this->once())->method('getAlias')->will($this->returnValue('/catalog/product/view/'));
         $model = $this->getUrlModel([
             'scopeResolver' => $this->scopeResolverMock,
-            'routeParamsResolver' => $this->getRouteParamsResolver(),
+            'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
             'request' => $request,
         ]);
 
@@ -299,9 +308,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase
         $requestMock = $this->getRequestMock();
         $routeConfigMock = $this->getMock('Magento\Framework\App\Route\ConfigInterface');
         $model = $this->getUrlModel(
-            ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(),
+            [
+                'scopeResolver' => $this->scopeResolverMock,
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
                 'queryParamsResolver' => $this->queryParamsResolverMock,
-                'request' => $requestMock, 'routeConfig' => $routeConfigMock, ]
+                'request' => $requestMock, 'routeConfig' => $routeConfigMock,
+            ]
         );
 
         $baseUrl = 'http://localhost/index.php/';
@@ -332,9 +344,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase
         $requestMock = $this->getRequestMock();
         $routeConfigMock = $this->getMock('Magento\Framework\App\Route\ConfigInterface');
         $model = $this->getUrlModel(
-            ['scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(),
+            [
+                'scopeResolver' => $this->scopeResolverMock,
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
                 'queryParamsResolver' => $this->queryParamsResolverMock,
-                'request' => $requestMock, 'routeConfig' => $routeConfigMock, ]
+                'request' => $requestMock, 'routeConfig' => $routeConfigMock,
+            ]
         );
 
         $baseUrl = 'http://localhost/index.php/';
@@ -363,7 +378,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
             'request' => $requestMock,
             'sidResolver' => $this->sidResolverMock,
             'scopeResolver' => $this->scopeResolverMock,
-            'routeParamsResolver' => $this->getRouteParamsResolver(),
+            'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(false),
             'queryParamsResolver' => $this->queryParamsResolverMock,
         ]);
 
@@ -376,8 +391,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     public function testGetRedirectUrl()
     {
         $model = $this->getUrlModel(
-            ['routeParamsResolver' => $this->getRouteParamsResolver(), 'session' => $this->sessionMock,
-                'sidResolver' => $this->sidResolverMock, 'queryParamsResolver' => $this->queryParamsResolverMock, ]
+            [
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
+                'session' => $this->sessionMock,
+                'sidResolver' => $this->sidResolverMock,
+                'queryParamsResolver' => $this->queryParamsResolverMock,
+            ]
         );
 
         $this->sidResolverMock->expects($this->once())->method('getUseSessionInUrl')->will($this->returnValue(true));
@@ -396,8 +415,12 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     public function testGetRedirectUrlWithSessionId()
     {
         $model = $this->getUrlModel(
-            ['routeParamsResolver' => $this->getRouteParamsResolver(), 'session' => $this->sessionMock,
-                'sidResolver' => $this->sidResolverMock, 'queryParamsResolver' => $this->queryParamsResolverMock, ]
+            [
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(false),
+                'session' => $this->sessionMock,
+                'sidResolver' => $this->sidResolverMock,
+                'queryParamsResolver' => $this->queryParamsResolverMock,
+            ]
         );
 
         $this->sidResolverMock->expects($this->once())->method('getUseSessionInUrl')->will($this->returnValue(true));
@@ -422,7 +445,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
 
     public function testGetRouteUrlWithValidUrl()
     {
-        $model = $this->getUrlModel(['routeParamsResolver' => $this->getRouteParamsResolver()]);
+        $model = $this->getUrlModel(['routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(false)]);
 
         $this->routeParamsResolverMock->expects($this->never())->method('unsetData');
         $this->assertEquals('http://example.com', $model->getRouteUrl('http://example.com'));
@@ -485,7 +508,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
         $urlSecurityInfoMock = $this->getMock('Magento\Framework\Url\SecurityInfoInterface');
         $model = $this->getUrlModel([
             'urlSecurityInfo' => $urlSecurityInfoMock,
-            'routeParamsResolver' => $this->getRouteParamsResolver(),
+            'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
             'scopeResolver' => $this->scopeResolverMock,
             'scopeConfig' => $this->scopeConfig,
         ]);
@@ -530,7 +553,7 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     public function testGetConfigDataWithSecureIsForcedParam()
     {
         $model = $this->getUrlModel([
-            'routeParamsResolver' => $this->getRouteParamsResolver(),
+            'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
             'scopeResolver' => $this->scopeResolverMock,
             'scopeConfig' => $this->scopeConfig,
         ]);
@@ -562,8 +585,13 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     {
         $requestMock = $this->getRequestMock();
         $model = $this->getUrlModel(
-            ['session' => $this->sessionMock, 'request' => $requestMock, 'sidResolver' => $this->sidResolverMock,
-                'scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(), ]
+            [
+                'session' => $this->sessionMock,
+                'request' => $requestMock,
+                'sidResolver' => $this->sidResolverMock,
+                'scopeResolver' => $this->scopeResolverMock,
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
+            ]
         );
 
         $requestMock->expects($this->once())
@@ -585,8 +613,13 @@ class UrlTest extends \PHPUnit_Framework_TestCase
     {
         $requestMock = $this->getRequestMock();
         $model = $this->getUrlModel(
-            ['session' => $this->sessionMock, 'request' => $requestMock, 'sidResolver' => $this->sidResolverMock,
-                'scopeResolver' => $this->scopeResolverMock, 'routeParamsResolver' => $this->getRouteParamsResolver(), ]
+            [
+                'session' => $this->sessionMock,
+                'request' => $requestMock,
+                'sidResolver' => $this->sidResolverMock,
+                'scopeResolver' => $this->scopeResolverMock,
+                'routeParamsResolverFactory' => $this->getRouteParamsResolverFactory(),
+            ]
         );
 
         $requestMock->expects($this->once())->method('getHttpHost')->will($this->returnValue('localhost'));
diff --git a/lib/internal/Magento/Framework/Url.php b/lib/internal/Magento/Framework/Url.php
index efffc744e3fe46da126f655f16a95c9f1c5bd4a7..697e461df050f1f623075655acd3ea4fc13ccaa9 100644
--- a/lib/internal/Magento/Framework/Url.php
+++ b/lib/internal/Magento/Framework/Url.php
@@ -56,6 +56,7 @@ namespace Magento\Framework;
  * - G: route_path
  * - H: route_url
  * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  */
 class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInterface
 {
@@ -133,8 +134,12 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
     /**
      * @var \Magento\Framework\Url\RouteParamsResolverInterface
      */
-    protected $_routeParamsResolver;
+    private $_routeParamsResolver;
 
+    /**
+     * @var \Magento\Framework\Url\RouteParamsResolverFactory
+     */
+    private $_routeParamsResolverFactory;
     /**
      * @var \Magento\Framework\Url\ScopeResolverInterface
      */
@@ -157,7 +162,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
      * @param \Magento\Framework\Url\ScopeResolverInterface $scopeResolver
      * @param \Magento\Framework\Session\Generic $session
      * @param \Magento\Framework\Session\SidResolverInterface $sidResolver
-     * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver
+     * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory
      * @param \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver
      * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
      * @param string $scopeType
@@ -171,7 +176,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
         \Magento\Framework\Url\ScopeResolverInterface $scopeResolver,
         \Magento\Framework\Session\Generic $session,
         \Magento\Framework\Session\SidResolverInterface $sidResolver,
-        \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolver,
+        \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory,
         \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver,
         \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
         $scopeType,
@@ -183,7 +188,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
         $this->_scopeResolver = $scopeResolver;
         $this->_session = $session;
         $this->_sidResolver = $sidResolver;
-        $this->_routeParamsResolver = $routeParamsResolver->create();
+        $this->_routeParamsResolverFactory = $routeParamsResolverFactory;
         $this->_queryParamsResolver = $queryParamsResolver;
         $this->_scopeConfig = $scopeConfig;
         $this->_scopeType = $scopeType;
@@ -322,10 +327,10 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
      */
     protected function _getType()
     {
-        if (!$this->_routeParamsResolver->hasData('type')) {
-            $this->_routeParamsResolver->setData('type', self::DEFAULT_URL_TYPE);
+        if (!$this->getRouteParamsResolver()->hasData('type')) {
+            $this->getRouteParamsResolver()->setData('type', self::DEFAULT_URL_TYPE);
         }
-        return $this->_routeParamsResolver->getType();
+        return $this->getRouteParamsResolver()->getType();
     }
 
     /**
@@ -335,27 +340,27 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
      */
     protected function _isSecure()
     {
-        if ($this->_routeParamsResolver->hasData('secure_is_forced')) {
-            return (bool) $this->_routeParamsResolver->getData('secure');
+        if ($this->getRouteParamsResolver()->hasData('secure_is_forced')) {
+            return (bool) $this->getRouteParamsResolver()->getData('secure');
         }
 
         if (!$this->_getScope()->isUrlSecure()) {
             return false;
         }
 
-        if (!$this->_routeParamsResolver->hasData('secure')) {
+        if (!$this->getRouteParamsResolver()->hasData('secure')) {
             if ($this->_getType() == UrlInterface::URL_TYPE_LINK) {
                 $pathSecure = $this->_urlSecurityInfo->isSecure('/' . $this->_getActionPath());
-                $this->_routeParamsResolver->setData('secure', $pathSecure);
+                $this->getRouteParamsResolver()->setData('secure', $pathSecure);
             } elseif ($this->_getType() == UrlInterface::URL_TYPE_STATIC) {
                 $isRequestSecure = $this->_getRequest()->isSecure();
-                $this->_routeParamsResolver->setData('secure', $isRequestSecure);
+                $this->getRouteParamsResolver()->setData('secure', $isRequestSecure);
             } else {
-                $this->_routeParamsResolver->setData('secure', true);
+                $this->getRouteParamsResolver()->setData('secure', true);
             }
         }
 
-        return $this->_routeParamsResolver->getData('secure');
+        return $this->getRouteParamsResolver()->getData('secure');
     }
 
     /**
@@ -367,7 +372,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
     public function setScope($params)
     {
         $this->setData('scope', $this->_scopeResolver->getScope($params));
-        $this->_routeParamsResolver->setScope($this->_scopeResolver->getScope($params));
+        $this->getRouteParamsResolver()->setScope($this->_scopeResolver->getScope($params));
         return $this;
     }
 
@@ -401,11 +406,11 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
             $this->setScope($params['_scope']);
         }
         if (isset($params['_type'])) {
-            $this->_routeParamsResolver->setType($params['_type']);
+            $this->getRouteParamsResolver()->setType($params['_type']);
         }
 
         if (isset($params['_secure'])) {
-            $this->_routeParamsResolver->setSecure($params['_secure']);
+            $this->getRouteParamsResolver()->setSecure($params['_secure']);
         }
 
         /**
@@ -416,13 +421,13 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
                 $this->_getRouteFrontName()
             )
         ) {
-            $this->_routeParamsResolver->setType(UrlInterface::URL_TYPE_DIRECT_LINK);
+            $this->getRouteParamsResolver()->setType(UrlInterface::URL_TYPE_DIRECT_LINK);
         }
 
         $result = $this->_getScope()->getBaseUrl($this->_getType(), $this->_isSecure());
         // setting back the original scope
         $this->setScope($origScope);
-        $this->_routeParamsResolver->setType(self::DEFAULT_URL_TYPE);
+        $this->getRouteParamsResolver()->setType(self::DEFAULT_URL_TYPE);
         return $result;
     }
 
@@ -471,7 +476,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
                 $key = array_shift($routePieces);
                 if (!empty($routePieces)) {
                     $value = array_shift($routePieces);
-                    $this->_routeParamsResolver->setRouteParam($key, $value);
+                    $this->getRouteParamsResolver()->setRouteParam($key, $value);
                 }
             }
         }
@@ -650,7 +655,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
      */
     protected function _setRouteParams(array $data, $unsetOldParams = true)
     {
-        $this->_routeParamsResolver->setRouteParams($data, $unsetOldParams);
+        $this->getRouteParamsResolver()->setRouteParams($data, $unsetOldParams);
         return $this;
     }
 
@@ -661,7 +666,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
      */
     protected function _getRouteParams()
     {
-        return $this->_routeParamsResolver->getRouteParams();
+        return $this->getRouteParamsResolver()->getRouteParams();
     }
 
     /**
@@ -677,7 +682,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
             return $routePath;
         }
 
-        $this->_routeParamsResolver->unsetData('route_params');
+        $this->getRouteParamsResolver()->unsetData('route_params');
 
         if (isset($routeParams['_direct'])) {
             if (is_array($routeParams)) {
@@ -786,7 +791,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
          * this method has condition for adding default controller and action names
          * in case when we have params
          */
-        $this->_routeParamsResolver->unsetData('secure');
+        $this->getRouteParamsResolver()->unsetData('secure');
         $fragment = null;
         if (isset($routeParams['_fragment'])) {
             $fragment = $routeParams['_fragment'];
@@ -840,7 +845,7 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
         if (!is_null($fragment)) {
             $url .= '#' . $fragment;
         }
-        $this->_routeParamsResolver->unsetData('secure');
+        $this->getRouteParamsResolver()->unsetData('secure');
         return $this->escape($url);
     }
 
@@ -1038,4 +1043,17 @@ class Url extends \Magento\Framework\Object implements \Magento\Framework\UrlInt
         $url = $this->_request->getScheme() . '://' . $this->_request->getHttpHost() . $port . $requestUri;
         return $url;
     }
+
+    /**
+     * Get Route Params Resolver
+     *
+     * @return Url\RouteParamsResolverInterface
+     */
+    protected function getRouteParamsResolver()
+    {
+        if (!$this->_routeParamsResolver) {
+            $this->_routeParamsResolver = $this->_routeParamsResolverFactory->create();
+        }
+        return $this->_routeParamsResolver;
+    }
 }
diff --git a/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php b/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php
index 893443659220d4b68e5ec3514f25beb0ea3c7725..a31ae25c76ea41a6efea88d85ccfb5c424092143 100644
--- a/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php
+++ b/lib/internal/Magento/Framework/Url/ScopeResolverInterface.php
@@ -10,7 +10,7 @@ interface ScopeResolverInterface extends \Magento\Framework\App\ScopeResolverInt
     /**
      * Retrieve area code
      *
-     * @return \Magento\Framework\Url\ScopeInterface[]
+     * @return string|null
      */
     public function getAreaCode();
 }
diff --git a/lib/internal/Magento/Framework/UrlInterface/Proxy.php b/lib/internal/Magento/Framework/UrlInterface/Proxy.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ed1a1bbc7bff3df6959ec70ed2aa2c779b21636
--- /dev/null
+++ b/lib/internal/Magento/Framework/UrlInterface/Proxy.php
@@ -0,0 +1,212 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\UrlInterface;
+
+/**
+ * Proxy class for @see \Magento\Framework\UrlInterface
+ */
+class Proxy implements \Magento\Framework\UrlInterface
+{
+    /**
+     * Object Manager instance
+     *
+     * @var \Magento\Framework\ObjectManagerInterface
+     */
+    protected $_objectManager = null;
+
+    /**
+     * Proxied instance name
+     *
+     * @var string
+     */
+    protected $_instanceName = null;
+
+    /**
+     * Proxied instance
+     *
+     * @var \Magento\Framework\UrlInterface
+     */
+    protected $_subject = null;
+
+    /**
+     * Instance shareability flag
+     *
+     * @var bool
+     */
+    protected $_isShared = null;
+
+    /**
+     * Proxy constructor
+     *
+     * @param \Magento\Framework\ObjectManagerInterface $objectManager
+     * @param string $instanceName
+     * @param bool $shared
+     */
+    public function __construct(
+        \Magento\Framework\ObjectManagerInterface $objectManager,
+        $instanceName = '\\Magento\\Framework\\UrlInterface',
+        $shared = true
+    ) {
+        $this->_objectManager = $objectManager;
+        $this->_instanceName = $instanceName;
+        $this->_isShared = $shared;
+    }
+
+    /**
+     * @return array
+     */
+    public function __sleep()
+    {
+        return ['_subject', '_isShared'];
+    }
+
+    /**
+     * Retrieve ObjectManager from global scope
+     *
+     * @return void
+     */
+    public function __wakeup()
+    {
+        $this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+    }
+
+    /**
+     * Clone proxied instance
+     *
+     * @return void
+     */
+    public function __clone()
+    {
+        $this->_subject = clone $this->_getSubject();
+    }
+
+    /**
+     * Get proxied instance
+     *
+     * @return \Magento\Framework\UrlInterface
+     */
+    protected function _getSubject()
+    {
+        if (!$this->_subject) {
+            $this->_subject = true === $this->_isShared
+                ? $this->_objectManager->get($this->_instanceName)
+                : $this->_objectManager->create($this->_instanceName);
+        }
+        return $this->_subject;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getUseSession()
+    {
+        return $this->_getSubject()->getUseSession();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBaseUrl($params = [])
+    {
+        return $this->_getSubject()->getBaseUrl($params);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getCurrentUrl()
+    {
+        return $this->_getSubject()->getCurrentUrl();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRouteUrl($routePath = null, $routeParams = null)
+    {
+        return $this->_getSubject()->getRouteUrl($routePath, $routeParams);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addSessionParam()
+    {
+        return $this->_getSubject()->addSessionParam();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function addQueryParams(array $data)
+    {
+        return $this->_getSubject()->addQueryParams($data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setQueryParam($key, $data)
+    {
+        return $this->_getSubject()->setQueryParam($key, $data);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getUrl($routePath = null, $routeParams = null)
+    {
+        return $this->_getSubject()->getUrl($routePath, $routeParams);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function escape($value)
+    {
+        return $this->_getSubject()->escape($value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDirectUrl($url, $params = [])
+    {
+        return $this->_getSubject()->getDirectUrl($url, $params);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function sessionUrlVar($html)
+    {
+        return $this->_getSubject()->sessionUrlVar($html);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function isOwnOriginUrl()
+    {
+        return $this->_getSubject()->isOwnOriginUrl();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getRedirectUrl($url)
+    {
+        return $this->_getSubject()->getRedirectUrl($url);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setScope($params)
+    {
+        return $this->_getSubject()->setScope($params);
+    }
+}
diff --git a/lib/internal/Magento/Framework/Validator/Factory.php b/lib/internal/Magento/Framework/Validator/Factory.php
index a663b91cc745f01f164e1d30a1f6e9f04682087b..ef5679b220c2460dbc1e94d7d09760af0436fa31 100644
--- a/lib/internal/Magento/Framework/Validator/Factory.php
+++ b/lib/internal/Magento/Framework/Validator/Factory.php
@@ -24,6 +24,11 @@ class Factory
      */
     protected $_configFiles = null;
 
+    /**
+     * @var bool
+     */
+    private $isDefaultTranslatorInitialized = false;
+
     /**
      * Initialize dependencies
      *
@@ -36,7 +41,6 @@ class Factory
     ) {
         $this->_objectManager = $objectManager;
         $this->_configFiles = $moduleReader->getConfigurationFiles('validation.xml');
-        $this->_initializeDefaultTranslator();
     }
 
     /**
@@ -46,15 +50,18 @@ class Factory
      */
     protected function _initializeDefaultTranslator()
     {
-        // Pass translations to \Magento\Framework\TranslateInterface from validators
-        $translatorCallback = function () {
-            $argc = func_get_args();
-            return (string)new \Magento\Framework\Phrase(array_shift($argc), $argc);
-        };
-        /** @var \Magento\Framework\Translate\Adapter $translator */
-        $translator = $this->_objectManager->create('Magento\Framework\Translate\Adapter');
-        $translator->setOptions(['translator' => $translatorCallback]);
-        \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($translator);
+        if (!$this->isDefaultTranslatorInitialized) {
+            // Pass translations to \Magento\Framework\TranslateInterface from validators
+            $translatorCallback = function () {
+                $argc = func_get_args();
+                return (string)new \Magento\Framework\Phrase(array_shift($argc), $argc);
+            };
+            /** @var \Magento\Framework\Translate\Adapter $translator */
+            $translator = $this->_objectManager->create('Magento\Framework\Translate\Adapter');
+            $translator->setOptions(['translator' => $translatorCallback]);
+            \Magento\Framework\Validator\AbstractValidator::setDefaultTranslator($translator);
+            $this->isDefaultTranslatorInitialized = true;
+        }
     }
 
     /**
@@ -66,6 +73,7 @@ class Factory
      */
     public function getValidatorConfig()
     {
+        $this->_initializeDefaultTranslator();
         return $this->_objectManager->create('Magento\Framework\Validator\Config', ['configFiles' => $this->_configFiles]);
     }
 
@@ -79,6 +87,7 @@ class Factory
      */
     public function createValidatorBuilder($entityName, $groupName, array $builderConfig = null)
     {
+        $this->_initializeDefaultTranslator();
         return $this->getValidatorConfig()->createValidatorBuilder($entityName, $groupName, $builderConfig);
     }
 
@@ -92,6 +101,7 @@ class Factory
      */
     public function createValidator($entityName, $groupName, array $builderConfig = null)
     {
+        $this->_initializeDefaultTranslator();
         return $this->getValidatorConfig()->createValidator($entityName, $groupName, $builderConfig);
     }
 }
diff --git a/lib/internal/Magento/Framework/View/Layout.php b/lib/internal/Magento/Framework/View/Layout.php
index 6c857548d18ed329d6767bbc6f35684f8205e059..da8130891b3b05e624f358c2f0fe5b1b52d7444d 100644
--- a/lib/internal/Magento/Framework/View/Layout.php
+++ b/lib/internal/Magento/Framework/View/Layout.php
@@ -23,6 +23,12 @@ use Magento\Framework\View\Layout\ScheduledStructure;
  */
 class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Framework\View\LayoutInterface
 {
+
+    /**
+     * Empty layout xml
+     */
+    const LAYOUT_NODE = '<layout/>';
+
     /**
      * Layout Update module
      *
@@ -173,7 +179,6 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
         $cacheable = true
     ) {
         $this->_elementClass = 'Magento\Framework\View\Layout\Element';
-        $this->setXml(simplexml_load_string('<layout/>', $this->_elementClass));
         $this->_renderingOutput = new \Magento\Framework\Object();
 
         $this->_processorFactory = $processorFactory;
@@ -188,8 +193,6 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
 
         $this->readerContextFactory = $readerContextFactory;
         $this->generatorContextFactory = $generatorContextFactory;
-
-        $this->readerContext = $this->readerContextFactory->create();
     }
 
     /**
@@ -248,7 +251,7 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
             $this->_update = null;
         }
         $this->_blocks = [];
-        $this->_xml = null;
+        parent::__destruct();
     }
 
     /**
@@ -278,14 +281,6 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
         return $this;
     }
 
-    /**
-     * @return Layout\Reader\Context
-     */
-    public function getReaderContext()
-    {
-        return $this->readerContext;
-    }
-
     /**
      * Create structure of elements from the loaded XML configuration
      *
@@ -297,13 +292,12 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
         $cacheId = 'structure_' . $this->getUpdate()->getCacheId();
         $result = $this->cache->load($cacheId);
         if ($result) {
-            /** @var Layout\Reader\Context $readerContext */
             $this->readerContext = unserialize($result);
         } else {
             \Magento\Framework\Profiler::start('build_structure');
-            $this->readerPool->interpret($this->readerContext, $this->getNode());
+            $this->readerPool->interpret($this->getReaderContext(), $this->getNode());
             \Magento\Framework\Profiler::stop('build_structure');
-            $this->cache->save(serialize($this->readerContext), $cacheId, $this->getUpdate()->getHandles());
+            $this->cache->save(serialize($this->getReaderContext()), $cacheId, $this->getUpdate()->getHandles());
         }
 
         $generatorContext = $this->generatorContextFactory->create(
@@ -314,7 +308,7 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
         );
 
         \Magento\Framework\Profiler::start('generate_elements');
-        $this->generatorPool->process($this->readerContext, $generatorContext);
+        $this->generatorPool->process($this->getReaderContext(), $generatorContext);
         \Magento\Framework\Profiler::stop('generate_elements');
 
         $this->addToOutputRootContainers();
@@ -1035,7 +1029,7 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
     public function isCacheable()
     {
         $this->build();
-        $cacheableXml = !(bool)count($this->_xml->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'));
+        $cacheableXml = !(bool)count($this->getXml()->xpath('//' . Element::TYPE_BLOCK . '[@cacheable="false"]'));
         return $this->cacheable && $cacheableXml;
     }
 
@@ -1060,4 +1054,30 @@ class Layout extends \Magento\Framework\Simplexml\Config implements \Magento\Fra
         $this->isPrivate = (bool)$isPrivate;
         return $this;
     }
+
+    /**
+     * Getter and lazy loader for xml element
+     *
+     * @return \Magento\Framework\Simplexml\Element
+     */
+    protected function getXml()
+    {
+        if (!$this->_xml) {
+            $this->setXml(simplexml_load_string(self::LAYOUT_NODE, $this->_elementClass));
+        }
+        return $this->_xml;
+    }
+
+    /**
+     * Getter and lazy loader for reader context
+     *
+     * @return Layout\Reader\Context
+     */
+    public function getReaderContext()
+    {
+        if (!$this->readerContext) {
+            $this->readerContext = $this->readerContextFactory->create();
+        }
+        return $this->readerContext;
+    }
 }
diff --git a/lib/internal/Magento/Framework/View/Layout/Proxy.php b/lib/internal/Magento/Framework/View/Layout/Proxy.php
index 8c399f4e4161a7f847b4f22db49e7e97e8528ef2..95220e34913df356a84e427d9d72e04f5c73b6ac 100644
--- a/lib/internal/Magento/Framework/View/Layout/Proxy.php
+++ b/lib/internal/Magento/Framework/View/Layout/Proxy.php
@@ -6,8 +6,7 @@
 namespace Magento\Framework\View\Layout;
 
 /**
- * Proxy class for \Magento\Framework\View\Layout
- *
+ * Proxy class for @see \Magento\Framework\View\Layout
  * @SuppressWarnings(PHPMD.ExcessivePublicCount)
  */
 class Proxy extends \Magento\Framework\View\Layout
@@ -17,42 +16,44 @@ class Proxy extends \Magento\Framework\View\Layout
      *
      * @var \Magento\Framework\ObjectManagerInterface
      */
-    protected $objectManager;
+    protected $_objectManager = null;
 
     /**
      * Proxied instance name
      *
      * @var string
      */
-    protected $instanceName;
+    protected $_instanceName = null;
 
     /**
      * Proxied instance
      *
      * @var \Magento\Framework\View\Layout
      */
-    protected $subject;
+    protected $_subject = null;
 
     /**
      * Instance shareability flag
      *
      * @var bool
      */
-    protected $isShared;
+    protected $_isShared = null;
 
     /**
+     * Proxy constructor
+     *
      * @param \Magento\Framework\ObjectManagerInterface $objectManager
      * @param string $instanceName
      * @param bool $shared
      */
     public function __construct(
         \Magento\Framework\ObjectManagerInterface $objectManager,
-        $instanceName = 'Magento\Framework\View\Layout',
+        $instanceName = '\\Magento\\Framework\\View\\Layout',
         $shared = true
     ) {
-        $this->objectManager = $objectManager;
-        $this->instanceName = $instanceName;
-        $this->isShared = $shared;
+        $this->_objectManager = $objectManager;
+        $this->_instanceName = $instanceName;
+        $this->_isShared = $shared;
     }
 
     /**
@@ -70,7 +71,7 @@ class Proxy extends \Magento\Framework\View\Layout
      */
     public function __wakeup()
     {
-        $this->objectManager = \Magento\Framework\App\ObjectManager::getInstance();
+        $this->_objectManager = \Magento\Framework\App\ObjectManager::getInstance();
     }
 
     /**
@@ -80,7 +81,7 @@ class Proxy extends \Magento\Framework\View\Layout
      */
     public function __clone()
     {
-        $this->subject = clone $this->getSubject();
+        $this->_subject = clone $this->_getSubject();
     }
 
     /**
@@ -88,415 +89,334 @@ class Proxy extends \Magento\Framework\View\Layout
      *
      * @return \Magento\Framework\View\Layout
      */
-    protected function getSubject()
+    protected function _getSubject()
     {
-        if (!$this->subject) {
-            $this->subject = true === $this->isShared
-                ? $this->objectManager->get($this->instanceName)
-                : $this->objectManager->create($this->instanceName);
+        if (!$this->_subject) {
+            $this->_subject = true === $this->_isShared
+                ? $this->_objectManager->get($this->_instanceName)
+                : $this->_objectManager->create($this->_instanceName);
         }
-        return $this->subject;
+        return $this->_subject;
     }
 
     /**
-     * Retrieve the layout update instance
-     *
-     * @return \Magento\Framework\View\Layout\ProcessorInterface
+     * {@inheritdoc}
+     */
+    public function setGeneratorPool(\Magento\Framework\View\Layout\GeneratorPool $generatorPool)
+    {
+        return $this->_getSubject()->setGeneratorPool($generatorPool);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setBuilder(\Magento\Framework\View\Layout\BuilderInterface $builder)
+    {
+        return $this->_getSubject()->setBuilder($builder);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function publicBuild()
+    {
+        $this->_getSubject()->publicBuild();
+    }
+
+    /**
+     * {@inheritdoc}
      */
     public function getUpdate()
     {
-        return $this->getSubject()->getUpdate();
+        return $this->_getSubject()->getUpdate();
     }
 
     /**
-     * Layout xml generation
-     *
-     * @return $this
+     * {@inheritdoc}
      */
     public function generateXml()
     {
-        return $this->getSubject()->generateXml();
+        return $this->_getSubject()->generateXml();
     }
 
     /**
-     * Create structure of elements from the loaded XML configuration
-     *
-     * @return void
+     * {@inheritdoc}
      */
     public function generateElements()
     {
-        $this->getSubject()->generateElements();
+        $this->_getSubject()->generateElements();
     }
 
     /**
-     * Get child block if exists
-     *
-     * @param string $parentName
-     * @param string $alias
-     * @return bool|\Magento\Framework\View\Element\AbstractBlock
+     * {@inheritdoc}
      */
     public function getChildBlock($parentName, $alias)
     {
-        return $this->getSubject()->getChildBlock($parentName, $alias);
+        return $this->_getSubject()->getChildBlock($parentName, $alias);
     }
 
     /**
-     * Set child element into layout structure
-     *
-     * @param string $parentName
-     * @param string $elementName
-     * @param string $alias
-     * @return $this
+     * {@inheritdoc}
      */
     public function setChild($parentName, $elementName, $alias)
     {
-        return $this->getSubject()->setChild($parentName, $elementName, $alias);
+        return $this->_getSubject()->setChild($parentName, $elementName, $alias);
     }
 
     /**
-     * Reorder a child of a specified element
-     *
-     * If $offsetOrSibling is null, it will put the element to the end
-     * If $offsetOrSibling is numeric (integer) value, it will put the element after/before specified position
-     * Otherwise -- after/before specified sibling
-     *
-     * @param string $parentName
-     * @param string $childName
-     * @param string|int|null $offsetOrSibling
-     * @param bool $after
-     * @return void
+     * {@inheritdoc}
      */
     public function reorderChild($parentName, $childName, $offsetOrSibling, $after = true)
     {
-        $this->getSubject()->reorderChild($parentName, $childName, $offsetOrSibling, $after);
+        $this->_getSubject()->reorderChild($parentName, $childName, $offsetOrSibling, $after);
     }
 
     /**
-     * Remove child element from parent
-     *
-     * @param string $parentName
-     * @param string $alias
-     * @return $this
+     * {@inheritdoc}
      */
     public function unsetChild($parentName, $alias)
     {
-        return $this->getSubject()->unsetChild($parentName, $alias);
+        return $this->_getSubject()->unsetChild($parentName, $alias);
     }
 
     /**
-     * Get list of child names
-     *
-     * @param string $parentName
-     * @return array
+     * {@inheritdoc}
      */
     public function getChildNames($parentName)
     {
-        return $this->getSubject()->getChildNames($parentName);
+        return $this->_getSubject()->getChildNames($parentName);
     }
 
     /**
-     * Get list of child blocks
-     *
-     * Returns associative array of <alias> => <block instance>
-     *
-     * @param string $parentName
-     * @return array
+     * {@inheritdoc}
      */
     public function getChildBlocks($parentName)
     {
-        return $this->getSubject()->getChildBlocks($parentName);
+        return $this->_getSubject()->getChildBlocks($parentName);
     }
 
     /**
-     * Get child name by alias
-     *
-     * @param string $parentName
-     * @param string $alias
-     * @return bool|string
+     * {@inheritdoc}
      */
     public function getChildName($parentName, $alias)
     {
-        return $this->getSubject()->getChildName($parentName, $alias);
+        return $this->_getSubject()->getChildName($parentName, $alias);
     }
 
     /**
-     * Find an element in layout, render it and return string with its output
-     *
-     * @param string $name
-     * @param bool $useCache
-     * @return string
+     * {@inheritdoc}
      */
     public function renderElement($name, $useCache = true)
     {
-        return $this->getSubject()->renderElement($name, $useCache);
+        return $this->_getSubject()->renderElement($name, $useCache);
     }
 
     /**
-     * Add element to parent group
-     *
-     * @param string $blockName
-     * @param string $parentGroupName
-     * @return bool
+     * {@inheritdoc}
+     */
+    public function renderNonCachedElement($name)
+    {
+        return $this->_getSubject()->renderNonCachedElement($name);
+    }
+
+    /**
+     * {@inheritdoc}
      */
     public function addToParentGroup($blockName, $parentGroupName)
     {
-        return $this->getSubject()->addToParentGroup($blockName, $parentGroupName);
+        return $this->_getSubject()->addToParentGroup($blockName, $parentGroupName);
     }
 
     /**
-     * Get element names for specified group
-     *
-     * @param string $blockName
-     * @param string $groupName
-     * @return array
+     * {@inheritdoc}
      */
     public function getGroupChildNames($blockName, $groupName)
     {
-        return $this->getSubject()->getGroupChildNames($blockName, $groupName);
+        return $this->_getSubject()->getGroupChildNames($blockName, $groupName);
     }
 
     /**
-     * Check if element exists in layout structure
-     *
-     * @param string $name
-     * @return bool
+     * {@inheritdoc}
      */
     public function hasElement($name)
     {
-        return $this->getSubject()->hasElement($name);
+        return $this->_getSubject()->hasElement($name);
     }
 
     /**
-     * Get property value of an element
-     *
-     * @param string $name
-     * @param string $attribute
-     * @return mixed
+     * {@inheritdoc}
      */
     public function getElementProperty($name, $attribute)
     {
-        return $this->getSubject()->getElementProperty($name, $attribute);
+        return $this->_getSubject()->getElementProperty($name, $attribute);
     }
 
     /**
-     * Whether specified element is a block
-     *
-     * @param string $name
-     * @return bool
+     * {@inheritdoc}
      */
     public function isBlock($name)
     {
-        return $this->getSubject()->isBlock($name);
+        return $this->_getSubject()->isBlock($name);
     }
 
     /**
-     * Checks if element with specified name is container
-     *
-     * @param string $name
-     * @return bool
+     * {@inheritdoc}
+     */
+    public function isUiComponent($name)
+    {
+        return $this->_getSubject()->isUiComponent($name);
+    }
+
+    /**
+     * {@inheritdoc}
      */
     public function isContainer($name)
     {
-        return $this->getSubject()->isContainer($name);
+        return $this->_getSubject()->isContainer($name);
     }
 
     /**
-     * Whether the specified element may be manipulated externally
-     *
-     * @param string $name
-     * @return bool
+     * {@inheritdoc}
      */
     public function isManipulationAllowed($name)
     {
-        return $this->getSubject()->isManipulationAllowed($name);
+        return $this->_getSubject()->isManipulationAllowed($name);
     }
 
     /**
-     * Save block in blocks registry
-     *
-     * @param string $name
-     * @param \Magento\Framework\View\Element\AbstractBlock $block
-     * @return $this
+     * {@inheritdoc}
      */
     public function setBlock($name, $block)
     {
-        return $this->getSubject()->setBlock($name, $block);
+        return $this->_getSubject()->setBlock($name, $block);
     }
 
     /**
-     * Remove block from registry
-     *
-     * @param string $name
-     * @return $this
+     * {@inheritdoc}
      */
     public function unsetElement($name)
     {
-        return $this->getSubject()->unsetElement($name);
+        return $this->_getSubject()->unsetElement($name);
     }
 
     /**
-     * Block Factory
-     *
-     * @param  string $type
-     * @param  string $name
-     * @param  array $attributes
-     * @return \Magento\Framework\View\Element\AbstractBlock
+     * {@inheritdoc}
      */
-    public function createBlock($type, $name = '', array $attributes = [])
+    public function createBlock($type, $name = '', array $arguments = [])
     {
-        return $this->getSubject()->createBlock($type, $name, $attributes);
+        return $this->_getSubject()->createBlock($type, $name, $arguments);
     }
 
     /**
-     * Add a block to registry, create new object if needed
-     *
-     * @param string|\Magento\Framework\View\Element\AbstractBlock $block
-     * @param string $name
-     * @param string $parent
-     * @param string $alias
-     * @return \Magento\Framework\View\Element\AbstractBlock
+     * {@inheritdoc}
      */
     public function addBlock($block, $name = '', $parent = '', $alias = '')
     {
-        return $this->getSubject()->addBlock($block, $name, $parent, $alias);
+        return $this->_getSubject()->addBlock($block, $name, $parent, $alias);
     }
 
     /**
-     * Insert container into layout structure
-     *
-     * @param string $name
-     * @param string $label
-     * @param array $options
-     * @param string $parent
-     * @param string $alias
-     * @return void
+     * {@inheritdoc}
      */
     public function addContainer($name, $label, array $options = [], $parent = '', $alias = '')
     {
-        $this->getSubject()->addContainer($name, $label, $options, $parent, $alias);
+        $this->_getSubject()->addContainer($name, $label, $options, $parent, $alias);
     }
 
     /**
-     * Rename element in layout and layout structure
-     *
-     * @param string $oldName
-     * @param string $newName
-     * @return bool
+     * {@inheritdoc}
      */
     public function renameElement($oldName, $newName)
     {
-        return $this->getSubject()->renameElement($oldName, $newName);
+        return $this->_getSubject()->renameElement($oldName, $newName);
     }
 
     /**
-     * Retrieve all blocks from registry as array
-     *
-     * @return array
+     * {@inheritdoc}
      */
     public function getAllBlocks()
     {
-        return $this->getSubject()->getAllBlocks();
+        return $this->_getSubject()->getAllBlocks();
     }
 
     /**
-     * Get block object by name
-     *
-     * @param string $name
-     * @return \Magento\Framework\View\Element\AbstractBlock|bool
+     * {@inheritdoc}
      */
     public function getBlock($name)
     {
-        return $this->getSubject()->getBlock($name);
+        return $this->_getSubject()->getBlock($name);
     }
 
     /**
-     * Gets parent name of an element with specified name
-     *
-     * @param string $childName
-     * @return bool|string
+     * {@inheritdoc}
+     */
+    public function getUiComponent($name)
+    {
+        return $this->_getSubject()->getUiComponent($name);
+    }
+
+    /**
+     * {@inheritdoc}
      */
     public function getParentName($childName)
     {
-        return $this->getSubject()->getParentName($childName);
+        return $this->_getSubject()->getParentName($childName);
     }
 
     /**
-     * Get element alias by name
-     *
-     * @param string $name
-     * @return bool|string
+     * {@inheritdoc}
      */
     public function getElementAlias($name)
     {
-        return $this->getSubject()->getElementAlias($name);
+        return $this->_getSubject()->getElementAlias($name);
     }
 
     /**
-     * Add an element to output
-     *
-     * @param string $name
-     * @return $this
+     * {@inheritdoc}
      */
     public function addOutputElement($name)
     {
-        return $this->getSubject()->addOutputElement($name);
+        return $this->_getSubject()->addOutputElement($name);
     }
 
     /**
-     * Remove an element from output
-     *
-     * @param string $name
-     * @return $this
+     * {@inheritdoc}
      */
     public function removeOutputElement($name)
     {
-        return $this->getSubject()->removeOutputElement($name);
+        return $this->_getSubject()->removeOutputElement($name);
     }
 
     /**
-     * Get all blocks marked for output
-     *
-     * @return string
+     * {@inheritdoc}
      */
     public function getOutput()
     {
-        return $this->getSubject()->getOutput();
+        return $this->_getSubject()->getOutput();
     }
 
     /**
-     * Retrieve messages block
-     *
-     * @return \Magento\Framework\View\Element\Messages
+     * {@inheritdoc}
      */
     public function getMessagesBlock()
     {
-        return $this->getSubject()->getMessagesBlock();
+        return $this->_getSubject()->getMessagesBlock();
     }
 
     /**
-     * Get block singleton
-     *
-     * @param string $type
-     * @throws \Magento\Framework\Exception\LocalizedException
-     * @return \Magento\Framework\App\Helper\AbstractHelper
+     * {@inheritdoc}
      */
     public function getBlockSingleton($type)
     {
-        return $this->getSubject()->getBlockSingleton($type);
+        return $this->_getSubject()->getBlockSingleton($type);
     }
 
     /**
-     * @param string $namespace
-     * @param string $staticType
-     * @param string $dynamicType
-     * @param string $type
-     * @param string $template
-     * @param array $data
-     * @return $this
+     * {@inheritdoc}
      */
     public function addAdjustableRenderer($namespace, $staticType, $dynamicType, $type, $template, $data = [])
     {
-        return $this->getSubject()->addAdjustableRenderer(
+        return $this->_getSubject()->addAdjustableRenderer(
             $namespace,
             $staticType,
             $dynamicType,
@@ -507,394 +427,298 @@ class Proxy extends \Magento\Framework\View\Layout
     }
 
     /**
-     * Get renderer options
-     *
-     * @param string $namespace
-     * @param string $staticType
-     * @param string $dynamicType
-     * @return array|null
+     * {@inheritdoc}
      */
     public function getRendererOptions($namespace, $staticType, $dynamicType)
     {
-        return $this->getSubject()->getRendererOptions($namespace, $staticType, $dynamicType);
+        return $this->_getSubject()->getRendererOptions($namespace, $staticType, $dynamicType);
     }
 
     /**
-     * Execute renderer
-     *
-     * @param string $namespace
-     * @param string $staticType
-     * @param string $dynamicType
-     * @param array $data
-     * @return void
+     * {@inheritdoc}
      */
     public function executeRenderer($namespace, $staticType, $dynamicType, $data = [])
     {
-        $this->getSubject()->executeRenderer($namespace, $staticType, $dynamicType, $data);
+        $this->_getSubject()->executeRenderer($namespace, $staticType, $dynamicType, $data);
     }
 
     /**
-     * Init messages by message storage(s), loading and adding messages to layout messages block
-     *
-     * @param string|array $messageGroups
-     * @return void
+     * {@inheritdoc}
      */
     public function initMessages($messageGroups = [])
     {
-        $this->getSubject()->initMessages($messageGroups);
+        $this->_getSubject()->initMessages($messageGroups);
     }
 
     /**
-     * Check is exists non-cacheable layout elements
-     *
-     * @return bool
+     * {@inheritdoc}
      */
     public function isCacheable()
     {
-        return $this->getSubject()->isCacheable();
+        return $this->_getSubject()->isCacheable();
     }
 
     /**
-     * Check is exists non-cacheable layout elements
-     *
-     * @return bool
+     * {@inheritdoc}
      */
     public function isPrivate()
     {
-        return $this->getSubject()->isPrivate();
+        return $this->_getSubject()->isPrivate();
     }
 
     /**
-     * Mark layout as private
-     *
-     * @param bool $isPrivate
-     * @return $this
+     * {@inheritdoc}
      */
     public function setIsPrivate($isPrivate = true)
     {
-        return $this->getSubject()->setIsPrivate($isPrivate);
+        return $this->_getSubject()->setIsPrivate($isPrivate);
     }
 
     /**
-     * Sets xml for this configuration
-     *
-     * @param \Magento\Framework\Simplexml\Element $node
-     * @return $this
+     * {@inheritdoc}
+     */
+    public function getReaderContext()
+    {
+        return $this->_getSubject()->getReaderContext();
+    }
+
+    /**
+     * {@inheritdoc}
      */
     public function setXml(\Magento\Framework\Simplexml\Element $node)
     {
-        return $this->getSubject()->setXml($node);
+        return $this->_getSubject()->setXml($node);
     }
 
     /**
-     * Returns node found by the $path
-     *
-     * @param string $path
-     * @return \Magento\Framework\Simplexml\Element|bool
-     * @see \Magento\Framework\Simplexml\Element::descend
+     * {@inheritdoc}
      */
     public function getNode($path = null)
     {
-        return $this->getSubject()->getNode($path);
+        return $this->_getSubject()->getNode($path);
     }
 
     /**
-     * Returns nodes found by xpath expression
-     *
-     * @param string $xpath
-     * @return \SimpleXMLElement[]|bool
+     * {@inheritdoc}
      */
     public function getXpath($xpath)
     {
-        return $this->getSubject()->getXpath($xpath);
+        return $this->_getSubject()->getXpath($xpath);
     }
 
     /**
-     * Set cache
-     *
-     * @param \Magento\Framework\Simplexml\Config\Cache\AbstractCache $cache
-     * @return $this
+     * {@inheritdoc}
      */
     public function setCache($cache)
     {
-        return $this->getSubject()->setCache($cache);
+        return $this->_getSubject()->setCache($cache);
     }
 
     /**
-     * Return cache
-     *
-     * @return \Magento\Framework\Simplexml\Config\Cache\AbstractCache
+     * {@inheritdoc}
      */
     public function getCache()
     {
-        return $this->getSubject()->getCache();
+        return $this->_getSubject()->getCache();
     }
 
     /**
-     * Set whether cache is saved
-     *
-     * @param boolean $flag
-     * @return $this
+     * {@inheritdoc}
      */
     public function setCacheSaved($flag)
     {
-        return $this->getSubject()->setCacheSaved($flag);
+        return $this->_getSubject()->setCacheSaved($flag);
     }
 
     /**
-     * Return whether cache is saved
-     *
-     * @return bool
-     *
-     * @SuppressWarnings(PHPMD.BooleanGetMethodName)
+     * {@inheritdoc}
      */
     public function getCacheSaved()
     {
-        return $this->getSubject()->getCacheSaved();
+        return $this->_getSubject()->getCacheSaved();
     }
 
     /**
-     * Set cache ID
-     *
-     * @param string $id
-     * @return $this
+     * {@inheritdoc}
      */
     public function setCacheId($id)
     {
-        return $this->getSubject()->setCacheId($id);
+        return $this->_getSubject()->setCacheId($id);
     }
 
     /**
-     * Return cache ID
-     *
-     * @return string
+     * {@inheritdoc}
      */
     public function getCacheId()
     {
-        return $this->getSubject()->getCacheId();
+        return $this->_getSubject()->getCacheId();
     }
 
     /**
-     * Set cache tags
-     *
-     * @param array $tags
-     * @return $this
+     * {@inheritdoc}
      */
     public function setCacheTags($tags)
     {
-        return $this->getSubject()->setCacheTags($tags);
+        return $this->_getSubject()->setCacheTags($tags);
     }
 
     /**
-     * Return cache tags
-     *
-     * @return array
+     * {@inheritdoc}
      */
     public function getCacheTags()
     {
-        return $this->getSubject()->getCacheTags();
+        return $this->_getSubject()->getCacheTags();
     }
 
     /**
-     * Set cache lifetime
-     *
-     * @param int $lifetime
-     * @return $this
+     * {@inheritdoc}
      */
     public function setCacheLifetime($lifetime)
     {
-        return $this->getSubject()->setCacheLifetime($lifetime);
+        return $this->_getSubject()->setCacheLifetime($lifetime);
     }
 
     /**
-     * Return cache lifetime
-     *
-     * @return int
+     * {@inheritdoc}
      */
     public function getCacheLifetime()
     {
-        return $this->getSubject()->getCacheLifetime();
+        return $this->_getSubject()->getCacheLifetime();
     }
 
     /**
-     * Set cache checksum
-     *
-     * @param string $data
-     * @return $this
+     * {@inheritdoc}
      */
     public function setCacheChecksum($data)
     {
-        return $this->getSubject()->setCacheChecksum($data);
+        return $this->_getSubject()->setCacheChecksum($data);
     }
 
     /**
-     * Update cache checksum
-     *
-     * @param string $data
-     * @return $this
+     * {@inheritdoc}
      */
     public function updateCacheChecksum($data)
     {
-        return $this->getSubject()->updateCacheChecksum($data);
+        return $this->_getSubject()->updateCacheChecksum($data);
     }
 
     /**
-     * Return cache checksum
-     *
-     * @return string
+     * {@inheritdoc}
      */
     public function getCacheChecksum()
     {
-        return $this->getSubject()->getCacheChecksum();
+        return $this->_getSubject()->getCacheChecksum();
     }
 
     /**
-     * Get cache checksum ID
-     *
-     * @return string
+     * {@inheritdoc}
      */
     public function getCacheChecksumId()
     {
-        return $this->getSubject()->getCacheChecksumId();
+        return $this->_getSubject()->getCacheChecksumId();
     }
 
     /**
-     * Fetch cache checksum
-     *
-     * @return boolean
+     * {@inheritdoc}
      */
     public function fetchCacheChecksum()
     {
-        return $this->getSubject()->fetchCacheChecksum();
+        return $this->_getSubject()->fetchCacheChecksum();
     }
 
     /**
-     * Validate cache checksum
-     *
-     * @return boolean
+     * {@inheritdoc}
      */
     public function validateCacheChecksum()
     {
-        return $this->getSubject()->validateCacheChecksum();
+        return $this->_getSubject()->validateCacheChecksum();
     }
 
     /**
-     * Load cache
-     *
-     * @return boolean
+     * {@inheritdoc}
      */
     public function loadCache()
     {
-        return $this->getSubject()->loadCache();
+        return $this->_getSubject()->loadCache();
     }
 
     /**
-     * Save cache
-     *
-     * @param array $tags
-     * @return $this
+     * {@inheritdoc}
      */
     public function saveCache($tags = null)
     {
-        return $this->getSubject()->saveCache($tags);
+        return $this->_getSubject()->saveCache($tags);
     }
 
     /**
-     * Return Xml of node as string
-     *
-     * @return string
+     * {@inheritdoc}
      */
     public function getXmlString()
     {
-        return $this->getSubject()->getXmlString();
+        return $this->_getSubject()->getXmlString();
     }
 
     /**
-     * Remove cache
-     *
-     * @return $this
+     * {@inheritdoc}
      */
     public function removeCache()
     {
-        return $this->getSubject()->removeCache();
+        return $this->_getSubject()->removeCache();
     }
 
     /**
-     * Imports XML file
-     *
-     * @param string $filePath
-     * @return boolean
+     * {@inheritdoc}
      */
     public function loadFile($filePath)
     {
-        return $this->getSubject()->loadFile($filePath);
+        return $this->_getSubject()->loadFile($filePath);
     }
 
     /**
-     * Imports XML string
-     *
-     * @param string $string
-     * @return boolean
+     * {@inheritdoc}
      */
     public function loadString($string)
     {
-        return $this->getSubject()->loadString($string);
+        return $this->_getSubject()->loadString($string);
     }
 
     /**
-     * Imports DOM node
-     *
-     * @param \DOMNode $dom
-     * @return bool
+     * {@inheritdoc}
      */
     public function loadDom(\DOMNode $dom)
     {
-        return $this->getSubject()->loadDom($dom);
+        return $this->_getSubject()->loadDom($dom);
     }
 
     /**
-     * Create node by $path and set its value.
-     *
-     * @param string $path separated by slashes
-     * @param string $value
-     * @param boolean $overwrite
-     * @return $this
+     * {@inheritdoc}
      */
     public function setNode($path, $value, $overwrite = true)
     {
-        return $this->getSubject()->setNode($path, $value, $overwrite);
+        return $this->_getSubject()->setNode($path, $value, $overwrite);
     }
 
     /**
-     * Process configuration xml
-     *
-     * @return $this
+     * {@inheritdoc}
      */
     public function applyExtends()
     {
-        return $this->getSubject()->applyExtends();
+        return $this->_getSubject()->applyExtends();
     }
 
     /**
-     * Stub method for processing file data right after loading the file text
-     *
-     * @param string $text
-     * @return string
+     * {@inheritdoc}
      */
     public function processFileData($text)
     {
-        return $this->getSubject()->processFileData($text);
+        return $this->_getSubject()->processFileData($text);
     }
 
     /**
-     * Extend configuration
-     *
-     * @param \Magento\Framework\Simplexml\Config $config
-     * @param boolean $overwrite
-     * @return $this
+     * {@inheritdoc}
      */
     public function extend(\Magento\Framework\Simplexml\Config $config, $overwrite = true)
     {
-        return $this->getSubject()->extend($config, $overwrite);
+        return $this->_getSubject()->extend($config, $overwrite);
     }
 }
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php
index bd18ebe344f884734c1b78233ed278c6c8b6b440..6f552656be534c3322b7f778a769fb55c83a366c 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php
@@ -9,6 +9,7 @@
  */
 namespace Magento\Framework\View\Test\Unit\Element;
 
+use Magento\Framework\Message\Manager;
 use \Magento\Framework\View\Element\Messages;
 
 use Magento\Framework\Message\ManagerInterface;
@@ -229,7 +230,7 @@ class MessagesTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($emptyMessagesCacheKey, $this->messages->getCacheKeyInfo());
 
         $messagesCacheKey = ['storage_types' => 'a:1:{i:0;s:7:"default";}'];
-        $this->messages->addStorageType(ManagerInterface::DEFAULT_GROUP);
+        $this->messages->addStorageType(Manager::DEFAULT_GROUP);
         $this->assertEquals($messagesCacheKey, $this->messages->getCacheKeyInfo());
     }
 
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php
index 45effcf449e43c4e7a05dcc77d37003195069eea..3d60c5e411300fd1ba642d03ed96720959434828 100644
--- a/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php
+++ b/lib/internal/Magento/Framework/View/Test/Unit/LayoutTest.php
@@ -134,9 +134,6 @@ class LayoutTest extends \PHPUnit_Framework_TestCase
         $this->readerContextMock = $this->getMockBuilder('Magento\Framework\View\Layout\Reader\Context')
             ->disableOriginalConstructor()
             ->getMock();
-        $this->readerContextFactoryMock->expects($this->once())
-            ->method('create')
-            ->willReturn($this->readerContextMock);
         $this->generatorContextFactoryMock = $this->getMockBuilder(
             'Magento\Framework\View\Layout\Generator\ContextFactory'
         )->disableOriginalConstructor()
@@ -683,6 +680,9 @@ class LayoutTest extends \PHPUnit_Framework_TestCase
 
     public function testGenerateElementsWithoutCache()
     {
+        $this->readerContextFactoryMock->expects($this->once())
+            ->method('create')
+            ->willReturn($this->readerContextMock);
         $layoutCacheId = 'layout_cache_id';
         $handles = ['default', 'another'];
         /** @var \Magento\Framework\View\Layout\Element $xml */
@@ -807,4 +807,10 @@ class LayoutTest extends \PHPUnit_Framework_TestCase
 
         $this->model->generateElements();
     }
+
+    public function testGetXml()
+    {
+        $xml = '<layout/>';
+        $this->assertSame($xml, \Magento\Framework\View\Layout::LAYOUT_NODE);
+    }
 }
diff --git a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php
index 79cd652f4b22e85a9abc8299e6d6180f4e6037d0..1bf5137218bf4de0d49039d61593c1730a9d871c 100644
--- a/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php
+++ b/lib/internal/Magento/Framework/Webapi/ServiceOutputProcessor.php
@@ -8,6 +8,7 @@ namespace Magento\Framework\Webapi;
 use Magento\Framework\Api\AbstractExtensibleObject;
 use Magento\Framework\Api\ExtensibleDataObjectConverter;
 use Magento\Framework\Reflection\DataObjectProcessor;
+use Magento\Framework\Reflection\MethodsMap;
 
 /**
  * Data object converter for REST
@@ -19,12 +20,21 @@ class ServiceOutputProcessor
      */
     protected $dataObjectProcessor;
 
+    /**
+     * @var MethodsMap
+     */
+    protected $methodsMapProcessor;
+
     /**
      * @param DataObjectProcessor $dataObjectProcessor
+     * @param MethodsMap $methodsMapProcessor
      */
-    public function __construct(DataObjectProcessor $dataObjectProcessor)
-    {
+    public function __construct(
+        DataObjectProcessor $dataObjectProcessor,
+        MethodsMap $methodsMapProcessor
+    ) {
         $this->dataObjectProcessor = $dataObjectProcessor;
+        $this->methodsMapProcessor = $methodsMapProcessor;
     }
 
     /**
@@ -44,7 +54,7 @@ class ServiceOutputProcessor
     public function process($data, $serviceClassName, $serviceMethodName)
     {
         /** @var string $dataType */
-        $dataType = $this->dataObjectProcessor->getMethodReturnType($serviceClassName, $serviceMethodName);
+        $dataType = $this->methodsMapProcessor->getMethodReturnType($serviceClassName, $serviceMethodName);
         if (is_array($data)) {
             $result = [];
             $arrayElementType = substr($dataType, 0, -2);
diff --git a/setup/src/Magento/Setup/Model/Installer.php b/setup/src/Magento/Setup/Model/Installer.php
index 5db3a546a66113ea49ff556c9f346281ec8afc44..414ef89f37a612ce5dcbbe14f6438d30e11a11a5 100644
--- a/setup/src/Magento/Setup/Model/Installer.php
+++ b/setup/src/Magento/Setup/Model/Installer.php
@@ -787,6 +787,9 @@ class Installer
     public function installUserConfig($data)
     {
         $userConfig = new StoreConfigurationDataMapper();
+        /** @var \Magento\Framework\App\State $appState */
+        $appState = $this->objectManagerProvider->get()->get('Magento\Framework\App\State');
+        $appState->setAreaCode('setup');
         $configData = $userConfig->getConfigData($data);
         if (count($configData) === 0) {
             return;
diff --git a/setup/src/Magento/Setup/Model/Lists.php b/setup/src/Magento/Setup/Model/Lists.php
index e898ed40c16c383ac2fa1ccadbdab2419aae793d..e52e921594caec60421e975f3782c8abf552ac13 100644
--- a/setup/src/Magento/Setup/Model/Lists.php
+++ b/setup/src/Magento/Setup/Model/Lists.php
@@ -10,6 +10,7 @@ use Magento\Framework\Locale\Bundle\CurrencyBundle;
 use Magento\Framework\Locale\Bundle\LanguageBundle;
 use Magento\Framework\Locale\Bundle\RegionBundle;
 use Magento\Framework\Locale\ConfigInterface;
+use Magento\Framework\Locale\Resolver;
 use Magento\Framework\Locale\ResolverInterface;
 
 class Lists
@@ -42,7 +43,7 @@ class Lists
             $list[$code] = \IntlTimeZone::createTimeZone($code)->getDisplayName(
                 false,
                 \IntlTimeZone::DISPLAY_LONG,
-                ResolverInterface::DEFAULT_LOCALE
+                Resolver::DEFAULT_LOCALE
             ) . ' (' . $code . ')';
         }
         asort($list);
@@ -56,7 +57,7 @@ class Lists
      */
     public function getCurrencyList()
     {
-        $currencies = (new CurrencyBundle())->get(ResolverInterface::DEFAULT_LOCALE)['Currencies'];
+        $currencies = (new CurrencyBundle())->get(Resolver::DEFAULT_LOCALE)['Currencies'];
         $list = [];
         foreach ($currencies as $code => $data) {
             $list[$code] = $data[1] . ' (' . $code . ')';
@@ -72,8 +73,8 @@ class Lists
      */
     public function getLocaleList()
     {
-        $languages = (new LanguageBundle())->get(ResolverInterface::DEFAULT_LOCALE)['Languages'];
-        $countries = (new RegionBundle())->get(ResolverInterface::DEFAULT_LOCALE)['Countries'];
+        $languages = (new LanguageBundle())->get(Resolver::DEFAULT_LOCALE)['Languages'];
+        $countries = (new RegionBundle())->get(Resolver::DEFAULT_LOCALE)['Countries'];
         $locales = \ResourceBundle::getLocales(null);
 
         $list = [];
diff --git a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php
index 553c5c8ed4c1ec544a824a240237a1b0eb505dca..e3e44ebd20a3b43114e32d7df801c2c86591bf02 100644
--- a/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php
+++ b/setup/src/Magento/Setup/Test/Unit/Model/InstallerTest.php
@@ -234,12 +234,21 @@ class InstallerTest extends \PHPUnit_Framework_TestCase
         $cacheManager = $this->getMock('Magento\Framework\App\Cache\Manager', [], [], '', false);
         $cacheManager->expects($this->once())->method('getAvailableTypes')->willReturn(['foo', 'bar']);
         $cacheManager->expects($this->once())->method('setEnabled')->willReturn(['foo', 'bar']);
+        $appState = (new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this))->getObject(
+            'Magento\Framework\App\State'
+        );
         $this->objectManager->expects($this->any())
             ->method('create')
             ->will($this->returnValueMap([
                 ['Magento\Setup\Module\Setup', ['resource' => $resource], $setup],
                 ['Magento\Setup\Module\DataSetup', [], $dataSetup],
                 ['Magento\Framework\App\Cache\Manager', [], $cacheManager],
+                ['Magento\Framework\App\State', [], $appState],
+            ]));
+        $this->objectManager->expects($this->any())
+            ->method('get')
+            ->will($this->returnValueMap([
+                ['Magento\Framework\App\State', $appState],
             ]));
         $this->adminFactory->expects($this->once())->method('create')->willReturn(
             $this->getMock('Magento\Setup\Model\AdminAccount', [], [], '', false)