diff --git a/app/code/Magento/Catalog/Api/ProductRepositoryInterface.php b/app/code/Magento/Catalog/Api/ProductRepositoryInterface.php
index 6fddee979e07f89879312fa6ed317acff6acba27..0f9b9c2589fa5683799de3c4589197b7e00a6519 100644
--- a/app/code/Magento/Catalog/Api/ProductRepositoryInterface.php
+++ b/app/code/Magento/Catalog/Api/ProductRepositoryInterface.php
@@ -41,7 +41,7 @@ interface ProductRepositoryInterface
      *
      * @param int $productId
      * @param bool $editMode
-     * @param null|int $storeId
+     * @param int|null $storeId
      * @param bool $forceReload
      * @return \Magento\Catalog\Api\Data\ProductInterface
      * @throws \Magento\Framework\Exception\NoSuchEntityException
diff --git a/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php b/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php
index 161b72ada2c707b3f4e8a938b23f0ec67028a0f7..a498d464eaf9484c3cb488d1bc07ee04d410f46d 100644
--- a/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php
+++ b/app/code/Magento/Newsletter/Test/Unit/Model/Queue/TransportBuilderTest.php
@@ -8,7 +8,7 @@ namespace Magento\Newsletter\Test\Unit\Model\Queue;
 use Magento\Framework\App\TemplateTypesInterface;
 use Magento\Framework\Mail\MessageInterface;
 
-class TransportBuilderTest extends \Magento\Framework\Mail\Test\Unit\Template\TransportBuilderTest
+class TransportBuilderTest extends \PHPUnit_Framework_TestCase
 {
     /**
      * @var string
@@ -20,20 +20,68 @@ class TransportBuilderTest extends \Magento\Framework\Mail\Test\Unit\Template\Tr
      */
     protected $builder;
 
+    /**
+     * @var \Magento\Framework\Mail\Template\FactoryInterface | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $templateFactoryMock;
+
+    /**
+     * @var \Magento\Framework\Mail\Message | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $messageMock;
+
+    /**
+     * @var \Magento\Framework\ObjectManagerInterface | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $objectManagerMock;
+
+    /**
+     * @var \Magento\Framework\Mail\Template\SenderResolverInterface | \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $senderResolverMock;
+
+    /**
+     * @var \PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $mailTransportFactoryMock;
+
+    /**
+     * @return void
+     */
+    public function setUp()
+    {
+        $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+        $this->templateFactoryMock = $this->getMock('Magento\Framework\Mail\Template\FactoryInterface');
+        $this->messageMock = $this->getMock('Magento\Framework\Mail\Message');
+        $this->objectManagerMock = $this->getMock('Magento\Framework\ObjectManagerInterface');
+        $this->senderResolverMock = $this->getMock('Magento\Framework\Mail\Template\SenderResolverInterface');
+        $this->mailTransportFactoryMock = $this->getMockBuilder('Magento\Framework\Mail\TransportInterfaceFactory')
+            ->disableOriginalConstructor()
+            ->setMethods(['create'])
+            ->getMock();
+        $this->builder = $objectManagerHelper->getObject(
+            $this->builderClassName,
+            [
+                'templateFactory' => $this->templateFactoryMock,
+                'message' => $this->messageMock,
+                'objectManager' => $this->objectManagerMock,
+                'senderResolver' => $this->senderResolverMock,
+                'mailTransportFactory' => $this->mailTransportFactoryMock
+            ]
+        );
+    }
+
     /**
      * @param int $templateType
      * @param string $messageType
      * @param string $bodyText
-     * @param string $templateNamespace
      * @return void
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
-     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
     public function testGetTransport(
         $templateType = TemplateTypesInterface::TYPE_HTML,
         $messageType = MessageInterface::TYPE_HTML,
-        $bodyText = '<h1>Html message</h1>',
-        $templateNamespace = ''
+        $bodyText = '<h1>Html message</h1>'
     ) {
         $filter = $this->getMock('Magento\Email\Model\Template\Filter', [], [], '', false);
         $data = [
diff --git a/app/code/Magento/Store/Model/Plugin/StoreCookie.php b/app/code/Magento/Store/Model/Plugin/StoreCookie.php
index cb4ec64e03a2f76f7a7414d6045b86a32a996426..b42e645799867c96372b79e8cdd768dd155d668d 100644
--- a/app/code/Magento/Store/Model/Plugin/StoreCookie.php
+++ b/app/code/Magento/Store/Model/Plugin/StoreCookie.php
@@ -52,29 +52,25 @@ class StoreCookie
      * Delete cookie "store" if the store (a value in the cookie) does not exist or is inactive
      *
      * @param \Magento\Framework\App\FrontController $subject
-     * @param callable $proceed
      * @param \Magento\Framework\App\RequestInterface $request
-     * @return mixed
+     * @return void
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
-    public function aroundDispatch(
+    public function beforeDispatch(
         \Magento\Framework\App\FrontController $subject,
-        \Closure $proceed,
         \Magento\Framework\App\RequestInterface $request
     ) {
-        $defaultStore = $this->storeManager->getDefaultStoreView();
         $storeCodeFromCookie = $this->storeCookieManager->getStoreCodeFromCookie();
         if ($storeCodeFromCookie) {
             try {
                 $this->storeRepository->getActiveStoreByCode($storeCodeFromCookie);
             } catch (StoreIsInactiveException $e) {
-                $this->storeCookieManager->deleteStoreCookie($defaultStore);
+                $this->storeCookieManager->deleteStoreCookie($this->storeManager->getDefaultStoreView());
             } catch (NoSuchEntityException $e) {
-                $this->storeCookieManager->deleteStoreCookie($defaultStore);
+                $this->storeCookieManager->deleteStoreCookie($this->storeManager->getDefaultStoreView());
             } catch (InvalidArgumentException $e) {
-                $this->storeCookieManager->deleteStoreCookie($defaultStore);
+                $this->storeCookieManager->deleteStoreCookie($this->storeManager->getDefaultStoreView());
             }
         }
-        return $proceed($request);
     }
 }
diff --git a/app/code/Magento/Store/Model/StoreResolver.php b/app/code/Magento/Store/Model/StoreResolver.php
index f609e271ffd6c8d11a4c1af2c5fbf99232e41b20..b87a5aabf6f71f6cd60fbe1e7fbf2981e39b4187 100644
--- a/app/code/Magento/Store/Model/StoreResolver.php
+++ b/app/code/Magento/Store/Model/StoreResolver.php
@@ -45,6 +45,11 @@ class StoreResolver implements \Magento\Store\Api\StoreResolverInterface
      */
     protected $scopeCode;
 
+    /*
+     * @var \Magento\Framework\App\RequestInterface
+     */
+    protected $request;
+
     /**
      * @param \Magento\Store\Api\StoreRepositoryInterface $storeRepository
      * @param StoreCookieManagerInterface $storeCookieManager
diff --git a/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php b/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php
index 3b2197494bef8f3406b0e8dbab1da3777db13a2b..43a471fba9ffcad52aad1d60583410b4ffafa735 100644
--- a/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php
+++ b/app/code/Magento/Store/Test/Unit/Model/Plugin/StoreCookieTest.php
@@ -37,11 +37,6 @@ class StoreCookieTest extends \PHPUnit_Framework_TestCase
      */
     protected $storeMock;
 
-    /**
-     * @var \Closure
-     */
-    protected $closureMock;
-
     /**
      * @var \Magento\Framework\App\FrontController|\PHPUnit_Framework_MockObject_MockObject
      */
@@ -77,10 +72,6 @@ class StoreCookieTest extends \PHPUnit_Framework_TestCase
             ->setMethods([])
             ->getMock();
 
-        $this->closureMock = function () {
-            return 'ExpectedValue';
-        };
-
         $this->subjectMock = $this->getMockBuilder('Magento\Framework\App\FrontController')
             ->disableOriginalConstructor()
             ->setMethods([])
@@ -106,7 +97,7 @@ class StoreCookieTest extends \PHPUnit_Framework_TestCase
         );
     }
 
-    public function testAroundDispatchNoSuchEntity()
+    public function testBeforeDispatchNoSuchEntity()
     {
         $storeCode = 'store';
         $this->storeManagerMock->expects($this->once())->method('getDefaultStoreView')->willReturn($this->storeMock);
@@ -115,13 +106,10 @@ class StoreCookieTest extends \PHPUnit_Framework_TestCase
             ->method('getActiveStoreByCode')
             ->willThrowException(new NoSuchEntityException);
         $this->storeCookieManagerMock->expects($this->once())->method('deleteStoreCookie')->with($this->storeMock);
-        $this->assertEquals(
-            'ExpectedValue',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
-    public function testAroundDispatchStoreIsInactive()
+    public function testBeforeDispatchStoreIsInactive()
     {
         $storeCode = 'store';
         $this->storeManagerMock->expects($this->once())->method('getDefaultStoreView')->willReturn($this->storeMock);
@@ -130,13 +118,10 @@ class StoreCookieTest extends \PHPUnit_Framework_TestCase
             ->method('getActiveStoreByCode')
             ->willThrowException(new StoreIsInactiveException);
         $this->storeCookieManagerMock->expects($this->once())->method('deleteStoreCookie')->with($this->storeMock);
-        $this->assertEquals(
-            'ExpectedValue',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
-    public function testAroundDispatchInvalidArgument()
+    public function testBeforeDispatchInvalidArgument()
     {
         $storeCode = 'store';
         $this->storeManagerMock->expects($this->once())->method('getDefaultStoreView')->willReturn($this->storeMock);
@@ -145,22 +130,16 @@ class StoreCookieTest extends \PHPUnit_Framework_TestCase
             ->method('getActiveStoreByCode')
             ->willThrowException(new InvalidArgumentException);
         $this->storeCookieManagerMock->expects($this->once())->method('deleteStoreCookie')->with($this->storeMock);
-        $this->assertEquals(
-            'ExpectedValue',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 
-    public function testAroundDispatchNoStoreCookie()
+    public function testBeforeDispatchNoStoreCookie()
     {
         $storeCode = null;
-        $this->storeManagerMock->expects($this->once())->method('getDefaultStoreView')->willReturn($this->storeMock);
         $this->storeCookieManagerMock->expects($this->once())->method('getStoreCodeFromCookie')->willReturn($storeCode);
+        $this->storeManagerMock->expects($this->never())->method('getDefaultStoreView')->willReturn($this->storeMock);
         $this->storeRepositoryMock->expects($this->never())->method('getActiveStoreByCode');
         $this->storeCookieManagerMock->expects($this->never())->method('deleteStoreCookie')->with($this->storeMock);
-        $this->assertEquals(
-            'ExpectedValue',
-            $this->plugin->aroundDispatch($this->subjectMock, $this->closureMock, $this->requestMock)
-        );
+        $this->plugin->beforeDispatch($this->subjectMock, $this->requestMock);
     }
 }
diff --git a/app/code/Magento/Store/etc/di.xml b/app/code/Magento/Store/etc/di.xml
index b72ef3a7b16761346e825cfc0938d9546ddab832..3b8d21188b28f54174ba36d62b39b475923cc117 100644
--- a/app/code/Magento/Store/etc/di.xml
+++ b/app/code/Magento/Store/etc/di.xml
@@ -281,6 +281,11 @@
         <plugin name="install" type="Magento\Framework\Module\Plugin\DbStatusValidator" sortOrder="40"/>
         <plugin name="storeCookieValidate" type="Magento\Store\Model\Plugin\StoreCookie" sortOrder="10"/>
     </type>
+    <type name="Magento\Store\Model\Plugin\StoreCookie">
+        <arguments>
+            <argument name="storeManager" xsi:type="object">Magento\Store\Model\StoreManagerInterface\Proxy</argument>
+        </arguments>
+    </type>
     <type name="Magento\Framework\Module\Plugin\DbStatusValidator">
         <arguments>
             <argument name="cache" xsi:type="object">Magento\Framework\App\Cache\Type\Config</argument>
diff --git a/app/code/Magento/User/Model/ResourceModel/User.php b/app/code/Magento/User/Model/ResourceModel/User.php
index 1c87be70f24eff99d27db25e2e78f9bd118cf96b..6f782c75f3108ab170c76240fd1383087e5c44e6 100644
--- a/app/code/Magento/User/Model/ResourceModel/User.php
+++ b/app/code/Magento/User/Model/ResourceModel/User.php
@@ -539,7 +539,7 @@ class User extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         $userId = (int)$user->getId();
         $table = $this->getTable('admin_passwords');
 
-        // purge expired passwords, except that should retain
+        // purge expired passwords, except those which should be retained
         $retainPasswordIds = $this->getConnection()->fetchCol(
             $this->getConnection()
                 ->select()
@@ -556,7 +556,7 @@ class User extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
         }
         $this->getConnection()->delete($table, $where);
 
-        // now get all remained passwords
+        // get all remaining passwords
         return $this->getConnection()->fetchCol(
             $this->getConnection()
                 ->select()
diff --git a/app/code/Magento/User/Model/User.php b/app/code/Magento/User/Model/User.php
index b9573b4f76e307a33ea42b8429b411ca6787844e..c5d38e8b98e258d085d6e15a74c4f961ea3cfa3c 100644
--- a/app/code/Magento/User/Model/User.php
+++ b/app/code/Magento/User/Model/User.php
@@ -256,9 +256,9 @@ class User extends AbstractModel implements StorageInterface, UserInterface
     }
 
     /**
-     * Validate customer attribute values.
-     * For existing customer password + confirmation will be validated only when password is set
-     * (i.e. its change is requested)
+     * Validate admin user data.
+     *
+     * Existing user password confirmation will be validated only when password is set
      *
      * @return bool|string[]
      */
@@ -272,8 +272,35 @@ class User extends AbstractModel implements StorageInterface, UserInterface
             return $validator->getMessages();
         }
 
-        return true;
+        return $this->validatePasswordChange();
+    }
+
+    /**
+     * Make sure admin password was changed.
+     *
+     * New password is compared to at least 4 previous passwords to prevent setting them again
+     *
+     * @return bool|string[]
+     */
+    protected function validatePasswordChange()
+    {
+        $password = $this->getPassword();
+        if ($password && !$this->getForceNewPassword() && $this->getId()) {
+            $errorMessage = __('Sorry, but this password has already been used. Please create another.');
+            // Check if password is equal to the current one
+            if ($this->_encryptor->isValidHash($password, $this->getOrigData('password'))) {
+                return [$errorMessage];
+            }
 
+            // Check whether password was used before
+            $passwordHash = $this->_encryptor->getHash($password, false);
+            foreach ($this->getResource()->getOldPasswords($this) as $oldPasswordHash) {
+                if ($passwordHash === $oldPasswordHash) {
+                    return [$errorMessage];
+                }
+            }
+        }
+        return true;
     }
 
     /**
diff --git a/app/code/Magento/User/Observer/Backend/CheckAdminPasswordChangeObserver.php b/app/code/Magento/User/Observer/Backend/CheckAdminPasswordChangeObserver.php
deleted file mode 100644
index 3bf06a441e248c7f0cfd6f676f95322e02bbe76a..0000000000000000000000000000000000000000
--- a/app/code/Magento/User/Observer/Backend/CheckAdminPasswordChangeObserver.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\User\Observer\Backend;
-
-use Magento\Framework\Event\Observer as EventObserver;
-use Magento\Framework\Event\ObserverInterface;
-
-/**
- * User backend observer model for passwords
- */
-class CheckAdminPasswordChangeObserver implements ObserverInterface
-{
-    /**
-     * Admin user resource model
-     *
-     * @var \Magento\User\Model\ResourceModel\User
-     */
-    protected $userResource;
-
-    /**
-     * Encryption model
-     *
-     * @var \Magento\Framework\Encryption\EncryptorInterface
-     */
-    protected $encryptor;
-
-    /**
-     * @param \Magento\User\Model\ResourceModel\User $userResource
-     * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
-     */
-    public function __construct(
-        \Magento\User\Model\ResourceModel\User $userResource,
-        \Magento\Framework\Encryption\EncryptorInterface $encryptor
-    ) {
-        $this->userResource = $userResource;
-        $this->encryptor = $encryptor;
-    }
-
-    /**
-     * Harden admin password change.
-     *
-     * New password must be minimum 7 chars length and include alphanumeric characters
-     * The password is compared to at least last 4 previous passwords to prevent setting them again
-     *
-     * @param EventObserver $observer
-     * @return void
-     * @throws \Magento\Framework\Exception\LocalizedException
-     */
-    public function execute(EventObserver $observer)
-    {
-        /* @var $user \Magento\User\Model\User */
-        $user = $observer->getEvent()->getObject();
-
-        if ($user->getNewPassword()) {
-            $password = $user->getNewPassword();
-        } else {
-            $password = $user->getPassword();
-        }
-
-        if ($password && !$user->getForceNewPassword() && $user->getId()) {
-            if ($this->encryptor->isValidHash($password, $user->getOrigData('password'))) {
-                throw new \Magento\Framework\Exception\LocalizedException(
-                    __('Sorry, but this password has already been used. Please create another.')
-                );
-            }
-
-            // check whether password was used before
-            $passwordHash = $this->encryptor->getHash($password, false);
-            foreach ($this->userResource->getOldPasswords($user) as $oldPasswordHash) {
-                if ($passwordHash === $oldPasswordHash) {
-                    throw new \Magento\Framework\Exception\LocalizedException(
-                        __('Sorry, but this password has already been used. Please create another.')
-                    );
-                }
-            }
-        }
-    }
-}
diff --git a/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php b/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php
index 790d78301f058805b12dcb779619fe1e8dfb4a51..0f33107ac01734c2b861e557b1ec25d0672c4895 100644
--- a/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php
+++ b/app/code/Magento/User/Observer/Backend/TrackAdminNewPasswordObserver.php
@@ -71,7 +71,7 @@ class TrackAdminNewPasswordObserver implements ObserverInterface
     }
 
     /**
-     * Save new admin password
+     * Save current admin password to prevent its usage when changed in the future.
      *
      * @param EventObserver $observer
      * @return void
@@ -81,7 +81,7 @@ class TrackAdminNewPasswordObserver implements ObserverInterface
         /* @var $user \Magento\User\Model\User */
         $user = $observer->getEvent()->getObject();
         if ($user->getId()) {
-            $password = $user->getNewPassword();
+            $password = $user->getCurrentPassword();
             $passwordLifetime = $this->observerConfig->getAdminPasswordLifetime();
             if ($passwordLifetime && $password && !$user->getForceNewPassword()) {
                 $passwordHash = $this->encryptor->getHash($password, false);
diff --git a/app/code/Magento/User/Test/Unit/Model/UserTest.php b/app/code/Magento/User/Test/Unit/Model/UserTest.php
index ed495d41e369d82d6d6e44ce7b4e898d5f94d0c1..d2f89414527de5687a2f5cbd792844ac2b6f03c4 100644
--- a/app/code/Magento/User/Test/Unit/Model/UserTest.php
+++ b/app/code/Magento/User/Test/Unit/Model/UserTest.php
@@ -610,4 +610,104 @@ class UserTest extends \PHPUnit_Framework_TestCase
         $this->userDataMock->expects($this->once())->method('getResetPasswordLinkExpirationPeriod')->willReturn(1);
         $this->assertFalse($this->model->isResetPasswordLinkTokenExpired());
     }
+
+    public function testCheckPasswordChangeEqualToCurrent()
+    {
+        /** @var $validatorMock \Magento\Framework\Validator\DataObject|\PHPUnit_Framework_MockObject_MockObject */
+        $validatorMock = $this->getMockBuilder('Magento\Framework\Validator\DataObject')
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
+        $this->validatorObjectFactoryMock->expects($this->once())->method('create')->willReturn($validatorMock);
+        $this->validationRulesMock->expects($this->once())
+            ->method('addUserInfoRules')
+            ->with($validatorMock);
+        $validatorMock->expects($this->once())->method('isValid')->willReturn(true);
+
+        $newPassword = "NEWmYn3wpassw0rd";
+        $oldPassword = "OLDmYn3wpassw0rd";
+        $this->model->setPassword($newPassword)
+            ->setId(1)
+            ->setOrigData('password', $oldPassword);
+        $this->encryptorMock->expects($this->once())
+            ->method('isValidHash')
+            ->with($newPassword, $oldPassword)
+            ->willReturn(true);
+        $result = $this->model->validate();
+        $this->assertInternalType('array', $result);
+        $this->assertCount(1, $result);
+        $this->assertContains("Sorry, but this password has already been used.", (string)$result[0]);
+    }
+
+    public function testCheckPasswordChangeEqualToPrevious()
+    {
+        /** @var $validatorMock \Magento\Framework\Validator\DataObject|\PHPUnit_Framework_MockObject_MockObject */
+        $validatorMock = $this->getMockBuilder('Magento\Framework\Validator\DataObject')
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
+        $this->validatorObjectFactoryMock->expects($this->once())->method('create')->willReturn($validatorMock);
+        $this->validationRulesMock->expects($this->once())
+            ->method('addUserInfoRules')
+            ->with($validatorMock);
+        $validatorMock->expects($this->once())->method('isValid')->willReturn(true);
+
+        $newPassword = "NEWmYn3wpassw0rd";
+        $newPasswordHash = "new password hash";
+        $oldPassword = "OLDmYn3wpassw0rd";
+        $this->model->setPassword($newPassword)
+            ->setId(1)
+            ->setOrigData('password', $oldPassword);
+        $this->encryptorMock->expects($this->once())
+            ->method('isValidHash')
+            ->with($newPassword, $oldPassword)
+            ->willReturn(false);
+
+        $this->encryptorMock->expects($this->once())
+            ->method('getHash')
+            ->with($newPassword, false)
+            ->willReturn($newPasswordHash);
+
+        $this->resourceMock->expects($this->once())->method('getOldPasswords')->willReturn(['hash1', $newPasswordHash]);
+
+        $result = $this->model->validate();
+        $this->assertInternalType('array', $result);
+        $this->assertCount(1, $result);
+        $this->assertContains("Sorry, but this password has already been used.", (string)$result[0]);
+    }
+
+    public function testCheckPasswordChangeValid()
+    {
+        /** @var $validatorMock \Magento\Framework\Validator\DataObject|\PHPUnit_Framework_MockObject_MockObject */
+        $validatorMock = $this->getMockBuilder('Magento\Framework\Validator\DataObject')
+            ->disableOriginalConstructor()
+            ->setMethods([])
+            ->getMock();
+        $this->validatorObjectFactoryMock->expects($this->once())->method('create')->willReturn($validatorMock);
+        $this->validationRulesMock->expects($this->once())
+            ->method('addUserInfoRules')
+            ->with($validatorMock);
+        $validatorMock->expects($this->once())->method('isValid')->willReturn(true);
+
+        $newPassword = "NEWmYn3wpassw0rd";
+        $newPasswordHash = "new password hash";
+        $oldPassword = "OLDmYn3wpassw0rd";
+        $this->model->setPassword($newPassword)
+            ->setId(1)
+            ->setOrigData('password', $oldPassword);
+        $this->encryptorMock->expects($this->once())
+            ->method('isValidHash')
+            ->with($newPassword, $oldPassword)
+            ->willReturn(false);
+
+        $this->encryptorMock->expects($this->once())
+            ->method('getHash')
+            ->with($newPassword, false)
+            ->willReturn($newPasswordHash);
+
+        $this->resourceMock->expects($this->once())->method('getOldPasswords')->willReturn(['hash1', 'hash2']);
+
+        $result = $this->model->validate();
+        $this->assertTrue($result);
+    }
 }
diff --git a/app/code/Magento/User/Test/Unit/Observer/Backend/CheckAdminPasswordChangeObserverTest.php b/app/code/Magento/User/Test/Unit/Observer/Backend/CheckAdminPasswordChangeObserverTest.php
deleted file mode 100644
index a4b9b37edbfa869409635d9bdc8627851c82a8f5..0000000000000000000000000000000000000000
--- a/app/code/Magento/User/Test/Unit/Observer/Backend/CheckAdminPasswordChangeObserverTest.php
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-/**
- * Copyright © 2015 Magento. All rights reserved.
- * See COPYING.txt for license details.
- */
-
-namespace Magento\User\Test\Unit\Observer\Backend;
-
-/**
- * Test class for \Magento\User\Observer\Backend\CheckAdminPasswordChangeObserver
- * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
- */
-class CheckAdminPasswordChangeObserverTest extends \PHPUnit_Framework_TestCase
-{
-    /** @var \Magento\User\Model\ResourceModel\User|\PHPUnit_Framework_MockObject_MockObject */
-    protected $userMock;
-
-    /** @var \Magento\Framework\Encryption\EncryptorInterface|\PHPUnit_Framework_MockObject_MockObject */
-    protected $encryptorMock;
-
-    /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */
-    protected $eventManagerMock;
-
-    /** @var \Magento\User\Observer\Backend\CheckAdminPasswordChangeObserver */
-    protected $model;
-
-    public function setUp()
-    {
-        $this->userMock = $this->getMockBuilder('Magento\User\Model\ResourceModel\User')
-            ->disableOriginalConstructor()
-            ->setMethods([])
-            ->getMock();
-
-        $this->encryptorMock = $this->getMockBuilder('\Magento\Framework\Encryption\EncryptorInterface')
-            ->disableOriginalConstructor()
-            ->setMethods([])
-            ->getMock();
-
-        $this->eventManagerMock = $this->getMockBuilder('Magento\Framework\Event\ManagerInterface')
-            ->disableOriginalConstructor()
-            ->setMethods([])
-            ->getMockForAbstractClass();
-
-        $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
-
-        $this->model = $helper->getObject(
-            '\Magento\User\Observer\Backend\CheckAdminPasswordChangeObserver',
-            [
-                'userResource' => $this->userMock,
-                'encryptor' => $this->encryptorMock,
-            ]
-        );
-    }
-
-    public function testCheckAdminPasswordChange()
-    {
-        $newPW = "mYn3wpassw0rd";
-        $uid = 123;
-        /** @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject $eventObserverMock */
-        $eventObserverMock = $this->getMockBuilder('Magento\Framework\Event\Observer')
-            ->disableOriginalConstructor()
-            ->setMethods([])
-            ->getMock();
-
-        /** @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject */
-        $eventMock = $this->getMockBuilder('Magento\Framework\Event')
-            ->disableOriginalConstructor()
-            ->setMethods(['getObject'])
-            ->getMock();
-
-        /** @var \Magento\User\Model\User|\PHPUnit_Framework_MockObject_MockObject $userMock */
-        $userMock = $this->getMockBuilder('Magento\User\Model\User')
-            ->disableOriginalConstructor()
-            ->setMethods(['getId', 'getNewPassword', 'getForceNewPassword'])
-            ->getMock();
-
-        $eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock);
-        $eventMock->expects($this->once())->method('getObject')->willReturn($userMock);
-        $userMock->expects($this->atLeastOnce())->method('getNewPassword')->willReturn($newPW);
-        $userMock->expects($this->once())->method('getForceNewPassword')->willReturn(false);
-        $userMock->expects($this->once())->method('getId')->willReturn($uid);
-        $this->encryptorMock->expects($this->once())->method('isValidHash')->willReturn(false);
-        $this->encryptorMock->expects($this->once())->method('getHash')->willReturn(md5($newPW));
-        $this->userMock->method('getOldPasswords')->willReturn([md5('pw1'), md5('pw2')]);
-
-        $this->model->execute($eventObserverMock);
-    }
-
-    public function testCheckAdminPasswordChangeThrowsLocalizedExp()
-    {
-        $newPW = "mYn3wpassw0rd";
-        $uid = 123;
-        /** @var \Magento\Framework\Event\Observer|\PHPUnit_Framework_MockObject_MockObject $eventObserverMock */
-        $eventObserverMock = $this->getMockBuilder('Magento\Framework\Event\Observer')
-            ->disableOriginalConstructor()
-            ->setMethods([])
-            ->getMock();
-
-        /** @var \Magento\Framework\Event|\PHPUnit_Framework_MockObject_MockObject */
-        $eventMock = $this->getMockBuilder('Magento\Framework\Event')
-            ->disableOriginalConstructor()
-            ->setMethods(['getObject'])
-            ->getMock();
-
-        /** @var \Magento\User\Model\User|\PHPUnit_Framework_MockObject_MockObject $userMock */
-        $userMock = $this->getMockBuilder('Magento\User\Model\User')
-            ->disableOriginalConstructor()
-            ->setMethods(['getId', 'getNewPassword', 'getForceNewPassword'])
-            ->getMock();
-
-        $eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock);
-        $eventMock->expects($this->once())->method('getObject')->willReturn($userMock);
-        $userMock->expects($this->atLeastOnce())->method('getNewPassword')->willReturn($newPW);
-        $userMock->expects($this->once())->method('getForceNewPassword')->willReturn(false);
-        $userMock->expects($this->once())->method('getId')->willReturn($uid);
-        $this->encryptorMock->expects($this->once())->method('isValidHash')->willReturn(true);
-        $this->userMock->method('getOldPasswords')->willReturn([md5('pw1'), md5('pw2')]);
-
-        try {
-            $this->model->execute($eventObserverMock);
-        } catch (\Magento\Framework\Exception\LocalizedException $expected) {
-            return;
-        }
-        $this->fail('An expected exception has not been raised.');
-    }
-}
diff --git a/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php b/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php
index eb1b930771dd3b3da85301725f3def49fdda5412..d6dc5ddde8dc6f422cb00b79ad439f0282402d3e 100644
--- a/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php
+++ b/app/code/Magento/User/Test/Unit/Observer/Backend/TrackAdminNewPasswordObserverTest.php
@@ -108,13 +108,13 @@ class TrackAdminNewPasswordObserverTest extends \PHPUnit_Framework_TestCase
         /** @var \Magento\User\Model\User|\PHPUnit_Framework_MockObject_MockObject $userMock */
         $userMock = $this->getMockBuilder('Magento\User\Model\User')
             ->disableOriginalConstructor()
-            ->setMethods(['getId', 'getNewPassword', 'getForceNewPassword'])
+            ->setMethods(['getId', 'getCurrentPassword', 'getForceNewPassword'])
             ->getMock();
 
         $eventObserverMock->expects($this->once())->method('getEvent')->willReturn($eventMock);
         $eventMock->expects($this->once())->method('getObject')->willReturn($userMock);
         $userMock->expects($this->once())->method('getId')->willReturn($uid);
-        $userMock->expects($this->once())->method('getNewPassword')->willReturn($newPW);
+        $userMock->expects($this->once())->method('getCurrentPassword')->willReturn($newPW);
         $this->configInterfaceMock
             ->expects($this->atLeastOnce())
             ->method('getValue')
diff --git a/app/code/Magento/User/etc/adminhtml/events.xml b/app/code/Magento/User/etc/adminhtml/events.xml
index 1bcdab99c766788a551a8120887bb73ff35fc417..469c19b9c1b256a06609943bb5d83c4f5f122242 100755
--- a/app/code/Magento/User/etc/adminhtml/events.xml
+++ b/app/code/Magento/User/etc/adminhtml/events.xml
@@ -12,9 +12,6 @@
     <event name="controller_action_predispatch">
         <observer name="magento_user" instance="Magento\User\Observer\Backend\ForceAdminPasswordChangeObserver" />
     </event>
-    <event name="admin_user_save_before">
-        <observer name="magento_user" instance="Magento\User\Observer\Backend\CheckAdminPasswordChangeObserver" />
-    </event>
     <event name="admin_user_save_after">
         <observer name="magento_user" instance="Magento\User\Observer\Backend\TrackAdminNewPasswordObserver" />
     </event>
diff --git a/app/code/Magento/Webapi/Model/Config/ClassReflector.php b/app/code/Magento/Webapi/Model/Config/ClassReflector.php
index 2bd5ac75d0fc73cfe4b146445e5dfbd995fa0370..7aa85ed2732c9340e9294ea798e70c2e3b14b733 100644
--- a/app/code/Magento/Webapi/Model/Config/ClassReflector.php
+++ b/app/code/Magento/Webapi/Model/Config/ClassReflector.php
@@ -98,7 +98,7 @@ class ClassReflector
             $methodData['interface']['in']['parameters'][$parameter->getName()] = $parameterData;
         }
         $returnType = $this->_typeProcessor->getGetterReturnType($method);
-        if ($returnType != 'void' && $returnType != 'null') {
+        if ($returnType['type'] != 'void' && $returnType['type'] != 'null') {
             $methodData['interface']['out']['parameters']['result'] = [
                 'type' => $this->_typeProcessor->register($returnType['type']),
                 'documentation' => $returnType['description'],
diff --git a/app/etc/di.xml b/app/etc/di.xml
index 256a424f14f9354fb1bc16b17dba2f105a942c64..bb0f0981894fdbdce86982fc0f45ecd073c40b10 100755
--- a/app/etc/di.xml
+++ b/app/etc/di.xml
@@ -128,7 +128,6 @@
     <preference for="Magento\Framework\Locale\FormatInterface" type="Magento\Framework\Locale\Format" />
     <preference for="Magento\Framework\Locale\ResolverInterface" type="Magento\Framework\Locale\Resolver" />
     <preference for="Magento\Framework\Stdlib\DateTime\TimezoneInterface" type="Magento\Framework\Stdlib\DateTime\Timezone" />
-
     <preference for="Magento\Framework\Communication\ConfigInterface" type="Magento\Framework\Communication\Config" />
     <preference for="Magento\Framework\Module\ResourceInterface" type="Magento\Framework\Module\ModuleResource" />
     <preference for="Magento\Framework\Pricing\Amount\AmountInterface" type="Magento\Framework\Pricing\Amount\Base" />
@@ -155,6 +154,20 @@
             <argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
         </arguments>
     </type>
+    <type name="Magento\Framework\Communication\Config\CompositeReader">
+        <arguments>
+            <argument name="readers" xsi:type="array">
+                <item name="xmlReader" xsi:type="array">
+                    <item name="reader" xsi:type="object">Magento\Framework\Communication\Config\Reader\XmlReader</item>
+                    <item name="sortOrder" xsi:type="string">10</item>
+                </item>
+                <item name="envReader" xsi:type="array">
+                    <item name="reader" xsi:type="object">Magento\Framework\Communication\Config\Reader\EnvReader</item>
+                    <item name="sortOrder" xsi:type="string">20</item>
+                </item>
+            </argument>
+        </arguments>
+    </type>
     <type name="Magento\Framework\Logger\Monolog">
         <arguments>
             <argument name="name" xsi:type="string">main</argument>
diff --git a/dev/tests/integration/framework/Magento/TestFramework/Application.php b/dev/tests/integration/framework/Magento/TestFramework/Application.php
index 067a539e0ac1209654c5cc4470123b4e0735a414..b87cea882de1840af576939ed75fca516f75e85f 100644
--- a/dev/tests/integration/framework/Magento/TestFramework/Application.php
+++ b/dev/tests/integration/framework/Magento/TestFramework/Application.php
@@ -492,7 +492,7 @@ class Application
     private function copyAppConfigFiles()
     {
         $globalConfigFiles = glob(
-            $this->_globalConfigDir . '/{di.xml,vendor_path.php}',
+            $this->_globalConfigDir . '/{di.xml,*/di.xml,vendor_path.php}',
             GLOB_BRACE
         );
         foreach ($globalConfigFiles as $file) {
diff --git a/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php b/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php
index 8900e66587a5d6d1827b6ae1863370c843854333..064884a54f9a15744193bd2d2fb8c07392463464 100644
--- a/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php
+++ b/dev/tests/integration/testsuite/Magento/Framework/Communication/ConfigTest.php
@@ -52,15 +52,6 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
         $this->getConfigInstance(__DIR__ . '/_files/communication_missing_request.xml')->getTopics();
     }
 
-    /**
-     * @expectedException \LogicException
-     * @expectedExceptionMessage "handler" element must be declared for topic "customerUpdated", because it has
-     */
-    public function testGetTopicsExceptionMissingHandler()
-    {
-        $this->getConfigInstance(__DIR__ . '/_files/communication_missing_handler.xml')->getTopics();
-    }
-
     /**
      * @expectedException \LogicException
      * @expectedExceptionMessage Service method specified in the definition of topic "customerRetrieved" is not
@@ -304,12 +295,20 @@ class ConfigTest extends \PHPUnit_Framework_TestCase
                 'methodsMap' => $methodsMap
             ]
         );
+        $readersConfig = [
+            'xmlReader' => ['reader' => $xmlReader, 'sortOrder' => 10],
+            'envReader' => ['reader' => $envReader, 'sortOrder' => 20]
+        ];
+        /** @var \Magento\Framework\Communication\Config\CompositeReader $reader */
+        $reader = $objectManager->create(
+            'Magento\Framework\Communication\Config\CompositeReader',
+            ['readers' => $readersConfig]
+        );
         /** @var \Magento\Framework\Communication\Config $config */
         $configData = $objectManager->create(
             'Magento\Framework\Communication\Config\Data',
             [
-                'reader' => $xmlReader,
-                'envReader' => $envReader
+                'reader' => $reader
             ]
         );
         return $objectManager->create(
diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php
index 6b8d0f843ebf415859221edc6083dda378da0135..04984323d775479c91698fbed729871fd2d93e2c 100755
--- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php
+++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/obsolete_classes.php
@@ -3879,6 +3879,7 @@ return [
         'Magento\Framework\Component\ComponentRegistrar'
     ],
     ['Magento\Framework\App\Router\ActionList\Reader'],
+    ['Magento\User\Observer\Backend\CheckAdminPasswordChangeObserver'],
     ['Magento\Framework\View\File\AbstractCollector'],
     ['Magento\Tools\Migration\Acl\FileManager'],
     ['Magento\Tools\Migration\Acl\Formatter'],
diff --git a/lib/internal/Magento/Framework/App/ObjectManager/Environment/Compiled.php b/lib/internal/Magento/Framework/App/ObjectManager/Environment/Compiled.php
index 6718dc4d11830d54741acc73a1788d8482933b70..ee02fd5afcc941db5173e227c89fb330c55873d8 100644
--- a/lib/internal/Magento/Framework/App/ObjectManager/Environment/Compiled.php
+++ b/lib/internal/Magento/Framework/App/ObjectManager/Environment/Compiled.php
@@ -73,7 +73,7 @@ class Compiled extends AbstractEnvironment implements EnvironmentInterface
      */
     protected function getConfigData()
     {
-        $this->getObjectManagerConfigLoader()->load(Area::AREA_GLOBAL);
+        return $this->getObjectManagerConfigLoader()->load(Area::AREA_GLOBAL);
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/App/ObjectManagerFactory.php b/lib/internal/Magento/Framework/App/ObjectManagerFactory.php
index 0d482bf1599dd334bc15bffd0a5cfe7f42b6167d..a9f0b69e2f078e9f77f3c5535baf60b80225f9e0 100644
--- a/lib/internal/Magento/Framework/App/ObjectManagerFactory.php
+++ b/lib/internal/Magento/Framework/App/ObjectManagerFactory.php
@@ -120,10 +120,10 @@ class ObjectManagerFactory
         $definitions = $definitionFactory->createClassDefinition($deploymentConfig->get('definitions'));
         $relations = $definitionFactory->createRelations();
 
-        /** @var EnvironmentFactory $enFactory */
-        $enFactory = new $this->envFactoryClassName($relations, $definitions);
+        /** @var EnvironmentFactory $envFactory */
+        $envFactory = new $this->envFactoryClassName($relations, $definitions);
         /** @var EnvironmentInterface $env */
-        $env =  $enFactory->createEnvironment();
+        $env =  $envFactory->createEnvironment();
 
         /** @var ConfigInterface $diConfig */
         $diConfig = $env->getDiConfig();
@@ -176,7 +176,15 @@ class ObjectManagerFactory
 
         $this->factory->setObjectManager($objectManager);
         ObjectManager::setInstance($objectManager);
-        $definitionFactory->getCodeGenerator()->setObjectManager($objectManager);
+
+        $generatorParams = $diConfig->getArguments('Magento\Framework\Code\Generator');
+        /** Arguments are stored in different format when DI config is compiled, thus require custom processing */
+        $generatedEntities = isset($generatorParams['generatedEntities']['_v_'])
+            ? $generatorParams['generatedEntities']['_v_']
+            : (isset($generatorParams['generatedEntities']) ? $generatorParams['generatedEntities'] : []);
+        $definitionFactory->getCodeGenerator()
+            ->setObjectManager($objectManager)
+            ->setGeneratedEntities($generatedEntities);
 
         $env->configureObjectManager($diConfig, $sharedInstances);
 
diff --git a/lib/internal/Magento/Framework/Code/Generator.php b/lib/internal/Magento/Framework/Code/Generator.php
index 2a572346e6a60bf2cb149a12f9c2a53d00e0b11d..92057208769f736643f05e8913ea973e8ec66f1f 100644
--- a/lib/internal/Magento/Framework/Code/Generator.php
+++ b/lib/internal/Magento/Framework/Code/Generator.php
@@ -22,7 +22,7 @@ class Generator
     protected $_ioObject;
 
     /**
-     * @var string[] of EntityAbstract classes
+     * @var array
      */
     protected $_generatedEntities;
 
@@ -57,13 +57,25 @@ class Generator
     /**
      * Get generated entities
      *
-     * @return string[]
+     * @return array
      */
     public function getGeneratedEntities()
     {
         return $this->_generatedEntities;
     }
 
+    /**
+     * Set entity-to-generator map
+     *
+     * @param array $generatedEntities
+     * @return $this
+     */
+    public function setGeneratedEntities($generatedEntities)
+    {
+        $this->_generatedEntities = $generatedEntities;
+        return $this;
+    }
+
     /**
      * Generate Class
      *
diff --git a/lib/internal/Magento/Framework/Communication/Config/CompositeReader.php b/lib/internal/Magento/Framework/Communication/Config/CompositeReader.php
new file mode 100644
index 0000000000000000000000000000000000000000..5033b7a7cd2cce6fa7c9e2ae9fa2cb23e0f3760c
--- /dev/null
+++ b/lib/internal/Magento/Framework/Communication/Config/CompositeReader.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Communication\Config;
+
+use Magento\Framework\Config\ReaderInterface;
+
+/**
+ * Composite reader for communication config.
+ */
+class CompositeReader implements ReaderInterface
+{
+    /**
+     * @var ReaderInterface[]
+     */
+    private $readers;
+
+    /**
+     * Initialize dependencies.
+     *
+     * @param array $readers
+     */
+    public function __construct(array $readers)
+    {
+        usort(
+            $readers,
+            function ($firstItem, $secondItem) {
+                if (!isset($firstItem['sortOrder']) || !isset($secondItem['sortOrder'])
+                    || $firstItem['sortOrder'] == $secondItem['sortOrder']
+                ) {
+                    return 0;
+                }
+                return $firstItem['sortOrder'] < $secondItem['sortOrder'] ? -1 : 1;
+            }
+        );
+        $this->readers = [];
+        foreach ($readers as $readerInfo) {
+            if (!isset($readerInfo['reader'])) {
+                continue;
+            }
+            $this->readers[] = $readerInfo['reader'];
+        }
+    }
+
+    /**
+     * Read config.
+     *
+     * @param string|null $scope
+     * @return array
+     */
+    public function read($scope = null)
+    {
+        $result = [];
+        foreach ($this->readers as $reader) {
+            $result = array_replace_recursive($result, $reader->read($scope));
+        }
+        return $result;
+    }
+}
diff --git a/lib/internal/Magento/Framework/Communication/Config/Data.php b/lib/internal/Magento/Framework/Communication/Config/Data.php
index c8d9c560bff225f2b49e397e56d033d7b01a4d3b..fda3ee8aadb301aa8fe9ee63f97479b323ff4d2b 100644
--- a/lib/internal/Magento/Framework/Communication/Config/Data.php
+++ b/lib/internal/Magento/Framework/Communication/Config/Data.php
@@ -13,18 +13,15 @@ class Data extends \Magento\Framework\Config\Data
     /**
      * Initialize dependencies.
      *
-     * @param \Magento\Framework\Communication\Config\Reader\XmlReader $reader
+     * @param \Magento\Framework\Communication\Config\CompositeReader $reader
      * @param \Magento\Framework\Config\CacheInterface $cache
-     * @param \Magento\Framework\Communication\Config\Reader\EnvReader $envReader
      * @param string $cacheId
      */
     public function __construct(
-        \Magento\Framework\Communication\Config\Reader\XmlReader $reader,
+        \Magento\Framework\Communication\Config\CompositeReader $reader,
         \Magento\Framework\Config\CacheInterface $cache,
-        \Magento\Framework\Communication\Config\Reader\EnvReader $envReader,
         $cacheId = 'communication_config_cache'
     ) {
         parent::__construct($reader, $cache, $cacheId);
-        $this->merge($envReader->read());
     }
 }
diff --git a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php b/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php
index 3406614738aad229c12fadffdd96fcc150810d13..7633e660276c5a60557c76a15f0f65a19c0659ae 100644
--- a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php
+++ b/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Converter.php
@@ -7,7 +7,7 @@ namespace Magento\Framework\Communication\Config\Reader\XmlReader;
 
 use Magento\Framework\Communication\ConfigInterface as Config;
 use Magento\Framework\Phrase;
-use Magento\Framework\Reflection\MethodsMap;
+use Magento\Framework\Communication\Config\ReflectionGenerator;
 use Magento\Framework\Stdlib\BooleanUtils;
 use Magento\Framework\Communication\Config\Reader\XmlReader\Validator;
 
@@ -19,9 +19,9 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
     const SERVICE_METHOD_NAME_PATTERN = '/^([a-zA-Z\\\\]+)::([a-zA-Z]+)$/';
 
     /**
-     * @var MethodsMap
+     * @var ReflectionGenerator
      */
-    private $methodsMap;
+    private $reflectionGenerator;
 
     /**
      * @var BooleanUtils
@@ -36,16 +36,16 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
     /**
      * Initialize dependencies
      *
-     * @param MethodsMap $methodsMap
+     * @param ReflectionGenerator $reflectionGenerator
      * @param BooleanUtils $booleanUtils
      * @param Validator $xmlValidator
      */
     public function __construct(
-        MethodsMap $methodsMap,
+        ReflectionGenerator $reflectionGenerator,
         BooleanUtils $booleanUtils,
         Validator $xmlValidator
     ) {
-        $this->methodsMap = $methodsMap;
+        $this->reflectionGenerator = $reflectionGenerator;
         $this->booleanUtils = $booleanUtils;
         $this->xmlValidator = $xmlValidator;
     }
@@ -78,7 +78,13 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
             $topicAttributes = $topicNode->attributes;
             $topicName = $topicAttributes->getNamedItem('name')->nodeValue;
 
-            $requestResponseSchema = $this->extractSchemaDefinedByServiceMethod($topicNode);
+            $serviceMethod = $this->getServiceMethodBySchema($topicNode);
+            $requestResponseSchema = $serviceMethod
+                ? $this->reflectionGenerator->extractMethodMetadata(
+                    $serviceMethod['typeName'],
+                    $serviceMethod['methodName']
+                )
+                : null;
             $requestSchema = $this->extractTopicRequestSchema($topicNode);
             $responseSchema = $this->extractTopicResponseSchema($topicNode);
             $handlers = $this->extractTopicResponseHandlers($topicNode);
@@ -95,16 +101,13 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
                 $requestSchema,
                 $responseSchema
             );
-            if ($requestResponseSchema) {
-                $output[$topicName] = [
-                    Config::TOPIC_NAME => $topicName,
-                    Config::TOPIC_IS_SYNCHRONOUS => true,
-                    Config::TOPIC_REQUEST => $requestResponseSchema[Config::SCHEMA_METHOD_PARAMS],
-                    Config::TOPIC_REQUEST_TYPE => Config::TOPIC_REQUEST_TYPE_METHOD,
-                    Config::TOPIC_RESPONSE => $requestResponseSchema[Config::SCHEMA_METHOD_RETURN_TYPE],
-                    Config::TOPIC_HANDLERS => $handlers
-                        ?: ['defaultHandler' => $requestResponseSchema[Config::SCHEMA_METHOD_HANDLER]]
-                ];
+            if ($serviceMethod) {
+                $output[$topicName] = $this->reflectionGenerator->generateTopicConfigForServiceMethod(
+                    $topicName,
+                    $serviceMethod['typeName'],
+                    $serviceMethod['methodName'],
+                    $handlers
+                );
             } else if ($requestSchema && $responseSchema) {
                 $output[$topicName] = [
                     Config::TOPIC_NAME => $topicName,
@@ -149,11 +152,11 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
                     continue;
                 }
                 $handlerName = $handlerAttributes->getNamedItem('name')->nodeValue;
-                $serviceName = $handlerAttributes->getNamedItem('type')->nodeValue;
+                $serviceType = $handlerAttributes->getNamedItem('type')->nodeValue;
                 $methodName = $handlerAttributes->getNamedItem('method')->nodeValue;
-                $this->xmlValidator->validateResponseHandlersType($serviceName, $methodName, $handlerName, $topicName);
+                $this->xmlValidator->validateResponseHandlersType($serviceType, $methodName, $handlerName, $topicName);
                 $handlerNodes[$handlerName] = [
-                    Config::HANDLER_TYPE => $serviceName,
+                    Config::HANDLER_TYPE => $serviceType,
                     Config::HANDLER_METHOD => $methodName
                 ];
             }
@@ -198,37 +201,19 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
     }
 
     /**
-     * Get message schema defined by service method signature.
+     * Get service class and method specified in schema attribute.
      *
      * @param \DOMNode $topicNode
-     * @return array
+     * @return array|null Contains class name and method name
      */
-    protected function extractSchemaDefinedByServiceMethod($topicNode)
+    protected function getServiceMethodBySchema($topicNode)
     {
         $topicAttributes = $topicNode->attributes;
         if (!$topicAttributes->getNamedItem('schema')) {
             return null;
         }
         $topicName = $topicAttributes->getNamedItem('name')->nodeValue;
-        list($className, $methodName) = $this->parseServiceMethod(
-            $topicAttributes->getNamedItem('schema')->nodeValue,
-            $topicName
-        );
-        $result = [
-            Config::SCHEMA_METHOD_PARAMS => [],
-            Config::SCHEMA_METHOD_RETURN_TYPE => $this->methodsMap->getMethodReturnType($className, $methodName),
-            Config::SCHEMA_METHOD_HANDLER => [Config::HANDLER_TYPE => $className, Config::HANDLER_METHOD => $methodName]
-        ];
-        $paramsMeta = $this->methodsMap->getMethodParams($className, $methodName);
-        foreach ($paramsMeta as $paramPosition => $paramMeta) {
-            $result[Config::SCHEMA_METHOD_PARAMS][] = [
-                Config::SCHEMA_METHOD_PARAM_NAME => $paramMeta[MethodsMap::METHOD_META_NAME],
-                Config::SCHEMA_METHOD_PARAM_POSITION => $paramPosition,
-                Config::SCHEMA_METHOD_PARAM_IS_REQUIRED => !$paramMeta[MethodsMap::METHOD_META_HAS_DEFAULT_VALUE],
-                Config::SCHEMA_METHOD_PARAM_TYPE => $paramMeta[MethodsMap::METHOD_META_TYPE],
-            ];
-        }
-        return $result;
+        return $this->parseServiceMethod($topicAttributes->getNamedItem('schema')->nodeValue, $topicName);
     }
 
     /**
@@ -236,7 +221,7 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
      *
      * @param string $serviceMethod
      * @param string $topicName
-     * @return string[] Contains class name and method name, in a call-back compatible format
+     * @return array Contains class name and method name
      */
     protected function parseServiceMethod($serviceMethod, $topicName)
     {
@@ -244,6 +229,6 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
         $className = $matches[1];
         $methodName = $matches[2];
         $this->xmlValidator->validateServiceMethod($serviceMethod, $topicName, $className, $methodName);
-        return [$className, $methodName];
+        return ['typeName' => $className, 'methodName' => $methodName];
     }
 }
diff --git a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Validator.php b/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Validator.php
index c3a61bd3f8bc7e17ea8da76c08704e612eabe682..086b70c7e5e48cac5f0456be259742a6aa2bc780 100644
--- a/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Validator.php
+++ b/lib/internal/Magento/Framework/Communication/Config/Reader/XmlReader/Validator.php
@@ -98,14 +98,6 @@ class Validator extends ConfigValidator
                 )
             );
         }
-        if ($responseSchema && !$handlers) {
-            throw new \LogicException(
-                sprintf(
-                    '"handler" element must be declared for topic "%s", because it has "response" declared',
-                    $topicName
-                )
-            );
-        }
         if (($requestResponseSchema || $responseSchema) && (count($handlers) >= 2)) {
             throw new \LogicException(
                 sprintf(
diff --git a/lib/internal/Magento/Framework/Communication/Config/ReflectionGenerator.php b/lib/internal/Magento/Framework/Communication/Config/ReflectionGenerator.php
new file mode 100644
index 0000000000000000000000000000000000000000..51d99d6a72b69c238c55fa04764403311455ff4d
--- /dev/null
+++ b/lib/internal/Magento/Framework/Communication/Config/ReflectionGenerator.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Copyright © 2015 Magento. All rights reserved.
+ * See COPYING.txt for license details.
+ */
+namespace Magento\Framework\Communication\Config;
+
+use Magento\Framework\Communication\ConfigInterface as Config;
+use Magento\Framework\Reflection\MethodsMap;
+
+/**
+ * Communication config generator based on service methods reflection
+ */
+class ReflectionGenerator
+{
+    const DEFAULT_HANDLER = 'defaultHandler';
+    /**
+     * @var MethodsMap
+     */
+    private $methodsMap;
+
+    /**
+     * Initialize dependencies
+     *
+     * @param MethodsMap $methodsMap
+     */
+    public function __construct(MethodsMap $methodsMap)
+    {
+        $this->methodsMap = $methodsMap;
+    }
+
+    /**
+     * Extract service method metadata.
+     *
+     * @param string $className
+     * @param string $methodName
+     * @return array
+     */
+    public function extractMethodMetadata($className, $methodName)
+    {
+        $result = [
+            Config::SCHEMA_METHOD_PARAMS => [],
+            Config::SCHEMA_METHOD_RETURN_TYPE => $this->methodsMap->getMethodReturnType($className, $methodName),
+            Config::SCHEMA_METHOD_HANDLER => [Config::HANDLER_TYPE => $className, Config::HANDLER_METHOD => $methodName]
+        ];
+        $paramsMeta = $this->methodsMap->getMethodParams($className, $methodName);
+        foreach ($paramsMeta as $paramPosition => $paramMeta) {
+            $result[Config::SCHEMA_METHOD_PARAMS][] = [
+                Config::SCHEMA_METHOD_PARAM_NAME => $paramMeta[MethodsMap::METHOD_META_NAME],
+                Config::SCHEMA_METHOD_PARAM_POSITION => $paramPosition,
+                Config::SCHEMA_METHOD_PARAM_IS_REQUIRED => !$paramMeta[MethodsMap::METHOD_META_HAS_DEFAULT_VALUE],
+                Config::SCHEMA_METHOD_PARAM_TYPE => $paramMeta[MethodsMap::METHOD_META_TYPE],
+            ];
+        }
+        return $result;
+    }
+
+    /**
+     * Generate config data based on service method signature.
+     *
+     * @param string $topicName
+     * @param string $serviceType
+     * @param string $serviceMethod
+     * @param array|null $handlers
+     * @return array
+     */
+    public function generateTopicConfigForServiceMethod($topicName, $serviceType, $serviceMethod, $handlers = [])
+    {
+        $methodMetadata = $this->extractMethodMetadata($serviceType, $serviceMethod);
+        $returnType = $methodMetadata[Config::SCHEMA_METHOD_RETURN_TYPE];
+        $returnType = ($returnType != 'void' && $returnType != 'null') ? $returnType : null;
+        return [
+            Config::TOPIC_NAME => $topicName,
+            Config::TOPIC_IS_SYNCHRONOUS => $returnType ? true : false,
+            Config::TOPIC_REQUEST => $methodMetadata[Config::SCHEMA_METHOD_PARAMS],
+            Config::TOPIC_REQUEST_TYPE => Config::TOPIC_REQUEST_TYPE_METHOD,
+            Config::TOPIC_RESPONSE => $returnType,
+            Config::TOPIC_HANDLERS => $handlers
+                ?: [self::DEFAULT_HANDLER => $methodMetadata[Config::SCHEMA_METHOD_HANDLER]]
+        ];
+    }
+}
diff --git a/lib/internal/Magento/Framework/Data/Form/FormKey.php b/lib/internal/Magento/Framework/Data/Form/FormKey.php
index 6a5485d7bb4f2e97694443c799d3fa80aa5fd9fb..55d841ff3519450548e8739f381d1194822e97d6 100644
--- a/lib/internal/Magento/Framework/Data/Form/FormKey.php
+++ b/lib/internal/Magento/Framework/Data/Form/FormKey.php
@@ -22,16 +22,24 @@ class FormKey
      */
     protected $session;
 
+    /**
+     * @var \Magento\Framework\Escaper
+     */
+    protected $escaper;
+
     /**
      * @param \Magento\Framework\Math\Random $mathRandom
      * @param \Magento\Framework\Session\SessionManagerInterface $session
+     * @param \Magento\Framework\Escaper $escaper
      */
     public function __construct(
         \Magento\Framework\Math\Random $mathRandom,
-        \Magento\Framework\Session\SessionManagerInterface $session
+        \Magento\Framework\Session\SessionManagerInterface $session,
+        \Magento\Framework\Escaper $escaper
     ) {
         $this->mathRandom = $mathRandom;
         $this->session = $session;
+        $this->escaper = $escaper;
     }
 
     /**
@@ -44,7 +52,7 @@ class FormKey
         if (!$this->isPresent()) {
             $this->set($this->mathRandom->getRandomString(16));
         }
-        return $this->session->getData(self::FORM_KEY);
+        return $this->escaper->escapeHtmlAttr($this->session->getData(self::FORM_KEY));
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php
index 5adb36c4ccef7566d659ace1c9891e3846ee9e7c..de92b3b911c69d17ba11689b0ce0862dadb6361b 100644
--- a/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php
+++ b/lib/internal/Magento/Framework/Data/Test/Unit/Form/FormKeyTest.php
@@ -22,6 +22,11 @@ class FormKeyTest extends \PHPUnit_Framework_TestCase
      */
     protected $sessionMock;
 
+    /**
+     * @var \Zend\Escaper\Escaper|\PHPUnit_Framework_MockObject_MockObject
+     */
+    protected $escaperMock;
+
     /**
      * @var FormKey
      */
@@ -32,9 +37,12 @@ class FormKeyTest extends \PHPUnit_Framework_TestCase
         $this->mathRandomMock = $this->getMock('Magento\Framework\Math\Random', [], [], '', false);
         $methods = ['setData', 'getData'];
         $this->sessionMock = $this->getMock('Magento\Framework\Session\SessionManager', $methods, [], '', false);
+        $this->escaperMock = $this->getMock('Magento\Framework\Escaper', [], [], '', false);
+        $this->escaperMock->expects($this->any())->method('escapeHtmlAttr')->willReturnArgument(0);
         $this->formKey = new FormKey(
             $this->mathRandomMock,
-            $this->sessionMock
+            $this->sessionMock,
+            $this->escaperMock
         );
     }
 
diff --git a/lib/internal/Magento/Framework/Escaper.php b/lib/internal/Magento/Framework/Escaper.php
index f28374eb0ac61e9e8450b5d313e318d3cb3d25bd..c6792dc93fa89c7e24c880634156d3691b5bcbf4 100644
--- a/lib/internal/Magento/Framework/Escaper.php
+++ b/lib/internal/Magento/Framework/Escaper.php
@@ -8,7 +8,7 @@ namespace Magento\Framework;
 /**
  * Magento escape methods
  */
-class Escaper
+class Escaper extends \Zend\Escaper\Escaper
 {
     /**
      * Escape html entities
diff --git a/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php b/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php
index 72d30d3373709f369d338855d1c9271db7a3e8b8..9de7bfe1ba66fbec0fd4ae416832cfccb39878e4 100644
--- a/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php
+++ b/lib/internal/Magento/Framework/Filter/Input/MaliciousCode.php
@@ -44,7 +44,11 @@ class MaliciousCode implements \Zend_Filter_Interface
      */
     public function filter($value)
     {
-        return preg_replace($this->_expressions, '', $value);
+        $replaced = 0;
+        do {
+            $value = preg_replace($this->_expressions, '', $value, -1, $replaced);
+        } while ($replaced !== 0);
+        return  $value;
     }
 
     /**
diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php
index dfe0a755397cc9c09c2f4f97630b78a4717499b0..5c6e4be9d83645670a8e204e488b53425b3ecc85 100644
--- a/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php
+++ b/lib/internal/Magento/Framework/Filter/Test/Unit/Input/MaliciousCodeTest.php
@@ -101,6 +101,10 @@ class MaliciousCodeTest extends \PHPUnit_Framework_TestCase
             'Base64' => [
                 '<img alt="Embedded Image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA..." />',
                 '<img alt="Embedded Image" />',
+            ],
+            'Nested malicious tags' => [
+                '<scri<script>pt>alert(1);</scri<script>pt>',
+                'alert(1);',
             ]
         ];
     }
diff --git a/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php b/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php
index 6c2f8db4da1e00e12df1e5ff3d92224f3ba1ed89..37a1896f565c2197f1e333791ecc2c14e2b0412d 100644
--- a/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php
+++ b/lib/internal/Magento/Framework/ObjectManager/DefinitionFactory.php
@@ -177,22 +177,7 @@ class DefinitionFactory
                 $this->_filesystemDriver,
                 $this->_generationDir
             );
-            $this->codeGenerator = new \Magento\Framework\Code\Generator(
-                $generatorIo,
-                [
-                    Generator\Factory::ENTITY_TYPE => '\Magento\Framework\ObjectManager\Code\Generator\Factory',
-                    Generator\Proxy::ENTITY_TYPE => '\Magento\Framework\ObjectManager\Code\Generator\Proxy',
-                    Generator\Repository::ENTITY_TYPE => '\Magento\Framework\ObjectManager\Code\Generator\Repository',
-                    Generator\Persistor::ENTITY_TYPE => '\Magento\Framework\ObjectManager\Code\Generator\Persistor',
-                    InterceptionGenerator\Interceptor::ENTITY_TYPE => '\Magento\Framework\Interception\Code\Generator\Interceptor',
-                    MapperGenerator::ENTITY_TYPE => '\Magento\Framework\Api\Code\Generator\Mapper',
-                    SearchResults::ENTITY_TYPE => '\Magento\Framework\Api\Code\Generator\SearchResults',
-                    ConverterGenerator::ENTITY_TYPE => '\Magento\Framework\ObjectManager\Code\Generator\Converter',
-                    ProfilerGenerator\Logger::ENTITY_TYPE => '\Magento\Framework\ObjectManager\Profiler\Code\Generator\Logger',
-                    ExtensionAttributesGenerator::ENTITY_TYPE => 'Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator',
-                    ExtensionAttributesInterfaceGenerator::ENTITY_TYPE => 'Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator'
-                ]
-            );
+            $this->codeGenerator = new \Magento\Framework\Code\Generator($generatorIo);
         }
         return $this->codeGenerator;
     }
diff --git a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php
index 24e3a990a9677b8b82f4a767fe38b0853fbf4182..a8ce1d6a565d7f1fccdb72f88c5ba64e3de7737b 100644
--- a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php
+++ b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php
@@ -678,11 +678,16 @@ class TypeProcessor
      * @param string $serviceName API service name
      * @param string $methodName
      * @return $this
+     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
      */
     public function processInterfaceCallInfo($interface, $serviceName, $methodName)
     {
         foreach ($interface as $direction => $interfaceData) {
             $direction = ($direction == 'in') ? 'requiredInput' : 'returned';
+            if ($direction == 'returned' && !isset($interfaceData['parameters'])) {
+                /** No return value means that service method is asynchronous */
+                return $this;
+            }
             foreach ($interfaceData['parameters'] as $parameterData) {
                 if (!$this->isTypeSimple($parameterData['type']) && !$this->isTypeAny($parameterData['type'])) {
                     $operation = $this->getOperationName($serviceName, $methodName);
diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
index b9dbc3aebbda25c7b382243de24761cf8ba4310a..711daf317ced7422725116f88a687b1e2c7524f4 100644
--- a/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/DiCompileCommand.php
@@ -94,7 +94,7 @@ class DiCompileCommand extends Command
     {
         $this->setName(self::NAME)
             ->setDescription(
-                'Generates DI configuration and all non-existing interceptors and factories'
+                'Generates DI configuration and all missing classes that can be auto-generated'
             );
         parent::configure();
     }
diff --git a/setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php b/setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php
index fc5eb973944b5d9feff34adce48fd0a67418e7cd..789166966a4f29f61a928cc60876cf9666e491f0 100644
--- a/setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php
+++ b/setup/src/Magento/Setup/Console/Command/DiCompileMultiTenantCommand.php
@@ -30,6 +30,7 @@ use Magento\Setup\Module\Di\Definition\Compressor;
 use Magento\Setup\Module\Di\Definition\Serializer\Igbinary;
 use Magento\Setup\Module\Di\Definition\Serializer\Standard;
 use \Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\Code\Generator as CodeGenerator;
 
 /**
  * Command to generate all non-existing proxies and factories, and pre-compile class definitions,
@@ -87,7 +88,7 @@ class DiCompileMultiTenantCommand extends AbstractSetupCommand
 
     /**
      *
-     * @var \Magento\Framework\Code\Generator
+     * @var CodeGenerator
      */
     private $generator;
 
@@ -274,26 +275,13 @@ class DiCompileMultiTenantCommand extends AbstractSetupCommand
         $interceptorScanner = new Scanner\XmlInterceptorScanner();
         $this->entities['interceptors'] = $interceptorScanner->collectEntities($this->files['di']);
         // 1.2 Generation of Factory and Additional Classes
-        $generatorIo = new \Magento\Framework\Code\Generator\Io(
-            new \Magento\Framework\Filesystem\Driver\File(),
-            $generationDir
+        $generatorIo = $this->objectManager->create(
+            'Magento\Framework\Code\Generator\Io',
+            ['generationDirectory' => $generationDir]
         );
-        $this->generator = new \Magento\Framework\Code\Generator(
-            $generatorIo,
-            [
-                Interceptor::ENTITY_TYPE => 'Magento\Framework\Interception\Code\Generator\Interceptor',
-                Proxy::ENTITY_TYPE => 'Magento\Framework\ObjectManager\Code\Generator\Proxy',
-                Factory::ENTITY_TYPE => 'Magento\Framework\ObjectManager\Code\Generator\Factory',
-                Mapper::ENTITY_TYPE => 'Magento\Framework\Api\Code\Generator\Mapper',
-                Persistor::ENTITY_TYPE => 'Magento\Framework\ObjectManager\Code\Generator\Persistor',
-                Repository::ENTITY_TYPE => 'Magento\Framework\ObjectManager\Code\Generator\Repository',
-                Converter::ENTITY_TYPE => 'Magento\Framework\ObjectManager\Code\Generator\Converter',
-                SearchResults::ENTITY_TYPE => 'Magento\Framework\Api\Code\Generator\SearchResults',
-                ExtensionAttributesInterfaceGenerator::ENTITY_TYPE =>
-                    'Magento\Framework\Api\Code\Generator\ExtensionAttributesInterfaceGenerator',
-                ExtensionAttributesGenerator::ENTITY_TYPE =>
-                    'Magento\Framework\Api\Code\Generator\ExtensionAttributesGenerator'
-            ]
+        $this->generator = $this->objectManager->create(
+            'Magento\Framework\Code\Generator',
+            ['ioObject' => $generatorIo]
         );
         /** Initialize object manager for code generation based on configs */
         $this->generator->setObjectManager($this->objectManager);
@@ -302,13 +290,13 @@ class DiCompileMultiTenantCommand extends AbstractSetupCommand
 
         foreach ($repositories as $entityName) {
             switch ($this->generator->generateClass($entityName)) {
-                case \Magento\Framework\Code\Generator::GENERATION_SUCCESS:
+                case CodeGenerator::GENERATION_SUCCESS:
                     $this->log->add(Log::GENERATION_SUCCESS, $entityName);
                     break;
-                case \Magento\Framework\Code\Generator::GENERATION_ERROR:
+                case CodeGenerator::GENERATION_ERROR:
                     $this->log->add(Log::GENERATION_ERROR, $entityName);
                     break;
-                case \Magento\Framework\Code\Generator::GENERATION_SKIP:
+                case CodeGenerator::GENERATION_SKIP:
                 default:
                     //no log
                     break;
@@ -318,13 +306,13 @@ class DiCompileMultiTenantCommand extends AbstractSetupCommand
             sort($this->entities[$type]);
             foreach ($this->entities[$type] as $entityName) {
                 switch ($this->generator->generateClass($entityName)) {
-                    case \Magento\Framework\Code\Generator::GENERATION_SUCCESS:
+                    case CodeGenerator::GENERATION_SUCCESS:
                         $this->log->add(Log::GENERATION_SUCCESS, $entityName);
                         break;
-                    case \Magento\Framework\Code\Generator::GENERATION_ERROR:
+                    case CodeGenerator::GENERATION_ERROR:
                         $this->log->add(Log::GENERATION_ERROR, $entityName);
                         break;
-                    case \Magento\Framework\Code\Generator::GENERATION_SKIP:
+                    case CodeGenerator::GENERATION_SKIP:
                     default:
                         //no log
                         break;
@@ -388,13 +376,13 @@ class DiCompileMultiTenantCommand extends AbstractSetupCommand
         foreach (['interceptors', 'di'] as $type) {
             foreach ($this->entities[$type] as $entityName) {
                 switch ($this->generator->generateClass($entityName)) {
-                    case \Magento\Framework\Code\Generator::GENERATION_SUCCESS:
+                    case CodeGenerator::GENERATION_SUCCESS:
                         $this->log->add(Log::GENERATION_SUCCESS, $entityName);
                         break;
-                    case \Magento\Framework\Code\Generator::GENERATION_ERROR:
+                    case CodeGenerator::GENERATION_ERROR:
                         $this->log->add(Log::GENERATION_ERROR, $entityName);
                         break;
-                    case \Magento\Framework\Code\Generator::GENERATION_SKIP:
+                    case CodeGenerator::GENERATION_SKIP:
                     default:
                         //no log
                         break;