1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "engine/engine_builder.h"
31 
32 #include <memory>
33 #include <utility>
34 
35 #include "base/file_util.h"
36 #include "base/logging.h"
37 #include "base/thread.h"
38 #include "data_manager/data_manager.h"
39 #include "engine/engine.h"
40 #include "protocol/engine_builder.pb.h"
41 
42 namespace mozc {
43 namespace {
44 
ConvertStatus(DataManager::Status status)45 EngineReloadResponse::Status ConvertStatus(DataManager::Status status) {
46   switch (status) {
47     case DataManager::Status::ENGINE_VERSION_MISMATCH:
48       return EngineReloadResponse::ENGINE_VERSION_MISMATCH;
49     case DataManager::Status::DATA_MISSING:
50       return EngineReloadResponse::DATA_MISSING;
51     case DataManager::Status::DATA_BROKEN:
52       return EngineReloadResponse::DATA_BROKEN;
53     case DataManager::Status::MMAP_FAILURE:
54       return EngineReloadResponse::MMAP_FAILURE;
55     case DataManager::Status::UNKNOWN:
56       return EngineReloadResponse::UNKNOWN_ERROR;
57     default:
58       LOG(DFATAL) << "Should not reach here";
59   }
60   return EngineReloadResponse::UNKNOWN_ERROR;
61 }
62 
63 }  // namespace
64 
65 class EngineBuilder::Preparator : public Thread {
66  public:
Preparator(const EngineReloadRequest & request)67   explicit Preparator(const EngineReloadRequest &request) {
68     *response_.mutable_request() = request;
69   }
70 
71   ~Preparator() override = default;
72 
Run()73   void Run() override {
74     const EngineReloadRequest &request = response_.request();
75 
76     std::unique_ptr<DataManager> tmp_data_manager(new DataManager());
77     const DataManager::Status status = InitDataManager(request,
78                                                        tmp_data_manager.get());
79     if (status != DataManager::Status::OK) {
80       LOG(ERROR) << "Failed to load data [" << status << "] "
81                  << request.Utf8DebugString();
82       response_.set_status(ConvertStatus(status));
83       return;
84     }
85 
86     if (request.has_install_location() &&
87         !FileUtil::AtomicRename(request.file_path(),
88                                 request.install_location())) {
89       LOG(ERROR) << "Atomic rename faild: " << request.Utf8DebugString();
90       response_.set_status(EngineReloadResponse::INSTALL_FAILURE);
91       return;
92     }
93 
94     response_.set_status(EngineReloadResponse::RELOAD_READY);
95     data_manager_ = std::move(tmp_data_manager);
96   }
97 
98  private:
InitDataManager(const EngineReloadRequest & request,DataManager * data_manager)99   DataManager::Status InitDataManager(const EngineReloadRequest &request,
100                                       DataManager *data_manager) {
101     if (request.has_magic_number()) {
102       return data_manager->InitFromFile(request.file_path(),
103                                         request.magic_number());
104     }
105     return data_manager->InitFromFile(request.file_path());
106   }
107 
108   friend class EngineBuilder;
109   EngineReloadResponse response_;
110   std::unique_ptr<DataManager> data_manager_;
111 };
112 
113 EngineBuilder::EngineBuilder() = default;
114 EngineBuilder::~EngineBuilder() = default;
115 
PrepareAsync(const EngineReloadRequest & request,EngineReloadResponse * response)116 void EngineBuilder::PrepareAsync(const EngineReloadRequest &request,
117                                  EngineReloadResponse *response) {
118   *response->mutable_request() = request;
119   if (preparator_) {
120     if (preparator_->IsRunning()) {
121       response->set_status(EngineReloadResponse::ALREADY_RUNNING);
122       return;
123     }
124     preparator_->Join();
125     VLOG(1) << "Previously loaded data is discarded";
126   }
127   preparator_.reset(new Preparator(request));
128   preparator_->SetJoinable(true);
129   preparator_->Start("EngineBuilder::Preparator");
130   response->set_status(EngineReloadResponse::ACCEPTED);
131 }
132 
Wait()133 void EngineBuilder::Wait() {
134   if (preparator_) {
135     preparator_->Join();
136   }
137 }
138 
HasResponse() const139 bool EngineBuilder::HasResponse() const {
140   return preparator_ && !preparator_->IsRunning();
141 }
142 
GetResponse(EngineReloadResponse * response) const143 void EngineBuilder::GetResponse(EngineReloadResponse *response) const {
144   if (!HasResponse()) {
145     return;
146   }
147   *response = preparator_->response_;
148 }
149 
BuildFromPreparedData()150 std::unique_ptr<EngineInterface> EngineBuilder::BuildFromPreparedData() {
151   std::unique_ptr<EngineInterface> engine;
152 
153   if (!HasResponse() ||
154       !preparator_->data_manager_ ||
155       preparator_->response_.status() != EngineReloadResponse::RELOAD_READY) {
156     LOG(ERROR) << "Build() is called in invalid state";
157     return engine;
158   }
159 
160   switch (preparator_->response_.request().engine_type()) {
161     case EngineReloadRequest::DESKTOP:
162       engine =
163           Engine::CreateDesktopEngine(std::move(preparator_->data_manager_));
164       break;
165     case EngineReloadRequest::MOBILE:
166       engine =
167           Engine::CreateMobileEngine(std::move(preparator_->data_manager_));
168       break;
169     default:
170       LOG(DFATAL) << "Should not reach here";
171       break;
172   }
173 
174   return engine;
175 }
176 
Clear()177 void EngineBuilder::Clear() {
178   if (!preparator_) {
179     return;
180   }
181   preparator_->Join();
182   preparator_.reset();
183 }
184 
185 }  // namespace mozc
186