blob: 56e7016efa981535af33d6445470c27c76efaf2f [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*/
package org.eclipse.osbp.osgi.hybrid.api;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.extensions.EventUtils;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.advanced.MPerspective;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.e4.ui.workbench.modeling.IPartListener;
import org.eclipse.osbp.bpm.api.IBlipBPMConstants;
import org.eclipse.osbp.dsl.common.datatypes.IDto;
import org.eclipse.osbp.eventbroker.EventBrokerMsg;
import org.eclipse.osbp.persistence.IPersistenceService;
import org.eclipse.osbp.runtime.web.vaadin.databinding.VaadinObservables;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService;
import org.eclipse.osbp.ui.api.themes.IThemeResourceService.ThemeResourceType;
import org.eclipse.osbp.ui.api.useraccess.AbstractAuthorization.Permission;
import org.eclipse.osbp.ui.api.useraccess.AbstractAuthorization.PermissionResult;
import org.eclipse.osbp.ui.api.useraccess.AbstractPosition;
import org.eclipse.osbp.ui.api.useraccess.IPermissionList;
import org.eclipse.osbp.utils.common.IEntityIdModificationListenerView;
import org.eclipse.osbp.webserver.messagequeue.ECXMqMessageAttribute;
import org.eclipse.osbp.webserver.messagequeue.ECXMqMessageEvent;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.AbstractOrderedLayout;
import com.vaadin.ui.FormLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
public abstract class AbstractHybridVaaclipseView implements IHybridVaadinVaaclipseListener, IPartListener, IEntityIdModificationListenerView {
@Inject
IEventBroker eventBroker;
@Inject
protected IPersistenceService persistenceService;
//
// @Inject
// private AbstractBlipBPMFunctionProvider taskProvider;
//
// @Inject
// protected IBPMTaskClient taskClient;
//
// private String workloadDtoFqn;
// private IDto initialWorkloadDto;
// private Class<?> operativeDtoClass;
// private List<IDto> initialOperativeDtos;
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractHybridVaaclipseView.class);
public static enum RenderMode {
SYNCHRONOUSLY, ASYNCHRONOUSLY
}
private final IEclipseContext fEclipseContext;
private final MApplication fE4App;
private final VerticalLayout fParent;
private boolean fViewInitialized;
protected final EventHandler fModifiedEntityIdsListener;
private Map<String, Set<Object>> fListeningForEntityIdsModifications = new HashMap<>();
private RenderMode renderMode = RenderMode.ASYNCHRONOUSLY;
private boolean firstTime = true;
private int pollingInterval = 2000;
private RecursiveFocusBlurListener fRecursiveFocusBlurListener;
/**
* <b><i><u>Warning:</u> never use initializing class attributes in the definition!<br>
* Due to Java specific internals the overriden method createView() will be called before that initializing will be done!<br>
* Instead put any initializing inside the overriden method createView()!</i></b>
*
* @param parent
* @param context
* @param app
*/
public AbstractHybridVaaclipseView(final VerticalLayout parent, final IEclipseContext context, final MApplication app) {
fE4App = app;
fEclipseContext = context;
fViewInitialized = false;
fParent = parent;
fModifiedEntityIdsListener = new EventHandler() {
@Override
public void handleEvent(Event event) {
Object data = event.getProperty(EventUtils.DATA);
// --- only if data contains an event broker message ---
if (data instanceof EventBrokerMsg) {
EventBrokerMsg eventBrokerMsg = (EventBrokerMsg) data;
// eventBrokerMsg.getInt() == 1 means INSERT and no id check is required
// otherwise it is an UPDATE and a id check is mandatory
if (eventBrokerMsg.getInt() == 1) {
refreshViewDueToModifiedEntityId(eventBrokerMsg.getName(), eventBrokerMsg.getId());
} else {
checkOnModifiedEntityId(eventBrokerMsg, true);
}
}
}
};
fRecursiveFocusBlurListener = RecursiveFocusBlurListener.attachFor(fParent);
}
@PostConstruct
public void initView() {
preInit();
VaadinObservables.getRealm(UI.getCurrent());
UI.getCurrent().setPollInterval(pollingInterval);
createView(fParent);
postInit(null);
renderData(true);
}
public void promptSecurityMessage(String message, AbstractOrderedLayout layout) {
layout.removeAllComponents();
FormLayout area = new FormLayout();
layout.addComponent(area);
Label msg = new Label(message);
msg.setPrimaryStyleName("osbp");
msg.setStyleName("osbpSecurityPrompt");
if (getThemeResourceService() != null) {
msg.setIcon(getThemeResourceService().getThemeResource("locksview.gif", ThemeResourceType.IMAGE));
}
else {
LOGGER.error("themeResourceService not set!");
}
area.addComponent(msg);
}
private String beautifyEntityName(String entityName) {
return StringUtils.strip(entityName.replace('.', '/'), "/").toLowerCase();
}
private Object beautifyEntityId(Object id) {
if (id instanceof String) {
id = ((String) id).trim();
try {
Double.parseDouble((String) id);
// --- if it's a double ---
id = StringUtils.removeEnd((String) id, "0");
id = StringUtils.removeEnd((String) id, ".");
} catch (Exception e) {
// NOTHING CAN BE DONE
}
}
return id;
}
@Override
public void resetListeningForEntityIdsModifications() {
fListeningForEntityIdsModifications = null;
}
public boolean broadcastEntityIdModified(String packageName, String entityName, String id) {
if (eventBroker != null) {
return eventBroker.send(EventBrokerMsg.REFRESH_VIEW + EventBrokerMsg.getEntityIdModifiedCategory(packageName, entityName), new EventBrokerMsg(id, entityName));
} else {
return false;
}
}
@Override
public boolean checkOnModifiedEntityId(EventBrokerMsg message, boolean doRefresh) {
return checkOnModifiedEntityId(message.getName(), message.getId(), doRefresh);
}
@Override
public boolean checkOnModifiedEntityId(String entityName, Object id, boolean doRefresh) {
// --- check if the entity sent is known ---
entityName = beautifyEntityName(entityName);
id = beautifyEntityId(id);
Set<Object> ids = fListeningForEntityIdsModifications.get(entityName);
if (ids instanceof Set) {
// --- if the id for that entity is known ---
if (ids.contains(id)) {
// --- force refresh of the report ---
if (doRefresh) {
refreshViewDueToModifiedEntityId(entityName, id);
}
return true;
}
}
return false;
}
@Override
public void refreshViewDueToModifiedEntityId(String entity, Object id) {
addEntityIdToModifyListener(entity, id);
}
@Override
public void addEntityIdToModifyListener(String entityName, Object id) {
// --- build the entity map if necessary ---
if (fListeningForEntityIdsModifications == null) {
fListeningForEntityIdsModifications = new HashMap<>();
}
entityName = beautifyEntityName(entityName);
Set<Object> ids = fListeningForEntityIdsModifications.get(entityName);
// --- build the ids set if necessary ---
if (ids == null) {
ids = new HashSet<>();
fListeningForEntityIdsModifications.put(entityName, ids);
}
// --- put the primary key ---
ids.add(beautifyEntityId(id));
}
@Override
public void removeEntityIdFromModifyListener(String entityName, Object id) {
// --- build the entity map if necessary ---
if (fListeningForEntityIdsModifications != null) {
Set<Object> ids = fListeningForEntityIdsModifications.get(beautifyEntityName(entityName));
// --- build the ids set if necessary ---
if (ids != null) {
// --- put the primary key ---
ids.remove(id);
}
}
}
public final MApplication getApplication() {
return fE4App;
}
public final VerticalLayout getParent() {
return fParent;
}
public final IEclipseContext getContext() {
return fEclipseContext;
}
public final MPart getPart() {
return (MPart) fEclipseContext.get(MPart.class);
}
public final String getProcessWorkloadDtoFqn() {
return (String) getTransientDataVariable(IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO_FQN);
}
public final IDto getProcessInitialWorkloadDto() {
return (IDto) getTransientDataVariable(IBlipBPMConstants.VARIABLE_PROCESS_WORKLOAD_DTO);
}
public final Class<?> getTaskOperativeDtoClass() {
return (Class<?>) getTransientDataVariable(IBlipBPMConstants.VARIABLE_TASK_OPERATIVE_DTO_CLASS);
}
public final String getTaskOperativeDtoFqn() {
return (String) getTransientDataVariable(IBlipBPMConstants.VARIABLE_TASK_OPERATIVE_DTO_FQN);
}
public final List<IDto> getTaskInitialOperativeDtos() {
return (List<IDto>) getTransientDataVariable(IBlipBPMConstants.VARIABLE_TASK_OPERATIVE_DTOS);
}
public final Object getTransientDataVariable(String variable) {
MPerspective perspective = getPerspective();
if (perspective != null) {
Map<String, Object> data = perspective.getTransientData();
if (data != null) {
return data.get(variable);
}
}
return null;
}
public final MPerspective getPerspective() {
MUIElement step = getPart();
while ((step != null) && !(step instanceof MPerspective)) {
step = step.getParent();
}
return (MPerspective) step;
}
public final IPersistenceService getPersistenceService() {
return (IPersistenceService) fEclipseContext.get(IPersistenceService.class);
}
protected final boolean isViewInitialized() {
return fViewInitialized;
}
/**
* <b><i><u>Warning:</u> put any initializing inside the your overriden method createView()!<br>
* Due to Java specific internals the overriden method createView() will be called before that initializing will be done!</i></b>
*
* @param parent
*/
abstract protected void createView(final VerticalLayout parent);
abstract protected void createComponents();
// renderData is used for components that are not fully embedded in vaadin's connector structure
// and must be repainted when parent window resizes
// but can also be used to make component rendering asynchronously from view creation
public void renderData() {
LOGGER.debug("renderData not firsttime");
renderData(false);
}
public void renderData(boolean firstTime) {
// the first time we only want to be triggered by the initial createView process, not by changeLocale
if (this.firstTime) {
if (!firstTime) {
LOGGER.debug("renderData ignored because not firsttime");
return;
} else {
this.firstTime = false;
}
}
if (UI.getCurrent() == null) {
LOGGER.debug("renderData has no current ui");
return;
}
if (renderMode == RenderMode.SYNCHRONOUSLY) {
LOGGER.debug("render synchronously");
UI.getCurrent().accessSynchronously(new Runnable() {
@Override
public void run() {
createComponents();
}
});
} else {
LOGGER.debug("render asynchronously");
UI.getCurrent().access(new Runnable() {
@Override
public void run() {
createComponents();
}
});
}
}
private IThemeResourceService getThemeResourceService() {
if (fEclipseContext.containsKey(IThemeResourceService.class)) {
return fEclipseContext.get(IThemeResourceService.class);
}
return null;
}
private EPartService getPartService() {
if (fEclipseContext.containsKey(EPartService.class)) {
return fEclipseContext.get(EPartService.class);
}
return null;
}
protected void preInit() {
getPartService().addPartListener(this);
HybridVaadinVaaclipseConnector.instance(fEclipseContext).addListener(this);
HybridVaadinVaaclipseConnector.instance(fEclipseContext).setE4Application(fE4App);
}
@PreDestroy
public void preDestroy() {
fRecursiveFocusBlurListener.detach();
HybridVaadinVaaclipseConnector.instance(fEclipseContext).removeListener(this);
fListeningForEntityIdsModifications = null;
}
/**
* You <b>must call this at the end of the overridden init()</b>
*/
protected final void postInit(VaadinRequest request) {
HybridVaadinVaaclipseConnector.instance(fEclipseContext).postInit(request);
fViewInitialized = true;
if (isAuthenticated()) {
setAuthenticated(isAuthenticated());
}
}
/**
* React in the application according to <code>authenticated</code>
*
* @param authenticated
* true if the user is authenticated now!
*/
public void setAuthenticated(boolean authenticated) {
// now send the list of perspectives
HybridVaadinVaaclipseConnector.instance(fEclipseContext).updatePerspectiveList();
}
protected final boolean isAuthenticated() {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).isAuthenticated();
}
protected final IDto getAuthenticatedUser() {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).getUser();
}
protected final AbstractPosition getAuthenticatedPosition() {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).getPosition();
}
protected final Collection<String> getAuthenticatedRoles() {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).getRoles();
}
protected final IPermissionList getAuthenticatedPermissions() {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).getPermissions();
}
protected final PermissionResult isAuthenticatedPermitted(Permission permission) {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).isPermitted(permission);
}
protected final Set<String> getAllUsers() {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).getAllUsers();
}
/**
* Try to authenticate with the credentials given!<br>
* {@link #setAuthenticated(boolean)} will explicit be called!
*
* @param portalId
* @param userName
* @param password
* @return true if the user was authenticated successful
*/
// protected boolean tryToAuthenticate(String portalId, String userName, String password) {
// return HybridVaadinVaaclipseConnector.instance().tryToAuthenticate(portalId, userName, password);
// }
/**
* Logout from the Shiro API and send a LOGOUT event via ActiveMQ
*/
protected void logout() {
HybridVaadinVaaclipseConnector.instance(fEclipseContext).logout();
}
/**
* handle any message received via ActiveMQ
*
* @param event
* @param body
*/
@Override
public final boolean onMessage(ECXMqMessageEvent event, Map<ECXMqMessageAttribute, Object> body) {
boolean retcode = false;
switch (event) {
// !!! handled by connector ...
case TRY_AUTHENTICATE:
case DISPOSE:
case LOGOUT:
break;
// ... handled by connector !!!
case FOCUS_PERSPECTIVE:
retcode = HybridVaadinVaaclipseConnector.instance(fEclipseContext).onFocusPerspective(body.get(ECXMqMessageAttribute.PERSPECTIVE_ID).toString());
break;
case REQUEST_ICON:
retcode = HybridVaadinVaaclipseConnector.instance(fEclipseContext).requestIcon(body.get(ECXMqMessageAttribute.PERSPECTIVE_ID).toString());
break;
default:
break;
}
return retcode;
}
@Override
public void partActivated(MPart part) {
HybridVaadinVaaclipseConnector.instance(fEclipseContext).activatePartStateRefresher(part);
MPerspective active = HybridVaadinVaaclipseConnector.findCurrentPerspectiveFor(part == null ? getPartService().getActivePart() : part);
if (active instanceof MPerspective) {
HybridVaadinVaaclipseConnector.instance(fEclipseContext).updatePerspectiveList();
HybridVaadinVaaclipseConnector.instance(fEclipseContext).updateFocusPerspective(active.getElementId());
} else if ((part != null || getPartService().getActivePart() != null)) {
LOGGER.debug("part activated:" + (part == null ? "<null>" : part.getElementId() + " '" + part.getLabel() + "'"));
List<MPerspective> existing = HybridVaadinVaaclipseConnector.instance(fEclipseContext).findPerspectives();
for (MPerspective perspective : existing) {
LOGGER.debug(" perspective:" + perspective.getElementId() + " '" + perspective.getLabel() + "'");
}
LOGGER.debug(" COULD NOT find the corresponding perspective :-(");
}
}
@Override
public void partBroughtToTop(MPart part) {
HybridVaadinVaaclipseConnector.instance(fEclipseContext).activatePartStateRefresher(part);
}
@Override
public void partDeactivated(MPart part) {
} // NOP
@Override
public void partHidden(MPart part) {
} // NOP
@Override
public void partVisible(MPart part) {
} // NOP
/**
* Try to authenticate with the credentials given!<br>
* {@link #setAuthenticated(boolean)} will explicit be called!
*
* @param portalId
* @param userName
* @param password
* @return true if the user was authenticated successful
*/
protected boolean tryToAuthenticate(String portalId, String userName, String password) {
return HybridVaadinVaaclipseConnector.instance(fEclipseContext).tryToAuthenticate(portalId, userName, password);
}
public RenderMode getRenderMode() {
return renderMode;
}
public void setRenderMode(RenderMode renderMode) {
this.renderMode = renderMode;
}
public int getPollingInterval() {
return pollingInterval;
}
public void setPollingInterval(int pollingInterval) {
this.pollingInterval = pollingInterval;
}
}