/**
* Copyright (c) 2011-2014, OpenIoT
*
* This file is part of OpenIoT.
*
* OpenIoT is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* OpenIoT is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenIoT. If not, see <http://www.gnu.org/licenses/>.
*
* Contact: OpenIoT mailto: info@openiot.eu
*/
package org.openiot.ui.request.definition.web.scopes.controllers.pages;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.openiot.commons.osdspec.model.DynamicAttrMaxValue;
import org.openiot.commons.osdspec.model.OAMO;
import org.openiot.commons.osdspec.model.OSDSpec;
import org.openiot.commons.osdspec.model.OSMO;
import org.openiot.commons.osdspec.model.PresentationAttr;
import org.openiot.commons.osdspec.model.QueryControls;
import org.openiot.commons.osdspec.model.QuerySchedule;
import org.openiot.commons.osdspec.model.RequestPresentation;
import org.openiot.commons.osdspec.model.Widget;
import org.openiot.commons.sensortypes.model.SensorTypes;
import org.openiot.commons.sparql.protocoltypes.model.QueryRequest;
import org.openiot.security.client.AccessControlUtil;
import org.openiot.security.client.OAuthorizationCredentials;
import org.openiot.ui.request.commons.interfaces.GraphModel;
import org.openiot.ui.request.commons.logging.LoggerService;
import org.openiot.ui.request.commons.models.DefaultGraphModel;
import org.openiot.ui.request.commons.models.OAMOManager;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNode;
import org.openiot.ui.request.commons.nodes.interfaces.GraphNodeProperty;
import org.openiot.ui.request.commons.providers.SchedulerAPIWrapper;
import org.openiot.ui.request.commons.providers.exceptions.APIException;
import org.openiot.ui.request.commons.util.MarshalOSDspecUtils;
import org.openiot.ui.request.definition.web.factory.PropertyGridFormFactory;
import org.openiot.ui.request.definition.web.jsf.components.events.NodeInsertedEvent;
import org.openiot.ui.request.definition.web.model.nodes.impl.sources.GenericSource;
import org.openiot.ui.request.definition.web.model.validation.validators.OpenIoTGraphNodeValidator;
import org.openiot.ui.request.definition.web.scopes.application.ApplicationBean;
import org.openiot.ui.request.definition.web.scopes.session.SessionBean;
import org.openiot.ui.request.definition.web.scopes.session.context.dialogs.FindSensorDialogContext;
import org.openiot.ui.request.definition.web.scopes.session.context.pages.ApplicationDesignPageContext;
import org.openiot.ui.request.definition.web.sparql.SparqlGenerator;
import org.openiot.ui.request.definition.web.util.FaceletLocalization;
import org.primefaces.context.RequestContext;
import org.primefaces.model.DefaultStreamedContent;
/**
*
* @author Achilleas Anagnostopoulos (aanag) email: aanag@sensap.eu
*/
@ManagedBean(name = "applicationDesignPageController")
@RequestScoped
public class ApplicationDesignPageController implements Serializable {
private static final long serialVersionUID = 1L;
// Cached context
private ApplicationDesignPageContext cachedContext;
// Injected properties
@ManagedProperty(value = "#{applicationBean}")
protected transient ApplicationBean applicationBean;
@ManagedProperty(value = "#{sessionBean}")
protected transient SessionBean sessionBean;
protected transient ResourceBundle messages;
private AccessControlUtil acUtil;
public ApplicationDesignPageController() {
this.messages = FaceletLocalization.getLocalizedResourceBundle();
acUtil = AccessControlUtil.getInstance();
}
public ApplicationDesignPageContext getContext() {
if (cachedContext == null) {
if (sessionBean.getUserId() == null) {
OAuthorizationCredentials credentials = acUtil.getOAuthorizationCredentials();
if(credentials == null)
return null;
else
sessionBean.setUserId(credentials.getUserIdURI());
}
cachedContext = (ApplicationDesignPageContext) (sessionBean == null ? ApplicationBean.lookupSessionBean() : sessionBean).getContext("applicationDesignPageContext");
if (cachedContext == null) {
cachedContext = new ApplicationDesignPageContext();
}
}
return cachedContext;
}
// ------------------------------------
// Controller entrypoints
// ------------------------------------
public void keepAlivePing() {
}
public void doAccessControl() throws IOException {
if(acUtil.getOAuthorizationCredentials() == null){
HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
HttpServletRequest req = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
acUtil.redirectToLogin(req, response);
}
// if (sessionBean.getUserId() == null) {
// applicationBean.redirect("/pages/login.xhtml?faces-redirect=true");
// }
}
// ------------------------------------
// Controller for OAMO editing
// ------------------------------------
public void onGraphNodeSelected() {
ApplicationDesignPageContext context = getContext();
if (context.getGraphModel().getSelectedNode() != null) {
context.setPropertyEditorModel(PropertyGridFormFactory.generatePropertyGridDynaForm(context.getGraphModel().getSelectedNode()));
} else {
context.setPropertyEditorModel(null);
}
}
public void onGraphNodeInserted(NodeInsertedEvent event) {
ApplicationDesignPageContext context = getContext();
// Lookup node category
List<GraphNode> nodesInGroup = context.getAvailableNodesByTypeMap().get(event.getNodeGroup());
if (nodesInGroup == null) {
return;
}
// Lookup node type
for (GraphNode node : nodesInGroup) {
if (!node.getLabel().equals(event.getNodeType())) {
continue;
}
// Instanciate a copy of the node
GraphNode newNode = node.getCopy();
context.getGraphModel().insert(newNode, event.getX(), event.getY());
// If we added a sensor, set the search filters into the instance
if (newNode instanceof GenericSource) {
GenericSource source = (GenericSource) node;
source.getPropertyValueMap().put("LAT", context.getFilterLocationLat());
source.getPropertyValueMap().put("LON", context.getFilterLocationLon());
source.getPropertyValueMap().put("RADIUS", context.getFilterLocationRadius());
}
break;
}
}
public void onGraphNodeDeleted() {
ApplicationDesignPageContext context = getContext();
context.getGraphModel().setSelectedNode(null);
context.setPropertyEditorModel(null);
}
public void executeSensorDiscoveryQuery() {
ApplicationDesignPageContext context = getContext();
FindSensorDialogContext findContext = (FindSensorDialogContext) (sessionBean == null ? ApplicationBean.lookupSessionBean() : sessionBean).getContext("findSensorDialogContext");
// Save filter options so we can inject them into the generated sparql
// queries
context.setFilterLocationLat(findContext.getSearchCenter().getLatlng().getLat());
context.setFilterLocationLon(findContext.getSearchCenter().getLatlng().getLng());
context.setFilterLocationRadius(findContext.getSearchRadius());
// Execute search and populate sensor toolbox
try {
OAuthorizationCredentials creds = acUtil.getOAuthorizationCredentials();
SensorTypes sensorTypes = SchedulerAPIWrapper.getAvailableSensors("user000", context.getFilterLocationLat(), context.getFilterLocationLon(), context.getFilterLocationRadius(), creds.getClientId(), creds.getAccessToken());
context.updateAvailableSensors(sensorTypes);
} catch (APIException ex) {
LoggerService.log(ex);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_CONNECTING_TO_DISCOVERY_SERVICE")));
}
}
// ------------------------------------
// Controllers for application editing
// ------------------------------------
public void prepareNewApplicationDialog() {
ApplicationDesignPageContext context = getContext();
context.setNewApplicationName(null);
context.setNewApplicationDescription(null);
}
public void createNewApplication() {
ApplicationDesignPageContext context = getContext();
// Check for duplicate names
if (context.getAppManager().exists(context.getNewApplicationName())) {
RequestContext.getCurrentInstance().addCallbackParam("validationFailed", "1");
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_DUPLICATE_APPLICATION_NAME", context.getNewApplicationName())));
return;
}
// Create a new OAMO and select it
context.getAppManager().createOAMO(context.getNewApplicationName(), context.getNewApplicationDescription(), true);
// Setup workspace
context.cleanupWorkspace();
context.setGraphModel(new DefaultGraphModel());
}
public void validateApplication() {
ApplicationDesignPageContext context = getContext();
OpenIoTGraphNodeValidator validator = new OpenIoTGraphNodeValidator(context.getGraphModel());
boolean success = validator.validate();
context.setGraphValidationErrors(validator.getValidationErrors());
context.setGraphValidationWarnings(validator.getValidationWarnings());
if (success) {
if (validator.getValidationWarnings().isEmpty()) {
String codeOutput = "";
SparqlGenerator generator = new SparqlGenerator();
// Generate code for each visualization node
GraphModel model = context.getGraphModel();
for (GraphNode node : model.getNodes()) {
if (node.getType().equals("SINK")) {
codeOutput += StringUtils.join(generator.generateQueriesForNodeEndpoints(model, node), "\n\n#-----------------------------------\n\n");
}
}
context.setGeneratedCode(codeOutput);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, messages.getString("GROWL_INFO_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "UI_GRAPH_COMPILER_SUCCESS")));
}
} else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "UI_VALIDATION_FAILED", validator.getValidationErrors().size(), validator.getValidationWarnings().size())));
}
}
public void clearApplicationWorkspace() {
ApplicationDesignPageContext context = getContext();
context.getAppManager().resetSelectedOAMO();
context.cleanupWorkspace();
}
// ------------------------------------
// Controllers for application management
// ------------------------------------
public void loadApplication(String name) {
ApplicationDesignPageContext context = getContext();
context.getAppManager().selectOAMOByName(name);
GraphModel graphModel = new DefaultGraphModel();
String graphMeta = context.getAppManager().getSelectedOAMO() != null ? context.getAppManager().getSelectedOAMO().getGraphMeta() : null;
if (graphMeta != null) {
try {
JSONObject spec = new JSONObject(new JSONTokener(graphMeta));
graphModel.importJSON(spec);
} catch (JSONException ex) {
LoggerService.log(ex);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_LOADING_APPLICATION_DESIGN")));
context.setGraphModel(null);
context.cleanupWorkspace();
return;
}
}
context.setGraphModel(graphModel);
context.clearAvailableSensors();
applicationBean.redirect("/pages/applicationDesign.xhtml?faces-redirect=true");
}
public void saveApplication() {
if (encodeApplication()) {
try {
OAuthorizationCredentials creds = acUtil.getOAuthorizationCredentials();
getContext().getAppManager().saveSelectedOAMO(creds.getClientId(), creds.getAccessToken());
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, messages.getString("GROWL_INFO_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "UI_GRAPH_SAVE_SUCCESS")));
} catch (APIException ex) {
LoggerService.log(ex);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_CONNECTING_TO_REGISTRATION_SERVICE")));
}
}
}
public void reloadApplications() {
ApplicationDesignPageContext context = getContext();
if (context.getAppManager() == null) {
context.setAppManager(new OAMOManager());
}
// Load services
try {
OAuthorizationCredentials creds = acUtil.getOAuthorizationCredentials();
context.getAppManager().loadUserOAMOs(ApplicationBean.lookupSessionBean().getUserId(), creds.getClientId(), creds.getAccessToken());
if( !context.getAppManager().getAvailableOAMOs().isEmpty() ){
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, messages.getString("GROWL_INFO_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "APPLICATIONS_LOADED_SUCCESSFULLY")));
}
} catch (APIException ex) {
LoggerService.log(ex);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_CONNECTING_TO_REGISTRATION_SERVICE")));
}
context.cleanupWorkspace();
}
public void prepareImportApplicationsDialog() {
ApplicationDesignPageContext context = getContext();
context.setUploadedSpec(null);
context.setPersistSpec(false);
}
public void importApplications() {
ApplicationDesignPageContext context = getContext();
if (context.getUploadedSpec() == null) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_SELECT_A_FILE_TO_UPLOAD")));
return;
}
// Try to parse OSDSpec
OSDSpec spec = null;
try {
// Unserialize
JAXBContext jaxbContext = JAXBContext.newInstance(OSDSpec.class);
Unmarshaller um = jaxbContext.createUnmarshaller();
spec = (OSDSpec) um.unmarshal(new StreamSource(
new StringReader(IOUtils.toString(context.getUploadedSpec().getInputstream(), "UTF-8"))));
} catch (Exception ex) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_COULD_NOT_PARSE_OSDSPEC_FILE")));
return;
}
context.getAppManager().loadOSDSPec(spec);
context.cleanupWorkspace();
if( context.isPersistSpec() ){
try{
OAuthorizationCredentials creds = acUtil.getOAuthorizationCredentials();
SchedulerAPIWrapper.registerService(context.getAppManager().exportOSDSpec(), creds.getClientId(), creds.getAccessToken());
} catch (APIException ex) {
LoggerService.log(ex);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "ERROR_CONNECTING_TO_REGISTRATION_SERVICE")));
}
}
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_INFO, messages.getString("GROWL_INFO_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "INFO_APPLICATION_IMPORT_SUCCESS")));
applicationBean.redirect("/pages/applicationDesign.xhtml?faces-redirect=true");
}
public void exportApplications() throws APIException, IOException {
ApplicationDesignPageContext context = getContext();
// If an OAMO is active, make sure we validate and encode it before
// continuing
if (context.getAppManager().getSelectedOAMO() != null && !encodeApplication()) {
context.setExportedSpec(null);
return;
}
// Export spec
OSDSpec spec = getContext().getAppManager().exportOSDSpec();
String osdSpecString;
try {
osdSpecString = MarshalOSDspecUtils.marshalOSDSpec(spec);
} catch (Exception e) {
throw new APIException(e);
}
getContext().setExportedSpec(new DefaultStreamedContent(IOUtils.toInputStream(osdSpecString, "UTF-8"), "application/xml", "applications.xml"));
}
// ------------------------------------
// Helpers
// ------------------------------------
public void setApplicationBean(ApplicationBean applicationBean) {
this.applicationBean = applicationBean;
}
public void setSessionBean(SessionBean sessionBean) {
this.sessionBean = sessionBean;
}
private boolean encodeApplication() {
ApplicationDesignPageContext context = getContext();
OpenIoTGraphNodeValidator validator = new OpenIoTGraphNodeValidator(context.getGraphModel());
boolean success = validator.validate();
context.setGraphValidationErrors(validator.getValidationErrors());
context.setGraphValidationWarnings(validator.getValidationWarnings());
if (success) {
if (validator.getValidationWarnings().isEmpty()) {
String codeOutput = "";
SparqlGenerator generator = new SparqlGenerator();
GraphModel model = context.getGraphModel();
OAMO oamo = context.getAppManager().getSelectedOAMO();
oamo.getOSMO().clear();
oamo.setGraphMeta(model.toJSON().toString());
// Force new id generation
oamo.setId(null);
// Generate an OSMO object for each visualization node
for (GraphNode node : model.getNodes()) {
if (node.getType().equals("SINK")) {
OSMO osmo = new OSMO();
//osmo.setId("node://" + node.getUID());
// Force new id generation
oamo.setId(null);
// Setup query controlls
QueryControls queryControls = new QueryControls();
queryControls.setReportIfEmpty(false);
QuerySchedule querySchedule = new QuerySchedule();
queryControls.setQuerySchedule(querySchedule);
osmo.setQueryControls(queryControls);
// Setup query request
List<String> queryBlocks = generator.generateQueriesForNodeEndpoints(model, node);
for (String query : queryBlocks) {
QueryRequest queryRequest = new QueryRequest();
queryRequest.setQuery(query);
osmo.getQueryRequest().add(queryRequest);
}
codeOutput += StringUtils.join(queryBlocks, "\n\n#-----------------------------------\n\n");
// Encode dynamic attributes
for (Map.Entry<GraphNodeProperty, Object> entry : generator.getVariableMap().entrySet()) {
DynamicAttrMaxValue attr = new DynamicAttrMaxValue();
attr.setName(entry.getKey().getVariableName());
attr.setValue(entry.getValue().toString());
osmo.getDynamicAttrMaxValue().add(attr);
}
// Setup visualization params
RequestPresentation requestPresentation = new RequestPresentation();
{
Widget widget = new Widget();
widget.setWidgetID("node://widget_" + node.getUID());
for (Map.Entry<String, Object> entry : node.getPropertyValueMap().entrySet()) {
PresentationAttr attr = new PresentationAttr();
attr.setName(entry.getKey());
attr.setValue(entry.getValue() != null ? entry.getValue().toString() : null);
widget.getPresentationAttr().add(attr);
}
// Add an extra attribute for the widget class
// so we know what type of widget to instanciate
// at the presentation layer
PresentationAttr attr = new PresentationAttr();
attr.setName("widgetClass");
attr.setValue(node.getClass().getCanonicalName());
widget.getPresentationAttr().add(attr);
requestPresentation.getWidget().add(widget);
osmo.setRequestPresentation(requestPresentation);
}
oamo.getOSMO().add(osmo);
}
}
context.setGeneratedCode(codeOutput);
return true;
}
} else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_FATAL, messages.getString("GROWL_ERROR_HEADER"), FaceletLocalization.getLocalisedMessage(messages, "UI_VALIDATION_FAILED", validator.getValidationErrors().size(), validator.getValidationWarnings().size())));
}
return false;
}
}