| /******************************************************************************* |
| * Copyright (c) 2020 Christian Pontesegger and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Christian Pontesegger - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.skills.service; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.e4.core.services.events.IEventBroker; |
| import org.eclipse.skills.Activator; |
| import org.eclipse.skills.BrokerTools; |
| import org.eclipse.skills.Logger; |
| import org.eclipse.skills.model.IQuest; |
| import org.eclipse.skills.model.IReward; |
| import org.eclipse.skills.model.ITask; |
| import org.eclipse.skills.model.IUser; |
| import org.eclipse.skills.model.IUserTask; |
| import org.eclipse.skills.service.questprovider.ExtensionPointQuestProvider; |
| import org.eclipse.skills.service.questprovider.IQuestProvider; |
| import org.eclipse.skills.service.storage.DataStorageProxy; |
| import org.eclipse.skills.service.storage.WorkspaceDataStorage; |
| import org.eclipse.ui.PlatformUI; |
| import org.osgi.service.event.Event; |
| import org.osgi.service.event.EventHandler; |
| |
| public class SkillService extends DataStorageProxy implements ISkillService, EventHandler { |
| |
| private static ISkillService fInstance = null; |
| |
| public synchronized static ISkillService getInstance() { |
| if (fInstance == null) |
| fInstance = new SkillService(); |
| |
| return fInstance; |
| } |
| |
| private static Collection<ITask> extractTasksRecursively(Collection<ITask> tasks) { |
| final Collection<ITask> result = new ArrayList<>(tasks); |
| |
| for (final ITask task : tasks) |
| result.addAll(extractTasksRecursively(task.getTasks())); |
| |
| return result; |
| } |
| |
| private UserStorage fUserStorage = null; |
| |
| private IQuestProvider fQuestProvider = null; |
| |
| private UINotificationService fNotificationService = null; |
| |
| /* package */ SkillService() { |
| super(new WorkspaceDataStorage()); |
| } |
| |
| @Override |
| public void activateService() { |
| getUser(); |
| |
| registerForEvents(); |
| |
| startUserTasks(); |
| activateOpenTasks(); |
| |
| BrokerTools.post(ISkillService.EVENT_USER_UPDATE, getUser()); |
| } |
| |
| @Override |
| public void deactivateService() { |
| stopUserTasks(); |
| deactivateOpenTasks(); |
| |
| unregisterFromEvents(); |
| |
| BrokerTools.post(ISkillService.EVENT_USER_UPDATE, getUser()); |
| |
| // unload resources |
| fUserStorage = null; |
| fQuestProvider = null; |
| fNotificationService = null; |
| } |
| |
| private void registerForEvents() { |
| final IEventBroker broker = BrokerTools.getBroker(); |
| if (broker != null) { |
| broker.subscribe(ISkillService.EVENT_BASE + "/*", this); |
| createNotificationService(); |
| |
| } else if (PlatformUI.isWorkbenchRunning()) { |
| // probably the broker service will startup later |
| final Job job = new Job("Register for Skill events") { |
| |
| @Override |
| protected IStatus run(IProgressMonitor monitor) { |
| registerForEvents(); |
| return Status.OK_STATUS; |
| } |
| }; |
| |
| job.setSystem(true); |
| job.schedule(10 * 1000); |
| } |
| } |
| |
| private void unregisterFromEvents() { |
| BrokerTools.unsubscribe(this); |
| if (hasNotificationService()) |
| getNotificationService().dispose(); |
| } |
| |
| @Override |
| public void handleEvent(Event event) { |
| if (ISkillService.EVENT_TASK_COMPLETED.equals(event.getTopic())) { |
| final Object data = event.getProperty(IEventBroker.DATA); |
| if (data instanceof IUserTask) |
| notifyTaskCompleted((IUserTask) data); |
| else |
| Logger.warning(Activator.PLUGIN_ID, "Task completed event from broker with invalid data type detected", |
| new IllegalArgumentException("Unknown event type: " + data)); |
| |
| } else if (ISkillService.EVENT_TASK_READY.equals(event.getTopic())) { |
| final Object data = event.getProperty(IEventBroker.DATA); |
| if (data instanceof ITask) |
| notifyTaskReady((ITask) data); |
| else |
| Logger.warning(Activator.PLUGIN_ID, "Task ready event from broker with invalid data type detected", |
| new IllegalArgumentException("Unknown event type: " + data)); |
| } |
| } |
| |
| @Override |
| public IUserTask notifyTaskReady(ITask task) { |
| final IUserTask userTask = getUser().addTask(task); |
| if (task.isAutoActivation()) |
| startTask(userTask); |
| |
| storeUser(); |
| |
| return userTask; |
| } |
| |
| @Override |
| public void notifyTaskCompleted(IUserTask userTask) { |
| for (final IReward reward : userTask.getTask().getRewards()) |
| getUser().consume(reward); |
| |
| updateUserTitle(); |
| |
| storeUser(); |
| } |
| |
| private void updateUserTitle() { |
| getUser().setTitle(getUserTitle()); |
| } |
| |
| private String getUserTitle() { |
| final UserTitleGenerator userTitleGenerator = new UserTitleGenerator(getUser(), getQuestProvider()); |
| return userTitleGenerator.createUserTitle(); |
| } |
| |
| @Override |
| public IUser getUser() { |
| return getUserStorage().getUser(); |
| } |
| |
| private void storeUser() { |
| try { |
| getUserStorage().storeUser(getUser()); |
| } catch (final IOException e) { |
| Logger.error(Activator.PLUGIN_ID, "Could not store user profile", e); |
| } |
| } |
| |
| @Override |
| public void resetProgress() { |
| getUserStorage().resetProgress(); |
| storeUser(); |
| } |
| |
| private UserStorage getUserStorage() { |
| if (fUserStorage == null) |
| fUserStorage = new UserStorage(this); |
| |
| return fUserStorage; |
| } |
| |
| private void createNotificationService() { |
| fNotificationService = new UINotificationService(); |
| } |
| |
| private boolean hasNotificationService() { |
| return fNotificationService != null; |
| } |
| |
| private UINotificationService getNotificationService() { |
| if (fNotificationService == null) |
| createNotificationService(); |
| |
| return fNotificationService; |
| } |
| |
| private void startUserTasks() { |
| for (final IUserTask task : getUser().getUsertasks()) { |
| if (task.isStarted() && !task.isCompleted()) |
| task.activate(); |
| } |
| } |
| |
| private void stopUserTasks() { |
| for (final IUserTask task : getUser().getUsertasks()) { |
| if (!task.isCompleted()) |
| task.getTask().getGoal().deactivate(); |
| } |
| } |
| |
| private void activateOpenTasks() { |
| for (final ITask task : getOpenTasks()) { |
| task.getRequirement().activate(); |
| |
| } |
| } |
| |
| private void deactivateOpenTasks() { |
| for (final ITask task : getOpenTasks()) |
| task.getRequirement().deactivate(); |
| } |
| |
| @Override |
| public Collection<ITask> getOpenTasks() { |
| final Collection<ITask> openTasks = getAllAvailableTasks(); |
| final List<ITask> runningTasks = getUser().getUsertasks().stream().map(t -> t.getTask()).collect(Collectors.toList()); |
| |
| // TODO is there a better way to remove the running tasks? openTasks.removeAll() does not work |
| for (final ITask runningTask : runningTasks) { |
| for (final ITask openTask : openTasks) { |
| if (Objects.equals(runningTask, openTask)) { |
| openTasks.remove(openTask); |
| break; |
| } |
| } |
| } |
| |
| return openTasks; |
| } |
| |
| @Override |
| public void storeResource(String name, byte[] data) throws IOException { |
| getStorage().storeResource(name, data); |
| } |
| |
| @Override |
| public byte[] loadResource(String name) throws IOException { |
| return getStorage().loadResource(name); |
| } |
| |
| @Override |
| public boolean hasResource(String name) { |
| return getStorage().hasResource(name); |
| } |
| |
| @Override |
| public void startTask(final IUserTask userTask) { |
| userTask.setStarted(new Date()); |
| |
| BrokerTools.post(ISkillService.EVENT_TASK_STARTED, userTask); |
| |
| userTask.activate(); |
| } |
| |
| @Override |
| public void setQuestProvider(IQuestProvider questProvider) { |
| fQuestProvider = questProvider; |
| } |
| |
| @Override |
| public IQuestProvider getQuestProvider() { |
| if (fQuestProvider != null) |
| return fQuestProvider; |
| |
| return new ExtensionPointQuestProvider(); |
| } |
| |
| @Override |
| public Collection<ITask> getAllAvailableTasks() { |
| final Collection<ITask> tasks = new HashSet<>(); |
| |
| for (final IQuest quest : getQuestProvider().getQuests()) |
| tasks.addAll(extractTasksRecursively(quest.getTasks())); |
| |
| return tasks; |
| } |
| } |