Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » Remote Application Platform (RAP) » Invalid Thread Access exception throws when using ChartJs during multiple sessions(Invalid Thread Access exception throws when using ChartJs during multiple sessions)
Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766876] Wed, 28 June 2017 13:03 Go to next message
Sudesh Bulathsinhala is currently offline Sudesh BulathsinhalaFriend
Messages: 193
Registered: October 2010
Senior Member
Hello,

We're working on a new framework to assist "LOW CODE APP" development using eclipse platform called rubix4eclipse.

https://rubix4eclipse.wordpress.com/

The framework is supporting single-source enterprise application development out of the box (RCP and RAP) with minimum java code.

We're using org.eclipse.rap.chartjs as our RAP chart provider and org.swtchart as our RCP chart provider. We have separate targets for each RCP and RAP with respective bundles.
RAP includes chartjs bundle.

Everything works fine until we start RAP client in multi sessions.
We get following error !
Invalid thread access exception

I tried to wrap the redraw() method of Chart.java around asyncExec, but still the same issue when running multi sessions, opening the same editor with chart.

Display.getDefault().asyncExec(new Runnable() {
       @Override
        public void run() {
                redraw();
        }
        });


any idea how to fix ?
if i remove the chart from the editor, everything works fine ! (multi sessions)

FYI > multi session throws error, only after starting record navigation from both sessions, where respective data is loaded to chart for each record. Just opening the editor with chart without record actions, in both sessions do not throw the error and i can see the chart with data immediately after loading both editors. Only when we start interacting with both editors(multi sessions) using record actions, it throws the following exception.

org.eclipse.swt.SWTException: Invalid thread access
	at org.eclipse.swt.SWT.error(SWT.java:3708)
	at org.eclipse.swt.SWT.error(SWT.java:3631)
	at org.eclipse.swt.SWT.error(SWT.java:3602)
	at org.eclipse.swt.widgets.Widget.error(Widget.java:1018)
	at org.eclipse.swt.widgets.Widget.checkWidget(Widget.java:956)
	at org.eclipse.swt.widgets.Widget.setData(Widget.java:317)
	at org.eclipse.swt.widgets.Control.setData(Control.java:2519)
	at org.eclipse.rap.chartjs.Chart.drawChart(Chart.java:122)
	at org.eclipse.rap.chartjs.Chart.drawBarChart(Chart.java:74)
	at com.rubixobjects.widgets.charts.DatasetBarChartWidgetHelperImpl.setObserverDataValueList(DatasetBarChartWidgetHelperImpl.java:156)
	at com.rubixobjects.widgets.controller.DatasetController.setChartValues(DatasetController.java:1019)
	at com.rubixobjects.widgets.dataset.ClientActionExecutionProvider.processNonInteruptingActions(ClientActionExecutionProvider.java:290)
	at com.rubixobjects.widgets.dataset.ClientActionExecutionProvider.executeClientActionsAsynchronously(ClientActionExecutionProvider.java:205)
	at com.rubixobjects.widgets.dataset.DatasetWidget.notifyObserverDataChange(DatasetWidget.java:394)
	at com.rubixobjects.widgets.controller.DatasetProvider.notifyDataChange(DatasetProvider.java:455)
	at com.rubixobjects.widgets.data.DatasetTextWidget.extractEntityRecordColumns(DatasetTextWidget.java:452)
	at com.rubixobjects.widgets.data.DatasetTextWidget.notifyObserverRecordsetChange(DatasetTextWidget.java:616)
	at com.rubixobjects.widgets.controller.DatasetProvider.notifyRecordViewObservers(DatasetProvider.java:621)
	at com.rubixobjects.widgets.dataset.RecordDatasetWidget.recordViewAction(RecordDatasetWidget.java:1802)
	at com.rubixobjects.widgets.controller.DatasetProvider.notifyRecordView(DatasetProvider.java:609)
	at com.rubixobjects.widgets.detailtable.DatasetDetailTable$4.doubleClick(DatasetDetailTable.java:399)
	at org.eclipse.jface.viewers.StructuredViewer$1.run(StructuredViewer.java:828)
	at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
	at org.eclipse.ui.internal.JFaceUtil$1.run(JFaceUtil.java:52)
	at org.eclipse.jface.util.SafeRunnable.run(SafeRunnable.java:177)
	at org.eclipse.jface.viewers.StructuredViewer.fireDoubleClick(StructuredViewer.java:826)
	at org.eclipse.jface.viewers.StructuredViewer.handleDoubleSelect(StructuredViewer.java:1139)
	at org.eclipse.jface.viewers.StructuredViewer$4.widgetDefaultSelected(StructuredViewer.java:1251)
	at org.eclipse.jface.util.OpenStrategy.fireDefaultSelectionEvent(OpenStrategy.java:242)
	at org.eclipse.jface.util.OpenStrategy.access$0(OpenStrategy.java:239)
	at org.eclipse.jface.util.OpenStrategy$1.handleEvent(OpenStrategy.java:304)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:109)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:687)
	at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:594)
	at org.eclipse.swt.widgets.Display.executeNextEvent(Display.java:1217)
	at org.eclipse.swt.widgets.Display.runPendingMessages(Display.java:1198)
	at org.eclipse.swt.widgets.Display.safeReadAndDispatch(Display.java:1181)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:1173)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2733)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2694)
	at org.eclipse.ui.internal.Workbench.access$5(Workbench.java:2530)
	at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:701)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:684)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:157)
	at com.rubix.erp.core.Application.start(Application.java:19)
	at org.eclipse.rap.ui.internal.application.EntryPointApplicationWrapper.createUI(EntryPointApplicationWrapper.java:38)
	at org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle.createUI(RWTLifeCycle.java:177)
	at org.eclipse.rap.rwt.internal.lifecycle.RWTLifeCycle$UIThreadController.run(RWTLifeCycle.java:290)
	at java.lang.Thread.run(Thread.java:745)
	at org.eclipse.rap.rwt.internal.lifecycle.UIThread.run(UIThread.java:107)

  • Attachment: rbac2.png
    (Size: 124.94KB, Downloaded 212 times)
  • Attachment: rbac1.png
    (Size: 121.69KB, Downloaded 205 times)
  • Attachment: error.png
    (Size: 202.42KB, Downloaded 239 times)

[Updated on: Wed, 28 June 2017 13:51]

Report message to a moderator

Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766887 is a reply to message #1766876] Wed, 28 June 2017 14:51 Go to previous messageGo to next message
Ivan Furnadjiev is currently offline Ivan FurnadjievFriend
Messages: 2426
Registered: July 2009
Location: Sofia, Bulgaria
Senior Member
Hi,
do you want to redraw a chart from a background thread? Do you have any statics in your app? Such an errors usually happen when something is kept into a static field and it's shared between the sessions.
You should not use Display.getDefault(), because in a background thread you don't know what is UI session used. You should provide a valid display object from a valid UI session or use uiSession.exec().
HTH,
Ivan
Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766889 is a reply to message #1766887] Wed, 28 June 2017 15:08 Go to previous messageGo to next message
Sudesh Bulathsinhala is currently offline Sudesh BulathsinhalaFriend
Messages: 193
Registered: October 2010
Senior Member
Hello Ivan,

The problem comes from the Chartjs bundle we use for rendering chart on RAP client.
Yes, I see lot of static fields in chart.java bundled inside org.eclipse.rap.chartjs.

Please find below the Chart.java >>

/*******************************************************************************
 * Copyright (c) 2014 EclipseSource and others.
 * 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:
 *    EclipseSource - initial API and implementation
 ******************************************************************************/

package org.eclipse.rap.chartjs;

import java.io.IOException;
import java.io.InputStream;

import org.eclipse.rap.chartjs.internal.ChartPaintListener;
import org.eclipse.rap.json.JsonObject;
import org.eclipse.rap.json.JsonValue;
import org.eclipse.rap.rwt.RWT;
import org.eclipse.rap.rwt.client.service.JavaScriptLoader;
import org.eclipse.rap.rwt.lifecycle.WidgetUtil;
import org.eclipse.rap.rwt.scripting.ClientListener;
import org.eclipse.rap.rwt.service.ResourceManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;


@SuppressWarnings("deprecation")
public class Chart extends Canvas {

  private static final String CHART_OPTIONS = "chartOptions";
  private static final String CHART_DATA = "chartData";
  private static final String CHART_TYPE = "chartType";
  private static final String CHART_MIN_JS = "Chart.js";

  private static final String LINE_CHART = "Line";
  private static final String PIE_CHART = "Pie";
  private static final String BAR_CHART = "Bar";
  private static final String RADAR_CHART = "Radar";
  private static final String POLAR_AREA_CHART = "PolarArea";
  private static final String DOUGHNUT_CHART = "Doughnut";

  public Chart( Composite parent, int style ) {
    super( parent, style );
    registerJS();
    requireJS();
    // NOTE: RAP re-transfers all attached widget data, even if only one of them changes,
    //       but JsonObject/JsonArray aren't deep-compared
    WidgetUtil.registerDataKeys( CHART_TYPE, CHART_DATA, CHART_OPTIONS );
    addPaintListener();
    applyFixes();
  }

  public void drawLineChart( ChartRowData data ) {
    drawLineChart( data, null );
  }

  public void drawLineChart( ChartRowData data, ChartOptions options ) {
    drawChart( LINE_CHART, getLineChartOptions( options ), data.toJson() );
  }

  public void drawBarChart( ChartRowData data ) {
    drawBarChart( data, null );
  }

  public void drawBarChart( ChartRowData data, ChartOptions options ) {
    drawChart( BAR_CHART, getBarChartOptions( options ), data.toJson() );
  }

  public void drawRadarChart( ChartRowData data ) {
    drawRadarChart( data, null );
  }

  public void drawRadarChart( ChartRowData data, ChartOptions options ) {
    drawChart( RADAR_CHART, getLineChartOptions( options ), data.toJson() );
  }

  public void drawPolarAreaChart( ChartPointData data ) {
    drawPolarAreaChart( data, null );
  }

  public void drawPolarAreaChart( ChartPointData data, ChartOptions options ) {
    drawChart( POLAR_AREA_CHART, getSegmentChartOptions( options ), data.toJson() );
  }

  public void drawPieChart( ChartPointData data ) {
    drawPieChart( data, null );
  }

  public void drawPieChart( ChartPointData data, ChartOptions options ) {
    drawChart( PIE_CHART, getSegmentChartOptions( options ), data.toJson() );
  }

  public void drawDoughnutChart( ChartPointData data ) {
    drawDoughnutChart( data, null );
  }

  public void drawDoughnutChart( ChartPointData data, ChartOptions options ) {
    drawChart( DOUGHNUT_CHART, getSegmentChartOptions( options ), data.toJson() );
  }

  public void clear() {
    setData( CHART_TYPE, JsonObject.NULL ); // "null" won't be synchronized
    redraw();
  }

  private void drawChart( String type, JsonObject options, JsonValue data ) {
    setData( CHART_TYPE, type );
    setData( CHART_OPTIONS, options  );
    setData( CHART_DATA, data );
    redraw();
  }

  private JsonObject getSegmentChartOptions( ChartOptions options ) {
    if( options == null ) {
      return new JsonObject();
    }
    JsonObject json = options.toJson();
    json.set( "segmentShowStroke", json.get( "showStroke" ).asBoolean() );
    json.set( "segmentStrokeWidth", json.get( "strokeWidth" ).asInt() );
    return json;
  }

  private JsonObject getBarChartOptions( ChartOptions options ) {
    if( options == null ) {
      return new JsonObject();
    }
    JsonObject json = options.toJson();
    json.set( "barShowStroke", json.get( "showStroke" ).asBoolean() );
    json.set( "barStrokeWidth", json.get( "strokeWidth" ).asInt() );
    return json;
  }

  private JsonObject getLineChartOptions( ChartOptions options ) {
    if( options == null ) {
      return new JsonObject();
    }
    JsonObject json = options.toJson();
    json.set( "datasetStroke", json.get( "showStroke" ).asBoolean() );
    json.set( "datasetStrokeWidth", json.get( "strokeWidth" ).asInt() );
    return json;
  }

  private void addPaintListener() {
    addListener( SWT.Paint, ChartPaintListener.getInstance() );
  }

  private void applyFixes() {
    // See RAP Bug 391414
    ClientListener listener = new ClientListener(
        "function handleEvent( event ) {\n"
      + "  event.widget.redraw();"
      + "}\n"
    );
    addListener( SWT.Show, listener );
  }

  private void requireJS() {
    JavaScriptLoader service = RWT.getClient().getService( JavaScriptLoader.class );
    service.require( RWT.getResourceManager().getLocation( CHART_MIN_JS ) );
  }

  private void registerJS() {
    ResourceManager manager = RWT.getResourceManager();
    if( !manager.isRegistered( CHART_MIN_JS ) ) {
      InputStream inputStream = ChartPaintListener.class.getResourceAsStream( CHART_MIN_JS );
      manager.register( CHART_MIN_JS, inputStream );
      try {
        inputStream.close();
      } catch( IOException e ) {
        throw new RuntimeException( e );
      }
    }
  }

}


Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766892 is a reply to message #1766889] Wed, 28 June 2017 15:13 Go to previous messageGo to next message
Ivan Furnadjiev is currently offline Ivan FurnadjievFriend
Messages: 2426
Registered: July 2009
Location: Sofia, Bulgaria
Senior Member
Hi,
I see only static string constants.
The stack trace is similar to another one, posted in the newsgroup recently. Is it possible to create a simple demo project (or patch against our workbench demo) to demonstrate the issue?
Regards,
Ivan
Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766894 is a reply to message #1766892] Wed, 28 June 2017 15:18 Go to previous messageGo to next message
Ivan Furnadjiev is currently offline Ivan FurnadjievFriend
Messages: 2426
Registered: July 2009
Location: Sofia, Bulgaria
Senior Member
... BTW, which RAP version are you using?
Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766895 is a reply to message #1766889] Wed, 28 June 2017 15:25 Go to previous messageGo to next message
Ivan Furnadjiev is currently offline Ivan FurnadjievFriend
Messages: 2426
Registered: July 2009
Location: Sofia, Bulgaria
Senior Member
... also the latest RAP chart addon is in the incubator [1].

[1] http://git.eclipse.org/c/rap/incubator/org.eclipse.rap.incubator.chart.git
Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766903 is a reply to message #1766895] Wed, 28 June 2017 16:12 Go to previous messageGo to next message
Sudesh Bulathsinhala is currently offline Sudesh BulathsinhalaFriend
Messages: 193
Registered: October 2010
Senior Member
Hello Ivan,

We're using RAP 3.1, it's time to migrate to 3.2 and check how much it has in store to offer this framework :)
Sure we will invest more time on suggested chart from incubator (http://git.eclipse.org/c/rap/incubator/org.eclipse.rap.incubator.chart.git)

Coming back to the context, I think i will explain the architecture abit.

We have 2 type of bundles target for both RCP and RAP.

COMMON BUNDLES
com.eclipsesource.widgets.gmaps
com.rubixobjects.emf.model
com.rubixobjects.entity
com.rubixobjects.utils
com.rubixobjects.validator
com.rubixobjects.widgets
log4j.properties
org.apache.log4j

RCP BUNDLES
com.rubixobjects.designer
com.rubixobjects.explorer
com.rubixobjects.jobs
com.rubixobjects.model
com.rubixobjects.res
com.rubixobjects.widgets.rcp

RAP BUNDLES
com.rubixobjects.widgets.rap
org.eclipse.rap.chartjs

RAP bundles compared to RCP is minimum, as RAP only requires minimal set of bundles for runtime rendering. However RCP requires additional bundles for application life cycle management, which includes importing entities and designing editors using drag'n drop, etc.

Most SWT widgets works for both RCP and RAP, like Text, Combo, Date, etc. However when we don't have such luxury, for example in the case of Charts, we use 2 different providers. For RCP we use swtchartand RAP we use chartjs. The architecture does not impose RCP or RAP boundaries for end user. End user will be creating meta models. These models get dynamically loaded by composite assembly factories and creates respective RCP, RAP editor UI counterparts. For that we're using 2 fragment projects.

FRAGMENT BUNDLES

com.rubixobjects.widgets.rcp
com.rubixobjects.widgets.rap

When chart model instance is found during rendering by component assembly factory, it uses following to delegate the call to respective fragments (RCP or RAP).
At runtime, this approach delegates the call to RCP or RAP, enabling the framework to pick the component dynamically. Both com.rubixobjects.widgets.rcp and com.rubixobjects.widgets.rap bundles provide their respective chart implementations.

package com.rubixobjects.widgets.charts;

import java.util.List;

import org.eclipse.swt.widgets.Composite;

import com.rubixobjects.entity.core.EntityObject;
import com.rubixobjects.widgets.ImplementationLoader;
import com.rubixobjects.widgets.controller.IDatasetBroadcast;
import com.rubixobjects.widgets.sys.BaseDatasetWidget;

public abstract class DatasetBarChartWidgetHelper extends BaseDatasetWidget {

	private static final DatasetBarChartWidgetHelper IMPL;

	static {
		IMPL = (DatasetBarChartWidgetHelper) ImplementationLoader.newInstance(DatasetBarChartWidgetHelper.class);
	}

	public static Object createWidget(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations) {
		return IMPL.create(name, caption, datasetBroadcast, composite, entity, chartConfigurations);
	}

	protected abstract Object create(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations);

}


Herewith I'm attaching DatasetBarChartWidgetHelperImpl, which is the implementation provider for RAP in com.rubixobjects.widgets.rap bundle.

package com.rubixobjects.widgets.charts;

import java.util.List;

import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.rap.chartjs.Chart;
import org.eclipse.rap.chartjs.ChartOptions;
import org.eclipse.rap.chartjs.ChartRowData;
import org.eclipse.rap.chartjs.ChartStyle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;

import com.rubixobjects.entity.column.type.ChartItemColumn;
import com.rubixobjects.entity.column.type.EntityColumn;
import com.rubixobjects.entity.column.type.IChartEntityColumn;
import com.rubixobjects.entity.core.EntityObject;
import com.rubixobjects.entity.core.RecordActionType;
import com.rubixobjects.widgets.controller.IDatasetBroadcast;
import com.rubixobjects.widgets.controller.IDatasetChartObserver;
import com.rubixobjects.widgets.controller.IDatasetObserver;

public class DatasetBarChartWidgetHelperImpl extends DatasetBarChartWidgetHelper
		implements IDatasetObserver, IDatasetChartObserver {

	private Chart chart;

	@Override
	protected Object create(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations) {

		setDatasetBroadcast(datasetBroadcast);
		datasetBroadcast.addObserver(this);
		this.name = name;

		// chart-composite
		final Composite chartComposite = new Composite(composite, SWT.NONE);
		chartComposite.setBackgroundMode(SWT.INHERIT_DEFAULT);
		chartComposite.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
		final FormLayout layout = new FormLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.marginLeft = 0;
		layout.marginRight = 0;
		layout.marginTop = 0;
		layout.marginBottom = 0;
		chartComposite.setLayout(layout);
		final FormData data = new FormData();
		data.left = new FormAttachment(0);
		data.right = new FormAttachment(100);
		data.top = new FormAttachment(0);
		data.bottom = new FormAttachment(100);
		chartComposite.setLayoutData(data);

		// header
		final CLabel header = new CLabel(chartComposite, SWT.NONE);
		header.setText(caption);
		header.setForeground(new Color(chartComposite.getDisplay(), 112, 146, 190));
		header.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
		FormData formData = new FormData();
		formData.left = new FormAttachment(0, 7);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(0, 7);
		header.setLayoutData(formData);

		// separator
		final Label seperator = new Label(chartComposite, SWT.SEPARATOR | SWT.SHADOW_OUT | SWT.HORIZONTAL);
		formData = new FormData();
		formData.left = new FormAttachment(0, 7);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(header);
		seperator.setLayoutData(formData);

		// chart
		chart = new Chart(chartComposite, SWT.NONE);
		formData = new FormData();
		formData.left = new FormAttachment(0);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(seperator);
		formData.bottom = new FormAttachment(100);
		chart.setLayoutData(formData);

		final EntityColumn column = entity.getChartByName(name);

		final String[] namesarray = new String[((IChartEntityColumn) column).getItems().size()];
		final int[] valuesarray = new int[((IChartEntityColumn) column).getItems().size()];
		for (int index = 0; index < ((IChartEntityColumn) column).getItems().size(); index++) {
			namesarray[index] = ((IChartEntityColumn) column).getItems().get(index).getName();
			valuesarray[index] = ((IChartEntityColumn) column).getItems().get(index).getValue().intValue();
		}

		final ChartOptions options = new ChartOptions();
		options.setAnimation(true);
		options.setShowToolTips(true);
		options.setScaleBeginAtZero(true);
		options.setBezierCurve(true);
		options.setShowFill(true);
		options.setScaleShowLabels(true);
		options.setPointDotRadius(3);
		options.setStrokeWidth(1);

		final ChartStyle chartStyle = new ChartStyle(151, 187, 205, 0.7f);
		chartStyle.setPointColor(new RGB(100, 220, 100));
		final ChartRowData chartrowdata = new ChartRowData(namesarray).addRow(valuesarray, chartStyle);

		chart.drawBarChart(chartrowdata, options);
		return this;
	}

	@Override
	public String getName() {
		return name;
	}

	/* -------------------------------------- */
	/* OBSERVER IMPLEMENTATION */
	/* -------------------------------------- */

	@Override
	public String getObserverName() {
		return name;
	}

	@Override
	public void setObserverDataValueList(final List<ChartItemColumn> chartItems) {
		final String[] namesarray = new String[chartItems.size()];
		final int[] valuesarray = new int[chartItems.size()];

		for (int index = 0; index < chartItems.size(); index++) {
			namesarray[index] = chartItems.get(index).getName();
			valuesarray[index] = chartItems.get(index).getValue().intValue();
		}

		final ChartOptions options = new ChartOptions();
		options.setAnimation(true);
		options.setShowToolTips(true);
		options.setScaleBeginAtZero(true);
		options.setBezierCurve(true);
		options.setShowFill(true);
		options.setScaleShowLabels(true);
		options.setPointDotRadius(1);
		options.setStrokeWidth(1);

		final ChartStyle chartStyle = new ChartStyle(151, 187, 205, 0.7f);
		chartStyle.setPointColor(new RGB(100, 220, 100));
		final ChartRowData chartrowdata = new ChartRowData(namesarray).addRow(valuesarray, chartStyle);

		if (chart != null && chart.isDisposed() == false) {
			chart.drawBarChart(chartrowdata, options);
		}
	}

	@Override
	public void clearObserver() {
		// TODO Auto-generated method stub
	}

	@Override
	public void setObserverEnable() {
		// TODO Auto-generated method stub
	}

	@Override
	public void setObserverDisable() {
		// TODO Auto-generated method stub
	}

	@Override
	public void notifyObserverActionEvent(final String identifier, final RecordActionType recordActionType,
			final boolean isRequired) {
		// TODO Auto-generated method stub
	}

}

Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766913 is a reply to message #1766903] Wed, 28 June 2017 17:00 Go to previous messageGo to next message
Ivan Furnadjiev is currently offline Ivan FurnadjievFriend
Messages: 2426
Registered: July 2009
Location: Sofia, Bulgaria
Senior Member
Hi Sudesh,
everything looks good and the single-source approach you choose is perfectly fine. Are you calling org.eclipse.rap.chartjs.Chart.drawBarChart() from a non UI thread? Use Thread.currentThread() to dump the current thread before calling redraw. It seems this thread is different from the thread returned by chart.getDisplay().getThread();

Try to wrap drawBarChart in asyncExec like:
chart.getDisplay().asyncExec( new Runnable() {
...
});

HTH,
Ivan
Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766928 is a reply to message #1766913] Wed, 28 June 2017 18:29 Go to previous messageGo to next message
Sudesh Bulathsinhala is currently offline Sudesh BulathsinhalaFriend
Messages: 193
Registered: October 2010
Senior Member
Hello Ivan,

In my DatasetBarChartWidgetHelperImpl chart implementation provider, i'm calling the drawBarChart like this.
Now the behavior is somewhat different, the latest session gets the upper hand and the previous ones don't draw chart data during record navigation.
However the application is not throwing Invalid thread exception any more and the other parts remain to load the data during navigation.

Change >>
		
                if (chart != null && chart.isDisposed() == false) {
			chart.getDisplay().asyncExec(new Runnable() {
				@Override
				public void run() {
					chart.drawBarChart(chartrowdata, options);
				}
			});
		}



DatasetBarChartWidgetHelperImpl
package com.rubixobjects.widgets.charts;

import java.util.List;

import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.rap.chartjs.Chart;
import org.eclipse.rap.chartjs.ChartOptions;
import org.eclipse.rap.chartjs.ChartRowData;
import org.eclipse.rap.chartjs.ChartStyle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;

import com.rubixobjects.entity.column.type.ChartItemColumn;
import com.rubixobjects.entity.column.type.EntityColumn;
import com.rubixobjects.entity.column.type.IChartEntityColumn;
import com.rubixobjects.entity.core.EntityObject;
import com.rubixobjects.entity.core.RecordActionType;
import com.rubixobjects.widgets.controller.IDatasetBroadcast;
import com.rubixobjects.widgets.controller.IDatasetChartObserver;
import com.rubixobjects.widgets.controller.IDatasetObserver;

public class DatasetBarChartWidgetHelperImpl extends DatasetBarChartWidgetHelper
		implements IDatasetObserver, IDatasetChartObserver {

	private Chart chart;

	@Override
	protected Object create(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations) {

		setDatasetBroadcast(datasetBroadcast);
		datasetBroadcast.addObserver(this);
		this.name = name;

		// chart-composite
		final Composite chartComposite = new Composite(composite, SWT.NONE);
		chartComposite.setBackgroundMode(SWT.INHERIT_DEFAULT);
		chartComposite.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
		final FormLayout layout = new FormLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.marginLeft = 0;
		layout.marginRight = 0;
		layout.marginTop = 0;
		layout.marginBottom = 0;
		chartComposite.setLayout(layout);
		final FormData data = new FormData();
		data.left = new FormAttachment(0);
		data.right = new FormAttachment(100);
		data.top = new FormAttachment(0);
		data.bottom = new FormAttachment(100);
		chartComposite.setLayoutData(data);

		// header
		final CLabel header = new CLabel(chartComposite, SWT.NONE);
		header.setText(caption);
		header.setForeground(new Color(chartComposite.getDisplay(), 112, 146, 190));
		header.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
		FormData formData = new FormData();
		formData.left = new FormAttachment(0, 7);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(0, 7);
		header.setLayoutData(formData);

		// separator
		final Label seperator = new Label(chartComposite, SWT.SEPARATOR | SWT.SHADOW_OUT | SWT.HORIZONTAL);
		formData = new FormData();
		formData.left = new FormAttachment(0, 7);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(header);
		seperator.setLayoutData(formData);

		// chart
		chart = new Chart(chartComposite, SWT.NONE);
		formData = new FormData();
		formData.left = new FormAttachment(0);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(seperator);
		formData.bottom = new FormAttachment(100);
		chart.setLayoutData(formData);

		final EntityColumn column = entity.getChartByName(name);

		final String[] namesarray = new String[((IChartEntityColumn) column).getItems().size()];
		final int[] valuesarray = new int[((IChartEntityColumn) column).getItems().size()];
		for (int index = 0; index < ((IChartEntityColumn) column).getItems().size(); index++) {
			namesarray[index] = ((IChartEntityColumn) column).getItems().get(index).getName();
			valuesarray[index] = ((IChartEntityColumn) column).getItems().get(index).getValue().intValue();
		}

		final ChartOptions options = new ChartOptions();
		options.setAnimation(true);
		options.setShowToolTips(true);
		options.setScaleBeginAtZero(true);
		options.setBezierCurve(true);
		options.setShowFill(true);
		options.setScaleShowLabels(true);
		options.setPointDotRadius(3);
		options.setStrokeWidth(1);

		final ChartStyle chartStyle = new ChartStyle(151, 187, 205, 0.7f);
		chartStyle.setPointColor(new RGB(100, 220, 100));
		final ChartRowData chartrowdata = new ChartRowData(namesarray).addRow(valuesarray, chartStyle);

		if (chart != null && chart.isDisposed() == false) {
			chart.getDisplay().asyncExec(new Runnable() {
				@Override
				public void run() {
					chart.drawBarChart(chartrowdata, options);
				}
			});
		}

		return this;
	}

	@Override
	public String getName() {
		return name;
	}

	/* -------------------------------------- */
	/* OBSERVER IMPLEMENTATION */
	/* -------------------------------------- */

	@Override
	public String getObserverName() {
		return name;
	}

	@Override
	public void setObserverDataValueList(final List<ChartItemColumn> chartItems) {
		final String[] namesarray = new String[chartItems.size()];
		final int[] valuesarray = new int[chartItems.size()];

		for (int index = 0; index < chartItems.size(); index++) {
			namesarray[index] = chartItems.get(index).getName();
			valuesarray[index] = chartItems.get(index).getValue().intValue();
		}

		final ChartOptions options = new ChartOptions();
		options.setAnimation(true);
		options.setShowToolTips(true);
		options.setScaleBeginAtZero(true);
		options.setBezierCurve(true);
		options.setShowFill(true);
		options.setScaleShowLabels(true);
		options.setPointDotRadius(1);
		options.setStrokeWidth(1);

		final ChartStyle chartStyle = new ChartStyle(151, 187, 205, 0.7f);
		chartStyle.setPointColor(new RGB(100, 220, 100));
		final ChartRowData chartrowdata = new ChartRowData(namesarray).addRow(valuesarray, chartStyle);

		if (chart != null && chart.isDisposed() == false) {
			chart.getDisplay().asyncExec(new Runnable() {
				@Override
				public void run() {
					chart.drawBarChart(chartrowdata, options);
				}
			});
		}
	}

	@Override
	public void clearObserver() {
		// TODO Auto-generated method stub
	}

	@Override
	public void setObserverEnable() {
		// TODO Auto-generated method stub
	}

	@Override
	public void setObserverDisable() {
		// TODO Auto-generated method stub
	}

	@Override
	public void notifyObserverActionEvent(final String identifier, final RecordActionType recordActionType,
			final boolean isRequired) {
		// TODO Auto-generated method stub
	}

}

Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1766964 is a reply to message #1766928] Thu, 29 June 2017 07:06 Go to previous messageGo to next message
Ivan Furnadjiev is currently offline Ivan FurnadjievFriend
Messages: 2426
Registered: July 2009
Location: Sofia, Bulgaria
Senior Member
Hi Sudesh,
I can't say anything more. You have to debug it yourself. What is the thread in your provider? What is the thread (display/ uiSession) in your chart? Is it possible for you not to create UI from non UI thread? All these questions must be answered during the debugging. But for me the main question is - what is the thread in DatasetBarChartWidgetHelperImpl? Is this thread executed outside a request? Maybe you need syncExec instead of asyncExec?
Regards,
Ivan
Re: Invalid Thread Access exception throws when using ChartJs during multiple sessions [message #1767206 is a reply to message #1766964] Mon, 03 July 2017 14:39 Go to previous message
Sudesh Bulathsinhala is currently offline Sudesh BulathsinhalaFriend
Messages: 193
Registered: October 2010
Senior Member
Hello Ivan,

Found the solution !!!
If we look at single sourcing implementation provider below, we call the create method. This method always return same object instance which internally creates a new chart every time. So basically the same object(DatasetBarChartWidgetHelperImpl) is shared with multiple charts widgets across sessions. We made changes to return new object(DatasetBarChartWidgetHelperImpl) having a new chart for each instance, and it worked !

public abstract class DatasetBarChartWidgetHelper extends BaseDatasetWidget {

	private static final DatasetBarChartWidgetHelper IMPL;

	static {
		IMPL = (DatasetBarChartWidgetHelper) ImplementationLoader.newInstance(DatasetBarChartWidgetHelper.class);
	}

	public static Object createWidget(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations) {
		return IMPL.create(name, caption, datasetBroadcast, composite, entity, chartConfigurations);
	}

	protected abstract Object create(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations);

}


OLD this basically returns the same object with new chart for every call, so the object itself is shared
public class DatasetBarChartWidgetHelperImpl extends DatasetBarChartWidgetHelper
		implements IDatasetObserver, IDatasetChartObserver {

	private Chart chart;

	@Override
	protected Object create(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations) {

		setDatasetBroadcast(datasetBroadcast);
		datasetBroadcast.addObserver(this);
		this.name = name;

		// chart-composite
		final Composite chartComposite = new Composite(composite, SWT.NONE);
		chartComposite.setBackgroundMode(SWT.INHERIT_DEFAULT);
		chartComposite.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WHITE));
		final FormLayout layout = new FormLayout();
		layout.marginWidth = 0;
		layout.marginHeight = 0;
		layout.marginLeft = 0;
		layout.marginRight = 0;
		layout.marginTop = 0;
		layout.marginBottom = 0;
		chartComposite.setLayout(layout);
		final FormData data = new FormData();
		data.left = new FormAttachment(0);
		data.right = new FormAttachment(100);
		data.top = new FormAttachment(0);
		data.bottom = new FormAttachment(100);
		chartComposite.setLayoutData(data);

		// header
		final CLabel header = new CLabel(chartComposite, SWT.NONE);
		header.setText(caption);
		header.setForeground(new Color(chartComposite.getDisplay(), 112, 146, 190));
		header.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DEFAULT_FONT));
		FormData formData = new FormData();
		formData.left = new FormAttachment(0, 7);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(0, 7);
		header.setLayoutData(formData);

		// separator
		final Label seperator = new Label(chartComposite, SWT.SEPARATOR | SWT.SHADOW_OUT | SWT.HORIZONTAL);
		formData = new FormData();
		formData.left = new FormAttachment(0, 7);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(header);
		seperator.setLayoutData(formData);

		// chart
		chart = new Chart(chartComposite, SWT.NONE);
		formData = new FormData();
		formData.left = new FormAttachment(0);
		formData.right = new FormAttachment(100);
		formData.top = new FormAttachment(seperator);
		formData.bottom = new FormAttachment(100);
		chart.setLayoutData(formData);

		final EntityColumn column = entity.getChartByName(name);

		final String[] namesarray = new String[((IChartEntityColumn) column).getItems().size()];
		final int[] valuesarray = new int[((IChartEntityColumn) column).getItems().size()];
		for (int index = 0; index < ((IChartEntityColumn) column).getItems().size(); index++) {
			namesarray[index] = ((IChartEntityColumn) column).getItems().get(index).getName();
			valuesarray[index] = ((IChartEntityColumn) column).getItems().get(index).getValue().intValue();
		}

		final ChartOptions options = new ChartOptions();
		options.setAnimation(true);
		options.setShowToolTips(true);
		options.setScaleBeginAtZero(true);
		options.setBezierCurve(true);
		options.setShowFill(true);
		options.setScaleShowLabels(true);
		options.setPointDotRadius(3);
		options.setStrokeWidth(1);

		final ChartStyle chartStyle = new ChartStyle(151, 187, 205, 0.7f);
		chartStyle.setPointColor(new RGB(100, 220, 100));
		final ChartRowData chartrowdata = new ChartRowData(namesarray).addRow(valuesarray, chartStyle);

		chart.drawBarChart(chartrowdata, options);
		return this;
	}
}



NEW this basically returns the new object with new chart for every call, so the object itself is not shared
public class DatasetBarChartWidgetHelperImpl extends DatasetBarChartWidgetHelper {

	@Override
	protected Object create(final String name, final String caption, final IDatasetBroadcast datasetBroadcast,
			final Composite composite, final EntityObject entity, final List<ChartConfiguration> chartConfigurations) {

		return new DatasetBarChartWidget(name, caption, datasetBroadcast, composite, entity, chartConfigurations);
	}

}


By delegating the DatasetBarChartWidgetHelperImpl to return new instance(DatasetBarChartWidget) for each create method call, now we can keep multiple sessions in perfect harmony.

Thanks Ivan for your valuable suggestions.
Will be starting to integrate Apache Shiro with Rubix framework to implement permission and access control. Will keep you posted further developments.

Regards,
Sudesh

[Updated on: Mon, 03 July 2017 14:43]

Report message to a moderator

Previous Topic:invalid thread access exception thrown on clicking on treeviewer when multiple sessions are on
Next Topic:RAP + Aspectj + Tomcat
Goto Forum:
  


Current Time: Thu Mar 28 14:59:59 GMT 2024

Powered by FUDForum. Page generated in 0.04184 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top