/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ro.nextreports.server.web.dashboard.indicator; import java.awt.Color; import java.util.Map; import java.util.concurrent.TimeoutException; import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.wicket.Component; import org.apache.wicket.ajax.AbstractDefaultAjaxBehavior; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.attributes.AjaxRequestAttributes; import org.apache.wicket.behavior.AttributeAppender; import org.apache.wicket.markup.head.IHeaderResponse; import org.apache.wicket.markup.head.JavaScriptHeaderItem; import org.apache.wicket.markup.head.OnLoadHeaderItem; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.panel.EmptyPanel; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.request.resource.JavaScriptResourceReference; import org.apache.wicket.request.resource.ResourceReference; import org.apache.wicket.spring.injection.annot.SpringBean; import ro.nextreports.engine.ReportRunnerException; import ro.nextreports.engine.exporter.exception.NoDataFoundException; import ro.nextreports.engine.exporter.util.IndicatorData; import ro.nextreports.server.service.DashboardService; import ro.nextreports.server.web.NextServerApplication; import ro.nextreports.server.web.dashboard.WidgetView; import ro.nextreports.server.web.dashboard.chart.ChartHTML5Panel; import ro.nextreports.server.web.dashboard.model.WidgetModel; public class IndicatorWidgetView extends WidgetView { private static final long serialVersionUID = 1L; private final ResourceReference INDICATOR_UTIL_JS = new JavaScriptResourceReference(ChartHTML5Panel.class, NextServerApplication.NEXT_CHARTS_JS); private String PARAM = "Param"; private String isHTML5 = ""; private WebMarkupContainer container; @SpringBean private DashboardService dashboardService; public IndicatorWidgetView(String id, WidgetModel model, boolean zoom) { this(id, model, zoom, null); } @SuppressWarnings("unchecked") public IndicatorWidgetView(String id, WidgetModel model, boolean zoom, Map<String, Object> urlQueryParameters) { super(id, model, zoom); final String widgetId = model.getWidgetId(); final IndicatorModel indicatorModel = new IndicatorModel(widgetId, urlQueryParameters); setModel(new CompoundPropertyModel(indicatorModel)); add(new Label("error", new Model<String>()) { private static final long serialVersionUID = 1L; @Override protected void onInitialize() { super.onInitialize(); if (indicatorModel.getObject() == null) { if (indicatorModel.getError() instanceof NoDataFoundException) { setDefaultModelObject(getString("ActionContributor.Run.nodata")); } else { setDefaultModelObject(ExceptionUtils.getRootCauseMessage(indicatorModel.getError())); } } } @Override public boolean isVisible() { return indicatorModel.hasError(); } }); container = new WebMarkupContainer("indicator") { private static final long serialVersionUID = 1L; @Override public boolean isVisible() { return !indicatorModel.hasError(); } }; container.setOutputMarkupId(true); // indicator background color is set to the entire container Color background = indicatorModel.getObject().getBackground(); String s = "background-color: rgb(" + background.getRed() + "," + background.getGreen() + "," + background.getBlue() + ");"; container.add(new AttributeAppender("style", s)); add(container); add(new HTML5Behavior(zoom, indicatorModel)); container.add(new EmptyPanel("image")); //IndicatorHTML5Panel panel = new IndicatorHTML5Panel("image", width, height, indicatorModel); //IndicatorImagePanel panel = new IndicatorImagePanel("image", width, height, indicatorModel); //container.add(panel); } private IndicatorData getIndicatorData(String widgetId, Map<String, Object> urlQueryParameters) { try { return dashboardService.getIndicatorData(widgetId, urlQueryParameters); } catch (ReportRunnerException e) { throw new RuntimeException(e); } catch (NoDataFoundException e) { IndicatorData data = new IndicatorData(); data.setTitle("No Data"); return data; } catch (TimeoutException e) { IndicatorData data = new IndicatorData(); data.setTitle("Timeout Elapsed"); return data; } } private class IndicatorModel extends LoadableDetachableModel<IndicatorData> { private static final long serialVersionUID = 1L; private Exception error; private String widgetId; private Map<String, Object> urlQueryParameters; public IndicatorModel(String widgetId, Map<String, Object> urlQueryParameters) { super(); this.widgetId = widgetId; this.urlQueryParameters = urlQueryParameters; } @Override protected IndicatorData load() { error = null; try { return getIndicatorData(widgetId, urlQueryParameters); } catch (Exception e) { e.printStackTrace(); error = e; return null; } } public Exception getError() { return error; } public boolean hasError() { return error != null; } } // if canvas is supported for HTML5 we will show IndicatorHTML5Panel // else we will show IndicatorImagePanel private class HTML5Behavior extends AbstractDefaultAjaxBehavior { private static final long serialVersionUID = 1L; private String width; private String height; private IndicatorModel indicatorModel; public HTML5Behavior(boolean zoom, IndicatorModel indicatorModel) { super(); this.indicatorModel = indicatorModel; // height used to have two indicators (one under the other) in dashbord to occupy same height as a single chart width = "200"; height = "122"; if (zoom) { width = "100%"; height = "100%"; } } @Override protected void updateAjaxAttributes(AjaxRequestAttributes attributes) { super.updateAjaxAttributes(attributes); StringBuilder javaScript = new StringBuilder(); javaScript.append("var data = isCanvasEnabled();"); javaScript.append("console.log(data);"); javaScript.append("return { '" + PARAM + "': data }"); attributes.getDynamicExtraParameters().add(javaScript); } @Override public void renderHead(Component component, IHeaderResponse response) { super.renderHead(component, response); //include js file response.render(JavaScriptHeaderItem.forReference(INDICATOR_UTIL_JS)); response.render(OnLoadHeaderItem.forScript(getCallbackFunctionBody())); } @Override protected void respond(AjaxRequestTarget target) { String param = getRequest().getRequestParameters().getParameterValue(PARAM).toString(); //System.out.println("---> "+param); // behavior is called on any refresh, we have to call it only once // (otherwise the panel will be replaced in the same time the old one is refreshed) if (isHTML5.isEmpty()) { isHTML5 = param; if (Boolean.parseBoolean(param)) { container.replace(new IndicatorHTML5Panel("image", width, height, indicatorModel).setOutputMarkupId(true)); } else { // for image height must be a little less than html5 panel // to have two indicators (one under the other) in dashboard to occupy same height as a single chart if ("122".equals(height)) { height = "120"; } container.replace(new IndicatorImagePanel("image", width, height, indicatorModel).setOutputMarkupId(true)); } target.add(container); } } } }