2 * Copyright (C) 2012-2015 Bonitasoft S.A.
3 * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2.0 of the License, or
7 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 package org.bonitasoft.studio.engine;
17 import static java.util.Objects.requireNonNull;
19 import java.io.FileNotFoundException;
20 import java.io.IOException;
21 import java.util.HashMap;
23 import java.util.Optional;
25 import org.bonitasoft.engine.api.ApplicationAPI;
26 import org.bonitasoft.engine.api.CommandAPI;
27 import org.bonitasoft.engine.api.IdentityAPI;
28 import org.bonitasoft.engine.api.LoginAPI;
29 import org.bonitasoft.engine.api.PageAPI;
30 import org.bonitasoft.engine.api.PlatformAPI;
31 import org.bonitasoft.engine.api.PlatformAPIAccessor;
32 import org.bonitasoft.engine.api.ProcessAPI;
33 import org.bonitasoft.engine.api.ProfileAPI;
34 import org.bonitasoft.engine.api.TenantAPIAccessor;
35 import org.bonitasoft.engine.api.TenantAdministrationAPI;
36 import org.bonitasoft.engine.exception.BonitaHomeNotSetException;
37 import org.bonitasoft.engine.exception.ServerAPIException;
38 import org.bonitasoft.engine.exception.UnknownAPITypeException;
39 import org.bonitasoft.engine.exception.UpdateException;
40 import org.bonitasoft.engine.platform.LoginException;
41 import org.bonitasoft.engine.platform.PlatformLoginException;
42 import org.bonitasoft.engine.platform.PlatformLogoutException;
43 import org.bonitasoft.engine.session.APISession;
44 import org.bonitasoft.engine.session.PlatformSession;
45 import org.bonitasoft.engine.session.SessionNotFoundException;
46 import org.bonitasoft.studio.common.CommandExecutor;
47 import org.bonitasoft.studio.common.extension.BonitaStudioExtensionRegistryManager;
48 import org.bonitasoft.studio.common.log.BonitaStudioLog;
49 import org.bonitasoft.studio.common.repository.AbstractRepository;
50 import org.bonitasoft.studio.common.repository.RepositoryManager;
51 import org.bonitasoft.studio.common.repository.extension.IEngineAction;
52 import org.bonitasoft.studio.common.repository.model.IRepository;
53 import org.bonitasoft.studio.engine.export.BarExporter;
54 import org.bonitasoft.studio.engine.i18n.Messages;
55 import org.bonitasoft.studio.engine.preferences.EnginePreferenceConstants;
56 import org.bonitasoft.studio.model.configuration.Configuration;
57 import org.bonitasoft.studio.model.process.AbstractProcess;
58 import org.bonitasoft.studio.preferences.dialog.BonitaPreferenceDialog;
59 import org.bonitasoft.studio.ui.notification.BonitaNotificator;
60 import org.eclipse.core.runtime.CoreException;
61 import org.eclipse.core.runtime.IConfigurationElement;
62 import org.eclipse.core.runtime.IProgressMonitor;
63 import org.eclipse.jface.preference.IPreferenceStore;
64 import org.eclipse.swt.widgets.Display;
65 import org.eclipse.swt.widgets.Shell;
68 * @author Romain Bioteau
70 public class BOSEngineManager {
72 public static final String CUSTOM_PERMISSIONS_MAPPING_PROPERTIES = "custom-permissions-mapping.properties";
74 public static final String CONSOLE_CONFIG_PROPERTIES = "console-config.properties";
76 private static final String POSTSTARTUP_CONTIBUTION_ID = "org.bonitasoft.studio.engine.postEngineAction";
78 public static final String PLATFORM_PASSWORD = "platform";
80 public static final String PLATFORM_USER = "platformAdmin";
82 public static final String BONITA_TECHNICAL_USER = "install";
84 public static final String BONITA_TECHNICAL_USER_PASSWORD = "install";
86 public static final String API_TYPE_PROPERTY_NAME = "org.bonitasoft.engine.api-type";
88 public static final String DEFAULT_TENANT_NAME = "default";
90 public static final String DEFAULT_TENANT_DESC = "The default tenant created by the Studio";
92 private static final String ENGINESERVERMANAGER_EXTENSION_D = "org.bonitasoft.studio.engine.bonitaEngineManager";
94 private static final long DEFAULT_TENANT_ID = 1;
96 public static final String SECURITY_CONFIG_PROPERTIES = "security-config.properties";
98 private static final String FIND_USER_PASSWORD_COMMAND = "org.bonitasoft.studio.actors.command.userPassword";
100 private static BOSEngineManager INSTANCE;
102 private boolean isRunning = false;
104 private IProgressMonitor monitor;
106 private CommandExecutor commandExecutor = new CommandExecutor();
108 protected BOSEngineManager(final IProgressMonitor monitor) {
109 if (monitor == null) {
110 this.monitor = AbstractRepository.NULL_PROGRESS_MONITOR;
112 this.monitor = monitor;
116 public static BOSEngineManager getInstance() {
117 return getInstance(null);
120 public static synchronized BOSEngineManager getInstance(final IProgressMonitor monitor) {
121 if (INSTANCE == null) {
122 INSTANCE = createInstance(monitor);
127 protected static BOSEngineManager createInstance(final IProgressMonitor monitor) {
128 for (final IConfigurationElement element : BonitaStudioExtensionRegistryManager.getInstance()
129 .getConfigurationElements(ENGINESERVERMANAGER_EXTENSION_D)) {
131 return (BOSEngineManager) element.createExecutableExtension("class");
132 } catch (final CoreException e) {
133 BonitaStudioLog.error(e, EnginePlugin.PLUGIN_ID);
137 return new BOSEngineManager(monitor);
140 public synchronized void start(AbstractRepository repository) {
141 if (!isRunning() || !BOSWebServerManager.getInstance().serverIsStarted()) {
142 // boolean notifying = notifyStartServer();
143 // monitor.beginTask(Messages.initializingProcessEngine, IProgressMonitor.UNKNOWN);
144 // BOSWebServerManager.getInstance().startServer(repository, monitor);
145 // isRunning = postEngineStart(repository);
147 // notifyServerStarted();
152 private boolean notifyStartServer() {
153 if (EngineNotificationSemaphore.getInstance().tryAcquire()) {
154 if (!isLazyModeEnabled(EnginePlugin.getDefault().getPreferenceStore())) {
155 BonitaNotificator.openNotification(Messages.startServerNotificationTitle,
156 Messages.engineLazyModeNotificationLink, e -> {
157 BonitaPreferenceDialog dialog = new BonitaPreferenceDialog(new Shell(Display.getDefault()));
159 dialog.setSelectedPreferencePage(BonitaPreferenceDialog.SERVER_SETTINGS_PAGE_ID);
168 private boolean isLazyModeEnabled(IPreferenceStore preferenceStore) {
169 return preferenceStore.getBoolean(EnginePreferenceConstants.LAZYLOAD_ENGINE)
170 || System.getProperty(EnginePreferenceConstants.LAZYLOAD_ENGINE) != null;
173 public synchronized void start() {
174 start(RepositoryManager.getInstance().getCurrentRepository());
177 protected boolean postEngineStart(IRepository repository) {
178 //RESUME ENGINE IF PAUSED AT STARTUP
180 final APISession apiSession = getLoginAPI().login(BONITA_TECHNICAL_USER, BONITA_TECHNICAL_USER_PASSWORD);
181 final TenantAdministrationAPI tenantManagementAPI = getTenantAdministrationAPI(apiSession);
182 if (tenantManagementAPI.isPaused()) {
183 tenantManagementAPI.resume();
185 executePostStartupContributions(repository);
186 } catch (final Exception e) {
187 return handlePostEngineStartException(e);
192 private void notifyServerStarted() {
193 BonitaNotificator.openNotification(Messages.startServerCompletedNotificationTitle,
194 Messages.serverRunningNotificationMessage);
195 EngineNotificationSemaphore.getInstance().release();
198 private boolean handlePostEngineStartException(final Exception e) {
199 if (tomcatServerIsRunning()) {
200 BonitaStudioLog.error(e);
202 BonitaStudioLog.warning("Tomcat server has been shutdown before first start ended.", EnginePlugin.PLUGIN_ID);
207 protected boolean tomcatServerIsRunning() {
208 return BOSWebServerManager.getInstance().serverIsStarted();
211 public synchronized void stop() {
212 APISession session = null;
213 TenantAdministrationAPI tenantManagementAPI = null;
215 session = loginDefaultTenant(null);
216 tenantManagementAPI = getTenantAdministrationAPI(session);
217 tenantManagementAPI.pause();
218 if (dropBusinessDataDBOnExit()) {
219 tenantManagementAPI.cleanAndUninstallBusinessDataModel();
221 tenantManagementAPI.uninstallBusinessDataModel();
223 tenantManagementAPI.resume();
224 } catch (final Exception e) {
225 BonitaStudioLog.error(e);
227 if (tenantManagementAPI != null && tenantManagementAPI.isPaused()) {
229 tenantManagementAPI.resume();
230 } catch (final UpdateException e) {
231 BonitaStudioLog.error(e);
234 if (session != null) {
235 logoutDefaultTenant(session);
238 if (BOSWebServerManager.getInstance().serverIsStarted()) {
239 BOSWebServerManager.getInstance().stopServer(monitor);
243 BOSWebServerManager.getInstance().cleanBeforeShutdown();
244 } catch (final IOException e) {
245 BonitaStudioLog.error(e);
249 private boolean dropBusinessDataDBOnExit() {
250 final IPreferenceStore preferenceStore = EnginePlugin.getDefault().getPreferenceStore();
251 return preferenceStore.getBoolean(EnginePreferenceConstants.DROP_BUSINESS_DATA_DB_ON_EXIT_PREF);
254 protected void executePostStartupContributions(IRepository repository) throws Exception {
255 final IConfigurationElement[] elements = BonitaStudioExtensionRegistryManager.getInstance()
256 .getConfigurationElements(POSTSTARTUP_CONTIBUTION_ID);
257 IEngineAction contrib = null;
258 for (final IConfigurationElement elem : elements) {
260 contrib = (IEngineAction) elem.createExecutableExtension("class"); //$NON-NLS-1$
261 } catch (final CoreException e) {
262 BonitaStudioLog.error(e);
264 if (contrib != null && contrib.shouldRun(repository)) {
265 final APISession session = getLoginAPI().login(BONITA_TECHNICAL_USER, BONITA_TECHNICAL_USER_PASSWORD);
267 contrib.run(session,repository);
269 if (session != null) {
270 logoutDefaultTenant(session);
278 public boolean isRunning() {
282 public ProcessAPI getProcessAPI(final APISession session) {
284 return TenantAPIAccessor.getProcessAPI(session);
285 } catch (final Exception e) {
286 BonitaStudioLog.error(e);
291 protected LoginAPI getLoginAPI() throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {
292 return TenantAPIAccessor.getLoginAPI();
295 public APISession loginDefaultTenant(final IProgressMonitor monitor)
296 throws LoginException, BonitaHomeNotSetException, ServerAPIException,
297 UnknownAPITypeException {
298 return loginTenant(BONITA_TECHNICAL_USER, BONITA_TECHNICAL_USER_PASSWORD, monitor);
301 public APISession loginTenant(final String login, final String password, final IProgressMonitor monitor)
302 throws LoginException,
303 BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {
304 if (!isRunning() && monitor != null) {
305 monitor.beginTask(Messages.waitingForEngineToStart, IProgressMonitor.UNKNOWN);
308 BonitaStudioLog.debug("Attempt to login as " + login, EnginePlugin.PLUGIN_ID);
309 final APISession session = getLoginAPI().login(requireNonNull(login), requireNonNull(password));
310 if (session != null) {
311 BonitaStudioLog.debug("Login successful.", EnginePlugin.PLUGIN_ID);
316 public void logoutDefaultTenant(final APISession session) {
318 getLoginAPI().logout(session);
319 } catch (final Exception e) {
320 BonitaStudioLog.error(e);
324 public IdentityAPI getIdentityAPI(final APISession session)
325 throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {
326 return TenantAPIAccessor.getIdentityAPI(session);
329 public CommandAPI getCommandAPI(final APISession session)
330 throws BonitaHomeNotSetException, ServerAPIException,
331 UnknownAPITypeException {
332 return TenantAPIAccessor.getCommandAPI(session);
335 public ProfileAPI getProfileAPI(final APISession session)
336 throws BonitaHomeNotSetException, ServerAPIException,
337 UnknownAPITypeException {
338 return TenantAPIAccessor.getProfileAPI(session);
341 public PageAPI getPageAPI(final APISession session)
342 throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {
343 return TenantAPIAccessor.getCustomPageAPI(session);
346 public ApplicationAPI getApplicationAPI(final APISession session)
347 throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {
348 return TenantAPIAccessor.getLivingApplicationAPI(session);
351 public TenantAdministrationAPI getTenantAdministrationAPI(final APISession session)
352 throws BonitaHomeNotSetException,
353 ServerAPIException, UnknownAPITypeException {
354 return TenantAPIAccessor.getTenantAdministrationAPI(session);
357 public PlatformSession loginPlatform(IProgressMonitor monitor)
358 throws PlatformLoginException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {
359 if (!isRunning() && monitor != null) {
360 monitor.beginTask(Messages.waitingForEngineToStart, IProgressMonitor.UNKNOWN);
363 return PlatformAPIAccessor.getPlatformLoginAPI().login(PLATFORM_USER, PLATFORM_PASSWORD);
366 public void logoutPlatform(PlatformSession session)
367 throws PlatformLogoutException, SessionNotFoundException, BonitaHomeNotSetException, ServerAPIException,
368 UnknownAPITypeException {
369 PlatformAPIAccessor.getPlatformLoginAPI().logout(session);
372 public PlatformAPI getPlatformAPI(PlatformSession session)
373 throws BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException {
374 return PlatformAPIAccessor.getPlatformAPI(session);
377 public APISession createSession(final AbstractProcess process, final String configurationId,
378 final IProgressMonitor monitor) throws Exception {
379 final Configuration configuration = BarExporter.getInstance().getConfiguration(process, configurationId);
381 String username = configuration.getUsername();
382 String password = retrieveUserPasswordFromActiveOrga(username)
383 .orElseThrow(() -> new Exception(
384 String.format("Unable to retrieve the password of %s in the active organization.",
387 session = BOSEngineManager.getInstance().loginTenant(username, password,
389 } catch (final Exception e1) {
390 throw new Exception(Messages.bind(Messages.loginFailed,
391 new String[] { username, process.getName(),
392 process.getVersion() }),
395 if (session == null) {
396 throw new Exception(Messages.bind(Messages.loginFailed,
397 new String[] { username, process.getName(),
398 process.getVersion() }));
403 private Optional<String> retrieveUserPasswordFromActiveOrga(String user) {
404 Map<String, Object> parameters = new HashMap<>();
405 parameters.put("userName", user);
406 Object result = commandExecutor.executeCommand(FIND_USER_PASSWORD_COMMAND, parameters);
407 return result instanceof Optional ? (Optional<String>) result : Optional.empty();
410 public byte[] getTenantConfigResourceContent(String resourceName, IProgressMonitor monitor)
411 throws PlatformLoginException, BonitaHomeNotSetException, ServerAPIException, UnknownAPITypeException,
412 PlatformLogoutException, SessionNotFoundException, FileNotFoundException {
413 PlatformSession loginPlatform = null;
415 loginPlatform = loginPlatform(monitor);
416 final PlatformAPI platformAPI = getPlatformAPI(loginPlatform);
417 final Map<Long, Map<String, byte[]>> clientTenantConfigurations = platformAPI.getClientTenantConfigurations();
418 final Map<String, byte[]> resources = clientTenantConfigurations.get(DEFAULT_TENANT_ID);
419 if (!resources.containsKey(resourceName)) {
420 throw new FileNotFoundException(String.format("Resource %s does not exist in database.", resourceName));
422 return resources.get(resourceName);
424 if (loginPlatform != null) {
425 logoutPlatform(loginPlatform);
430 public void updateTenantConfigResourceContent(String resourceName, byte[] content)
431 throws PlatformLoginException, BonitaHomeNotSetException,
432 ServerAPIException, UnknownAPITypeException,
433 UpdateException, PlatformLogoutException, SessionNotFoundException {
434 PlatformSession loginPlatform = null;
436 loginPlatform = loginPlatform(null);
437 final PlatformAPI platformAPI = getPlatformAPI(loginPlatform);
438 platformAPI.updateClientTenantConfigurationFile(DEFAULT_TENANT_ID, resourceName, content);
440 if (loginPlatform != null) {
441 logoutPlatform(loginPlatform);