Handling checked exceptions for eclipse plugin - exception

I am working on an Eclipse plugin and am faced with checked exceptions thrown from the API that I am using in my plugin. What is the practice around handling such exceptions in the context of a plugin? I could not find an appropriate Exception subclass from the Plugin development API.
Thanks in advance.

I use logging classes that I found in a book titled "Eclipse Plug-ins", by Eric Clayberg and Dan Rubel.
Here's the main logging class.
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
public class EclipseLogging {
public static void logInfo(Plugin plugin, String pluginID, String message,
boolean display) {
log(plugin, pluginID, IStatus.INFO, IStatus.OK, message, null, display);
}
public static void logInfo(Plugin plugin, String pluginID,
Throwable exception) {
log(plugin, createStatus(pluginID, IStatus.ERROR, IStatus.OK,
"Unexpected Exception", exception));
}
public static void logInfo(Plugin plugin, String pluginID, String message,
Throwable exception) {
log(plugin, createStatus(pluginID, IStatus.ERROR, IStatus.OK, message,
exception));
}
public static void logError(Plugin plugin, String pluginID,
Throwable exception) {
logError(plugin, pluginID, "Unexpected Exception", exception);
}
public static void logError(Plugin plugin, String pluginID, String message,
Throwable exception) {
log(plugin, pluginID, IStatus.ERROR, IStatus.OK, message, exception,
true);
}
public static void log(Plugin plugin, String pluginID, int severity,
int code, String message, Throwable exception, boolean display) {
if (display) {
ExceptionAction action = new ExceptionAction(plugin, message,
exception);
action.run();
}
log(plugin, createStatus(pluginID, severity, code, message, exception));
}
public static IStatus createStatus(String pluginID, int severity, int code,
String message, Throwable exception) {
return new Status(severity, pluginID, code, message, exception);
}
public static void log(Plugin plugin, IStatus status) {
plugin.getLog().log(status);
}
}
And here's the exception action class.
import org.eclipse.core.runtime.Plugin;
import org.eclipse.jface.action.Action;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
public class ExceptionAction extends Action {
private Plugin plugin;
private String message;
private Throwable exception;
public ExceptionAction(Plugin plugin, String message, Throwable exception) {
this.plugin = plugin;
this.message = message;
this.exception = exception;
}
public void run() {
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow window = workbench.getActiveWorkbenchWindow();
if (window != null) {
Shell parentShell = window.getShell();
parentShell.forceActive();
try {
ExceptionDetailsDialog dialog = new ExceptionDetailsDialog(
parentShell, null, null, message, exception, plugin);
dialog.open();
} catch (SWTException e) {
}
}
}
}
And finally, the classes to display the exception.
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.jface.window.SameShellProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* An abstract dialog with a details section that can be shown or hidden by the
* user. Subclasses are responsible for providing the content of the details
* section.
*/
public abstract class AbstractDetailsDialog extends Dialog {
private final String title;
private final String message;
private final Image image;
private Button detailsButton;
private Control detailsArea;
private Point cachedWindowSize;
/**
* Construct a new instance with the specified elements. Note that the
* window will have no visual representation (no widgets) until it is told
* to open. By default, <code>open</code> blocks for dialogs.
*
* #param parentShell
* the parent shell, or <code>null</code> to create a top-level
* shell
* #param title
* the title for the dialog or <code>null</code> for none
* #param image
* the image to be displayed
* #param message
* the message to be displayed
*/
public AbstractDetailsDialog(Shell parentShell, String title, Image image,
String message) {
this(new SameShellProvider(parentShell), title, image, message);
}
/**
* Construct a new instance with the specified elements. Note that the
* window will have no visual representation (no widgets) until it is told
* to open. By default, <code>open</code> blocks for dialogs.
*
* #param parentShell
* the parent shell provider (not <code>null</code>)
* #param title
* the title for the dialog or <code>null</code> for none
* #param image
* the image to be displayed
* #param message
* the message to be displayed
*/
public AbstractDetailsDialog(IShellProvider parentShell, String title,
Image image, String message) {
super(parentShell);
this.title = title;
this.image = image;
this.message = message;
setShellStyle(SWT.DIALOG_TRIM | SWT.RESIZE | SWT.APPLICATION_MODAL);
}
/**
* Configures the given shell in preparation for opening this window in it.
* In our case, we set the title if one was provided.
*/
protected void configureShell(Shell shell) {
super.configureShell(shell);
if (title != null)
shell.setText(title);
}
/**
* Creates and returns the contents of the upper part of this dialog (above
* the button bar). This includes an image, if specified, and a message.
*
* #param parent
* the parent composite to contain the dialog area
* #return the dialog area control
*/
protected Control createDialogArea(Composite parent) {
Composite composite = (Composite) super.createDialogArea(parent);
composite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
if (image != null) {
((GridLayout) composite.getLayout()).numColumns = 2;
Label label = new Label(composite, 0);
image.setBackground(label.getBackground());
label.setImage(image);
label.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER
| GridData.VERTICAL_ALIGN_BEGINNING));
}
Label label = new Label(composite, SWT.WRAP);
if (message != null)
label.setText(message);
GridData data = new GridData(GridData.FILL_HORIZONTAL
| GridData.VERTICAL_ALIGN_CENTER);
data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
label.setLayoutData(data);
label.setFont(parent.getFont());
return composite;
}
/**
* Adds OK and Details buttons to this dialog's button bar.
*
* #param parent
* the button bar composite
*/
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
false);
detailsButton = createButton(parent, IDialogConstants.DETAILS_ID,
IDialogConstants.SHOW_DETAILS_LABEL, false);
}
/**
* The buttonPressed() method is called when either the OK or Details
* buttons is pressed. We override this method to alternately show or hide
* the details area if the Details button is pressed.
*/
protected void buttonPressed(int id) {
if (id == IDialogConstants.DETAILS_ID)
toggleDetailsArea();
else
super.buttonPressed(id);
}
/**
* Toggles the unfolding of the details area. This is triggered by the user
* pressing the Details button.
*/
protected void toggleDetailsArea() {
Point oldWindowSize = getShell().getSize();
Point newWindowSize = cachedWindowSize;
cachedWindowSize = oldWindowSize;
// Show the details area.
if (detailsArea == null) {
detailsArea = createDetailsArea((Composite) getContents());
detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL);
}
// Hide the details area.
else {
detailsArea.dispose();
detailsArea = null;
detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL);
}
/*
* Must be sure to call getContents().computeSize(SWT.DEFAULT,
* SWT.DEFAULT) before calling getShell().setSize(newWindowSize) since
* controls have been added or removed.
*/
// Compute the new window size.
Point oldSize = getContents().getSize();
Point newSize = getContents().computeSize(SWT.DEFAULT, SWT.DEFAULT);
if (newWindowSize == null)
newWindowSize = new Point(oldWindowSize.x, oldWindowSize.y
+ (newSize.y - oldSize.y));
// Crop new window size to screen.
Point windowLoc = getShell().getLocation();
Rectangle screenArea = getContents().getDisplay().getClientArea();
final int pos = screenArea.height - (windowLoc.y - screenArea.y);
if (newWindowSize.y > pos)
newWindowSize.y = pos;
getShell().setSize(newWindowSize);
((Composite) getContents()).layout();
}
/**
* subclasses must implement createDetailsArea to provide content for the
* area of the dialog made visible when the Details button is clicked.
*
* #param parent
* the details area parent
* #return the details area
*/
protected abstract Control createDetailsArea(Composite parent);
}
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.Dictionary;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.jface.window.SameShellProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
* A dialog to display one or more errors to the user, as contained in an
* <code>IStatus</code> object along with the plug-in identifier, name, version
* and provider. If an error contains additional detailed information then a
* Details button is automatically supplied, which shows or hides an error
* details viewer when pressed by the user.
*
* #see org.eclipse.core.runtime.IStatus
*/
public class ExceptionDetailsDialog extends AbstractDetailsDialog {
/**
* The details to be shown ({#link Exception}, {#link IStatus}, or
* <code>null</code> if no details).
*/
private final Object details;
/**
* The plugin triggering this details dialog and whose information is to be
* shown in the details area or <code>null</code> if no plugin details
* should be shown.
*/
private final Plugin plugin;
/**
* Construct a new instance with the specified elements. Note that the
* window will have no visual representation (no widgets) until it is told
* to open. By default, <code>open</code> blocks for dialogs.
*
* #param parentShell
* the parent shell, or <code>null</code> to create a top-level
* shell
* #param title
* the title for the dialog or <code>null</code> for none
* #param image
* the image to be displayed
* #param message
* the message to be displayed
* #param details
* an object whose content is to be displayed in the details
* area, or <code>null</code> for none
* #param plugin
* The plugin triggering this deatils dialog and whose
* information is to be shown in the details area or
* <code>null</code> if no plugin details should be shown.
*/
public ExceptionDetailsDialog(Shell parentShell, String title, Image image,
String message, Object details, Plugin plugin) {
this(new SameShellProvider(parentShell), title, image, message,
details, plugin);
}
/**
* Construct a new instance with the specified elements. Note that the
* window will have no visual representation (no widgets) until it is told
* to open. By default, <code>open</code> blocks for dialogs.
*
* #param parentShell
* the parent shell provider (not <code>null</code>)
* #param title
* the title for the dialog or <code>null</code> for none
* #param image
* the image to be displayed
* #param message
* the message to be displayed
* #param details
* an object whose content is to be displayed in the details
* area, or <code>null</code> for none
* #param plugin
* The plugin triggering this deatils dialog and whose
* information is to be shown in the details area or
* <code>null</code> if no plugin details should be shown.
*/
public ExceptionDetailsDialog(IShellProvider parentShell, String title,
Image image, String message, Object details, Plugin plugin) {
super(parentShell, getTitle(title, details), getImage(image, details),
getMessage(message, details));
this.details = details;
this.plugin = plugin;
}
/**
* Build content for the area of the dialog made visible when the Details
* button is clicked.
*
* #param parent
* the details area parent
* #return the details area
*/
protected Control createDetailsArea(Composite parent) {
// Create the details area.
Composite panel = new Composite(parent, SWT.NONE);
panel.setLayoutData(new GridData(GridData.FILL_BOTH));
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
panel.setLayout(layout);
// Create the details content.
createProductInfoArea(panel);
createDetailsViewer(panel);
return panel;
}
/**
* Create fields displaying the plugin information such as name, identifer,
* version and vendor. Do nothing if the plugin is not specified.
*
* #param parent
* the details area in which the fields are created
* #return the product info composite or <code>null</code> if no plugin
* specified.
*/
protected Composite createProductInfoArea(Composite parent) {
// If no plugin specified, then nothing to display here
if (plugin == null)
return null;
Composite composite = new Composite(parent, SWT.NULL);
composite.setLayoutData(new GridData());
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
composite.setLayout(layout);
Dictionary<?, ?> bundleHeaders = plugin.getBundle().getHeaders();
String pluginId = plugin.getBundle().getSymbolicName();
String pluginVendor = (String) bundleHeaders.get("Bundle-Vendor");
String pluginName = (String) bundleHeaders.get("Bundle-Name");
String pluginVersion = (String) bundleHeaders.get("Bundle-Version");
new Label(composite, SWT.NONE).setText("Provider:");
new Label(composite, SWT.NONE).setText(pluginVendor);
new Label(composite, SWT.NONE).setText("Plug-in Name:");
new Label(composite, SWT.NONE).setText(pluginName);
new Label(composite, SWT.NONE).setText("Plug-in ID:");
new Label(composite, SWT.NONE).setText(pluginId);
new Label(composite, SWT.NONE).setText("Version:");
new Label(composite, SWT.NONE).setText(pluginVersion);
return composite;
}
/**
* Create the details field based upon the details object. Do nothing if the
* details object is not specified.
*
* #param parent
* the details area in which the fields are created
* #return the details field
*/
protected Control createDetailsViewer(Composite parent) {
if (details == null)
return null;
Text text = new Text(parent, SWT.MULTI | SWT.READ_ONLY | SWT.BORDER
| SWT.H_SCROLL | SWT.V_SCROLL);
text.setLayoutData(new GridData(GridData.FILL_BOTH));
// Create the content.
StringWriter writer = new StringWriter(1000);
if (details instanceof Throwable)
appendException(new PrintWriter(writer), (Throwable) details);
else if (details instanceof IStatus)
appendStatus(new PrintWriter(writer), (IStatus) details, 0);
text.setText(writer.toString());
return text;
}
// /////////////////////////////////////////////////////////////////
//
// Utility methods for building content
//
// /////////////////////////////////////////////////////////////////
/**
* Answer the title based on the provided title and details object.
*/
public static String getTitle(String title, Object details) {
if (title != null)
return title;
if (details instanceof Throwable) {
Throwable e = (Throwable) details;
while (e instanceof InvocationTargetException)
e = ((InvocationTargetException) e).getTargetException();
String name = e.getClass().getName();
return name.substring(name.lastIndexOf('.') + 1);
}
return "Exception";
}
/**
* Answer the image based on the provided image and details object.
*/
public static Image getImage(Image image, Object details) {
if (image != null)
return image;
Display display = Display.getCurrent();
if (details instanceof IStatus) {
switch (((IStatus) details).getSeverity()) {
case IStatus.ERROR:
return display.getSystemImage(SWT.ICON_ERROR);
case IStatus.WARNING:
return display.getSystemImage(SWT.ICON_WARNING);
case IStatus.INFO:
return display.getSystemImage(SWT.ICON_INFORMATION);
case IStatus.OK:
return null;
}
}
return display.getSystemImage(SWT.ICON_ERROR);
}
/**
* Answer the message based on the provided message and details object.
*/
public static String getMessage(String message, Object details) {
if (details instanceof Throwable) {
Throwable e = (Throwable) details;
while (e instanceof InvocationTargetException)
e = ((InvocationTargetException) e).getTargetException();
if (message == null)
return e.toString();
return MessageFormat.format(message, new Object[] { e.toString() });
}
if (details instanceof IStatus) {
String statusMessage = ((IStatus) details).getMessage();
if (message == null)
return statusMessage;
return MessageFormat
.format(message, new Object[] { statusMessage });
}
if (message != null)
return message;
return "An Exception occurred.";
}
public static void appendException(PrintWriter writer, Throwable ex) {
if (ex instanceof CoreException) {
appendStatus(writer, ((CoreException) ex).getStatus(), 0);
writer.println();
}
appendStackTrace(writer, ex);
if (ex instanceof InvocationTargetException)
appendException(writer, ((InvocationTargetException) ex)
.getTargetException());
}
public static void appendStatus(PrintWriter writer, IStatus status,
int nesting) {
for (int i = 0; i < nesting; i++)
writer.print(" ");
writer.println(status.getMessage());
IStatus[] children = status.getChildren();
for (int i = 0; i < children.length; i++)
appendStatus(writer, children[i], nesting + 1);
}
public static void appendStackTrace(PrintWriter writer, Throwable ex) {
ex.printStackTrace(writer);
}
}

Related

Applitools openBase() failed with com.applitools.eyes.EyesException

I'm unable to figure out why is this code failing, I browsed through Applitools tutorials and I can't figure out what is happening here.
This is the exception being thrown:
com.applitools.eyes.EyesException: eyes.openBase() failed
at com.applitools.eyes.EyesBase.openBase(EyesBase.java:1037)
at com.applitools.eyes.selenium.SeleniumEyes.open(SeleniumEyes.java:246)
at com.applitools.eyes.selenium.Eyes.open(Eyes.java:77)
at com.applitools.eyes.selenium.Eyes.open(Eyes.java:1374)
at BaseTests.validateWindow(BaseTests.java:49)
at SearchTests.testSearchByFullTitle(SearchTests.java:11)
This is SearchTests:
import org.junit.Test;
public class SearchTests extends BaseTests {
#Test
public void testSearchByFullTitle(){
String title = "Agile Testing";
page.search(title);
validateWindow();
}
}
Validate window method:
public void validateWindow(){
eyes.open(driver, "Automation Bookstore", "neka metoda npr: "+
Thread.currentThread().getStackTrace()[2].getMethodName());
eyes.checkWindow();
eyes.close();
}
and the class throwing the exception:
protected void openBase() throws EyesException {
openLogger();
int retry = 0;
do {
try {
if (isDisabled) {
logger.verbose("Ignored");
return;
}
sessionEventHandlers.testStarted(getAUTSessionId());
validateApiKey();
logOpenBase();
validateSessionOpen();
initProviders();
this.isViewportSizeSet = false;
sessionEventHandlers.initStarted();
beforeOpen();
RectangleSize viewportSize = getViewportSizeForOpen();
viewportSizeHandler.set(viewportSize);
try {
if (viewportSize != null) {
ensureRunningSession();
}
} catch (Exception e) {
GeneralUtils.logExceptionStackTrace(logger, e);
retry++;
continue;
}
this.validationId = -1;
isOpen = true;
afterOpen();
return;
} catch (EyesException e) {
logger.log(e.getMessage());
logger.getLogHandler().close();
throw e;
}
} while (MAX_ITERATION > retry);
throw new EyesException("eyes.openBase() failed");
}
After some debugging, I found that I had a typo in my API key. After fixing that, works as expected.
In my case, the same issue was caused by using null as a value for the testName parameter.
I didn't understand it from the beginning, cause I relied on the javadoc for the open function:
/**
* Starts a test.
*
* #param driver The web driver that controls the browser hosting the application under test.
* #param appName The name of the application under test.
* #param testName The test name. (i.e., the visible part of the document's body) or {#code null} to use the current window's viewport.
* #return A wrapped WebDriver which enables SeleniumEyes trigger recording and frame handling.
*/
public WebDriver open(WebDriver driver, String appName, String testName) {
RectangleSize viewportSize = SeleniumEyes.getViewportSize(driver);
this.configuration.setAppName(appName);
this.configuration.setTestName(testName);
this.configuration.setViewportSize(viewportSize);
return open(driver);
}

java.lang.ClassCastException when maximize mpart

I have a Eclipse E4 application with some different MParts. Now when I maximize the MPart by double clicking on its tab I got the ClassCastException. Also when I minimize it again, the same exception will be thrown. I looked at the error but could't find a place in my source where this exception can be thrown. Can someone help me with this? Below you can find the full stack trace of the error.
java.lang.ClassCastException: org.eclipse.swt.layout.GridData cannot be cast to org.eclipse.swt.layout.FillData
at org.eclipse.swt.layout.FillLayout.computeChildSize(FillLayout.java:145)
at org.eclipse.swt.layout.FillLayout.computeSize(FillLayout.java:123)
at org.eclipse.swt.widgets.Composite.computeSizeInPixels(Composite.java:238)
at org.eclipse.swt.widgets.Control.computeSize(Control.java:663)
at org.eclipse.ui.forms.widgets.LayoutComposite.computeSize(LayoutComposite.java:37)
at org.eclipse.ui.forms.widgets.SizeCache.controlComputeSize(SizeCache.java:391)
at org.eclipse.ui.forms.widgets.SizeCache.computeMinimumWidth(SizeCache.java:430)
at org.eclipse.ui.forms.widgets.SizeCache.computeSize(SizeCache.java:189)
at org.eclipse.ui.forms.widgets.ExpandableComposite$ExpandableLayout.computeSize(ExpandableComposite.java:476)
at org.eclipse.ui.forms.widgets.ExpandableComposite.computeSize(ExpandableComposite.java:964)
at org.eclipse.swt.layout.FillData.computeSize(FillData.java:26)
at org.eclipse.swt.layout.FillLayout.computeChildSize(FillLayout.java:152)
at org.eclipse.swt.layout.FillLayout.computeSize(FillLayout.java:123)
at org.eclipse.swt.widgets.Composite.computeSizeInPixels(Composite.java:238)
at org.eclipse.swt.widgets.Control.computeSize(Control.java:663)
at org.eclipse.swt.layout.FillData.computeSize(FillData.java:26)
at org.eclipse.swt.layout.FillLayout.computeChildSize(FillLayout.java:152)
at org.eclipse.swt.layout.FillLayout.computeSize(FillLayout.java:123)
at org.eclipse.swt.widgets.Composite.computeSizeInPixels(Composite.java:238)
at org.eclipse.swt.widgets.Control.computeSize(Control.java:663)
at org.eclipse.ui.forms.widgets.LayoutComposite.computeSize(LayoutComposite.java:37)
at org.eclipse.ui.forms.widgets.SizeCache.controlComputeSize(SizeCache.java:391)
at org.eclipse.ui.forms.widgets.SizeCache.computeMinimumWidth(SizeCache.java:430)
at org.eclipse.ui.forms.widgets.SizeCache.computeSize(SizeCache.java:189)
at org.eclipse.ui.forms.widgets.Form$FormLayout.computeSize(Form.java:143)
at org.eclipse.ui.forms.widgets.Form.computeSize(Form.java:200)
at org.eclipse.ui.forms.widgets.SizeCache.controlComputeSize(SizeCache.java:391)
at org.eclipse.ui.forms.widgets.SizeCache.computeSize(SizeCache.java:270)
at org.eclipse.ui.forms.widgets.SharedScrolledComposite.reflow(SharedScrolledComposite.java:188)
at org.eclipse.ui.forms.widgets.SharedScrolledComposite.scheduleReflow(SharedScrolledComposite.java:237)
at org.eclipse.ui.forms.widgets.SharedScrolledComposite.access$1(SharedScrolledComposite.java:221)
at org.eclipse.ui.forms.widgets.SharedScrolledComposite$1.handleEvent(SharedScrolledComposite.java:68)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
at org.eclipse.swt.widgets.Display.sendEvent(Display.java:4410)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1079)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1103)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1084)
at org.eclipse.swt.widgets.Control.WM_SIZE(Control.java:5459)
at org.eclipse.swt.widgets.Scrollable.WM_SIZE(Scrollable.java:367)
at org.eclipse.swt.widgets.Composite.WM_SIZE(Composite.java:1783)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4866)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5102)
at org.eclipse.swt.internal.win32.OS.DefWindowProcW(Native Method)
at org.eclipse.swt.internal.win32.OS.DefWindowProc(OS.java:2547)
at org.eclipse.swt.widgets.Scrollable.callWindowProc(Scrollable.java:88)
at org.eclipse.swt.widgets.Control.WM_WINDOWPOSCHANGED(Control.java:5691)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4879)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5102)
at org.eclipse.swt.internal.win32.OS.SetWindowPos(Native Method)
at org.eclipse.swt.widgets.Widget.SetWindowPos(Widget.java:1482)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3251)
at org.eclipse.swt.widgets.Composite.setBoundsInPixels(Composite.java:1099)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3212)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3208)
at org.eclipse.swt.widgets.Control.setBounds(Control.java:3203)
at org.eclipse.swt.layout.FillLayout.layout(FillLayout.java:219)
at org.eclipse.swt.widgets.Composite.updateLayout(Composite.java:1383)
at org.eclipse.swt.widgets.Composite.WM_SIZE(Composite.java:1794)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4866)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5102)
at org.eclipse.swt.internal.win32.OS.DefWindowProcW(Native Method)
at org.eclipse.swt.internal.win32.OS.DefWindowProc(OS.java:2547)
at org.eclipse.swt.widgets.Scrollable.callWindowProc(Scrollable.java:88)
at org.eclipse.swt.widgets.Control.WM_WINDOWPOSCHANGED(Control.java:5691)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4879)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5115)
at org.eclipse.swt.internal.win32.OS.SetWindowPos(Native Method)
at org.eclipse.swt.widgets.Widget.SetWindowPos(Widget.java:1482)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3251)
at org.eclipse.swt.widgets.Composite.setBoundsInPixels(Composite.java:1099)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3212)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3208)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3284)
at org.eclipse.swt.widgets.Control.setBounds(Control.java:3280)
at org.eclipse.swt.custom.CTabFolderLayout.layout(CTabFolderLayout.java:116)
at org.eclipse.swt.widgets.Composite.updateLayout(Composite.java:1383)
at org.eclipse.swt.widgets.Composite.WM_SIZE(Composite.java:1794)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4866)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5102)
at org.eclipse.swt.internal.win32.OS.DefWindowProcW(Native Method)
at org.eclipse.swt.internal.win32.OS.DefWindowProc(OS.java:2547)
at org.eclipse.swt.widgets.Scrollable.callWindowProc(Scrollable.java:88)
at org.eclipse.swt.widgets.Control.WM_WINDOWPOSCHANGED(Control.java:5691)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4879)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5115)
at org.eclipse.swt.internal.win32.OS.EndDeferWindowPos(Native Method)
at org.eclipse.swt.widgets.Composite.resizeChildren(Composite.java:1027)
at org.eclipse.swt.widgets.Composite.resizeChildren(Composite.java:993)
at org.eclipse.swt.widgets.Composite.setResizeChildren(Composite.java:1207)
at org.eclipse.swt.widgets.Composite.WM_SIZE(Composite.java:1798)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4866)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5102)
at org.eclipse.swt.internal.win32.OS.DefWindowProcW(Native Method)
at org.eclipse.swt.internal.win32.OS.DefWindowProc(OS.java:2547)
at org.eclipse.swt.widgets.Scrollable.callWindowProc(Scrollable.java:88)
at org.eclipse.swt.widgets.Control.WM_WINDOWPOSCHANGED(Control.java:5691)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4879)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5102)
at org.eclipse.swt.internal.win32.OS.SetWindowPos(Native Method)
at org.eclipse.swt.widgets.Widget.SetWindowPos(Widget.java:1482)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3251)
at org.eclipse.swt.widgets.Composite.setBoundsInPixels(Composite.java:1099)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3212)
at org.eclipse.swt.widgets.Control.setBoundsInPixels(Control.java:3208)
at org.eclipse.swt.widgets.Control.setBounds(Control.java:3203)
at org.eclipse.swt.layout.FillLayout.layout(FillLayout.java:204)
at org.eclipse.swt.widgets.Composite.updateLayout(Composite.java:1383)
at org.eclipse.swt.widgets.Composite.WM_SIZE(Composite.java:1794)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4866)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5102)
at org.eclipse.swt.internal.win32.OS.DefWindowProcW(Native Method)
at org.eclipse.swt.internal.win32.OS.DefWindowProc(OS.java:2547)
at org.eclipse.swt.widgets.Scrollable.callWindowProc(Scrollable.java:88)
at org.eclipse.swt.widgets.Control.WM_WINDOWPOSCHANGED(Control.java:5691)
at org.eclipse.swt.widgets.Control.windowProc(Control.java:4879)
at org.eclipse.swt.widgets.Display.windowProc(Display.java:5115)
at org.eclipse.swt.internal.win32.OS.EndDeferWindowPos(Native Method)
at org.eclipse.swt.widgets.Composite.resizeChildren(Composite.java:1027)
at org.eclipse.swt.widgets.Composite.resizeChildren(Composite.java:993)
at org.eclipse.swt.widgets.Composite.setResizeChildren(Composite.java:1207)
at org.eclipse.swt.widgets.Composite.updateLayout(Composite.java:1384)
at org.eclipse.swt.widgets.Composite.updateLayout(Composite.java:1368)
at org.eclipse.swt.widgets.Composite.setLayoutDeferred(Composite.java:1167)
at org.eclipse.swt.widgets.Display.runDeferredLayouts(Display.java:4252)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3809)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine$4.run(PartRenderingEngine.java:1121)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:336)
at org.eclipse.e4.ui.internal.workbench.swt.PartRenderingEngine.run(PartRenderingEngine.java:1022)
at org.eclipse.e4.ui.internal.workbench.E4Workbench.createAndRunUI(E4Workbench.java:150)
at org.eclipse.e4.ui.internal.workbench.swt.E4Application.start(E4Application.java:161)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:134)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:104)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:388)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:243)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:673)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:610)
at org.eclipse.equinox.launcher.Main.run(Main.java:1519)
at org.eclipse.equinox.launcher.Main.main(Main.java:1492)
My class that will render the screen:
public class UsersScreen extends MainPart {
/**
* Table with all the users
*/
private CustomNatTable<PlatformUser> userTable;
/**
* Table with all the different roles
*/
private CustomNatTable<RoleCompanyViewModel> roleTable;
/**
* #see org.eclipse.e4.ui.workbench.modeling.EPartService
*/
private EPartService partService;
/**
* Part of this part
*/
private MPart part;
/**
* List of all the roleCompany view models
*/
private ArrayList<RoleCompanyViewModel> roleData = new ArrayList<>();
/**
* List of all the userroles
*/
private ArrayList<UserRole> allUserRoles;
/**
* UserPermissionRepository repository to search for the user permissions
*/
private UserPermissionRepository userPermissionrepo;
/**
* Parent composite of the userTable
*/
private Composite userComposite;
/**
* Parent composite of the roleTable
*/
private Composite roleComposite;
private IEclipseContext context;
/**
* Creates the components and place it on the screen
*
* #param parent
* Parent composite where the components need to be write on
* #param application
* #see org.eclipse.e4.ui.model.application.MApplication
* #param service
* #see org.eclipse.e4.ui.workbench.modeling.EModelService
* #param partService
* #see org.eclipse.e4.ui.workbench.modeling.EPartService
* #param context
* #see org.eclipse.e4.core.contexts.IEclipseContext
* #param currentPart
* MPart of the UserScreen
*/
#PostConstruct
public void createComposite(Composite parent, MApplication application, EModelService service,
EPartService partService, final IEclipseContext context, MPart currentPart) {
this.userPermissionrepo = new RepositoryManager<UserPermissionRepository>()
.getRepository(UserPermissionRepository.class.getSimpleName());
this.application = application;
this.service = service;
this.partService = partService;
this.part = currentPart;
this.context = context;
FormToolkit toolkit = new FormToolkit(parent.getDisplay());
ScrolledForm form = toolkit.createScrolledForm(parent);
form.setText("User Management");
Composite body = form.getBody();
toolkit.decorateFormHeading(form.getForm());
toolkit.paintBordersFor(body);
form.getBody().setLayout(new FillLayout(SWT.HORIZONTAL));
Composite container = new Composite(body, SWT.NONE);
container.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WHITE));
container.setLayout(new FillLayout());
Section userSection = toolkit.createSection(container, Section.TITLE_BAR);
//userSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
toolkit.paintBordersFor(userSection);
userSection.setText("Users");
Section roleSection = toolkit.createSection(container, Section.TITLE_BAR);
//roleSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 2));
toolkit.paintBordersFor(roleSection);
roleSection.setText("Roles");
userComposite = toolkit.createComposite(userSection);
roleComposite = toolkit.createComposite(roleSection);
userComposite.setLayout(new FillLayout());
roleComposite.setLayout(new FillLayout());
userSection.setClient(userComposite);
roleSection.setClient(roleComposite);
showToolItems();
createUserTable(userComposite, context);
createRoleTable(roleComposite, context);
}
/**
* Creates the role table on the parent composite
*
* #param roleTableComposite
* Composite where the table must be on
* #param context
* #see org.eclipse.e4.core.contexts.IEclipseContext
*/
private void createRoleTable(Composite roleTableComposite, final IEclipseContext context) {
ArrayList<NATColumnDefinition> roleDefinitions = new ArrayList<>();
NATColumnDefinition assigned = new NATColumnDefinition("on");
assigned.setLabel("Assigned");
assigned.setAligment(Alignment.COLUMN_CENTER_LABEL);
assigned.setColumnWidth(60);
assigned.setData(NATColumnDefinition.DataType.BOOLEAN_COLUMN_LABEL);
if (!connectionClosed && userPermissionrepo.containsPermission("AssignRole")) {
assigned.setEditor(NATColumnDefinition.EditorType.BOOLEAN_EDITOR_COLUMN_LABEL);
}
NATColumnDefinition roleName = new NATColumnDefinition("name");
roleName.setLabel("Name");
roleName.setAligment(Alignment.COLUMN_CENTER_LABEL);
roleName.setColumnWidth(120);
NATColumnDefinition description = new NATColumnDefinition("description");
description.setLabel("Description");
description.setAligment(Alignment.COLUMN_CENTER_LABEL);
description.setColumnWidth(240);
roleDefinitions.add(assigned);
roleDefinitions.add(roleName);
roleDefinitions.add(description);
roleTable = new CustomNatTable<>(roleDefinitions, roleTableComposite);
roleTableComposite.setLayout(new FillLayout(SWT.HORIZONTAL));
roleTable.showFilterRow(true);
roleTable.setSortEnabled(true);
roleTable.showHeaderGroups(false);
roleTable.showHeaderGroupGroups(false);
roleTable.expandTree(true);
roleTable.treeFormat(new TreeFormat<RoleCompanyViewModel>(new Comparator<RoleCompanyViewModel>() {
#Override
public int compare(RoleCompanyViewModel obj1, RoleCompanyViewModel obj2) {
if (obj1.getCompanyId() != null && obj2.getCompanyId() != null) {
return obj1.getCompanyId().compareTo(obj2.getCompanyId());
}
return 0;
}
}));
roleTable.generate();
roleTable.addMoficationListener(new IModificationListener<RoleCompanyViewModel>() {
#Override
public void objectModified(RoleCompanyViewModel changedObject, RoleCompanyViewModel originalObject) {
PlatformUser user = userTable.getSelectedItem();
if (changedObject.getChildren().size() > 0) {
ArrayList<RoleCompanyViewModel> userRoles = (ArrayList<RoleCompanyViewModel>) changedObject
.getChildren();
for (RoleCompanyViewModel viewModel : userRoles) {
UserRole role = new UserRole();
role.setPlatformUser(user);
role.setCompany(viewModel.getCompanyObject());
role.setRole(viewModel.getRoleObject());
if (viewModel.isOn()) {
SendInsertHandler<UserRole> handler = new SendInsertHandler<>();
handler.execute(role, context, UserRole.class.getSimpleName(), "UserManagement", part);
} else {
SendRemoveHandle<UserRole> handler = new SendRemoveHandle<>();
handler.execute(role, context, UserRole.class.getSimpleName(), "UserManagement", part);
}
}
} else {
UserRole role = new UserRole();
role.setPlatformUser(user);
role.setCompany(changedObject.getCompanyObject());
role.setRole(changedObject.getRoleObject());
if (changedObject.isOn()) {
SendInsertHandler<UserRole> handler = new SendInsertHandler<>();
handler.execute(role, context, UserRole.class.getSimpleName(), "UserManagement", part);
} else {
SendRemoveHandle<UserRole> handler = new SendRemoveHandle<>();
handler.execute(role, context, UserRole.class.getSimpleName(), "UserManagement", part);
}
}
}
});
updateRoleTable(null, null);
}
/**
* Creates the user table on the parent composite
*
* #param userTableComposite
* Composite where the table must be on
* #param context
* #see org.eclipse.e4.core.contexts.IEclipseContext
*/
private void createUserTable(Composite userTableComposite, final IEclipseContext context) {
ArrayList<NATColumnDefinition> userColumnDefinitions = new ArrayList<>();
NATColumnDefinition userName = new NATColumnDefinition("userName");
userName.setLabel("User name");
userName.setAligment(Alignment.COLUMN_CENTER_LABEL);
userName.setColumnWidth(120);
NATColumnDefinition firstName = new NATColumnDefinition("firstName");
firstName.setLabel("First name");
firstName.setAligment(Alignment.COLUMN_CENTER_LABEL);
firstName.setColumnWidth(120);
NATColumnDefinition lastName = new NATColumnDefinition("lastName");
lastName.setLabel("Last name");
lastName.setAligment(Alignment.COLUMN_CENTER_LABEL);
lastName.setColumnWidth(120);
NATColumnDefinition domain = new NATColumnDefinition("domain");
domain.setLabel("Domain");
domain.setAligment(Alignment.COLUMN_CENTER_LABEL);
domain.setColumnWidth(60);
NATColumnDefinition active = new NATColumnDefinition("active");
active.setLabel("Active");
active.setAligment(Alignment.COLUMN_CENTER_LABEL);
active.setColumnWidth(60);
active.setDataType(NATColumnDefinition.DataType.BOOLEAN_COLUMN_LABEL);
if (!connectionClosed && userPermissionrepo.containsPermission("DeactivateUser")) {
active.setEditor(NATColumnDefinition.EditorType.BOOLEAN_EDITOR_COLUMN_LABEL);
}
if (!connectionClosed && userPermissionrepo.containsPermission("EditUser")) {
firstName.setEditor(NATColumnDefinition.EditorType.TEXT_EDITOR_COLUMN_LABEL);
lastName.setEditor(NATColumnDefinition.EditorType.TEXT_EDITOR_COLUMN_LABEL);
}
userColumnDefinitions.add(userName);
userColumnDefinitions.add(firstName);
userColumnDefinitions.add(lastName);
userColumnDefinitions.add(domain);
userColumnDefinitions.add(active);
userTable = new CustomNatTable<>(userColumnDefinitions, userTableComposite);
// userTableComposite.setLayout(new FillLayout(SWT.HORIZONTAL));
userTable.showFilterRow(true);
userTable.setSortEnabled(true);
userTable.showHeaderGroups(false);
userTable.showHeaderGroupGroups(false);
userTable.generate();
userTable.addSelectionListener(new SelectionListener<PlatformUser>() {
#Override
public void itemSelected(PlatformUser item) {
checkSelection(item);
roleTable.refresh(false);
}
#Override
public void gridSelectionChanged(GridSelection selection) {
}
});
userTable.addMoficationListener(new IModificationListener<PlatformUser>() {
#Override
public void objectModified(PlatformUser changedObject, PlatformUser originalObject) {
SendUpdateHandler<PlatformUser> handler = new SendUpdateHandler<>();
handler.execute(changedObject, originalObject, context, changedObject.getClass().getSimpleName(),
"UserManagement", partService.getActivePart());
}
});
userDataChanged(null);
}
/**
* Handles the close action from the window
*/
#PreDestroy
public void windowClose() {
CloseScreenHandler handler = new CloseScreenHandler();
handler.execute("UserManagement", part);
MDirectToolItem addUser = getToolItem(application, service, "addUserToolItem");
addUser.setToBeRendered(false);
addUser.setVisible(false);
}
/**
* Will be called when some data has been changed in the RoleRepository
*
* #param repo
* Platform user repository
*/
#Inject
#Optional
public void roleDataChanged(#UIEventTopic("RoleRepository") RoleRepository repo) {
updateRoleTable(repo, null);
}
/**
* Will be called when some data has been changed in the
* PlatformUserRepository
*
* #param data
* Platform user repository
*/
#Inject
#Optional
public void userDataChanged(#UIEventTopic("PlatformUserRepository") PlatformUserRepository data) {
if(data == null)
{
RepositoryManager<PlatformUserRepository> manager = new RepositoryManager<PlatformUserRepository>();
data = manager.getRepository(PlatformUserRepository.class.getSimpleName());
}
userTable.setItems(data.getAllData());
userTable.refresh(true);
}
/**
* Will be called when some data has been changed in the CompanyRepository.
*
* #param repo
* Company repository
*/
#Inject
#Optional
public void companyDataChanged(#UIEventTopic("CompanyRepository") CompanyRepository repo) {
updateRoleTable(null, repo);
}
/**
* Handles the change of UserRoleRepository object
*
* #param repo
* UserRoleRepository object that has been changed
*/
#Inject
#Optional
public void userRolDataChanged(#UIEventTopic("UserRoleRepository") UserRoleRepository repo) {
allUserRoles = repo.getAllData();
PlatformUser selectedUser = userTable.getSelectedItem();
if(selectedUser != null)
{
checkSelection(selectedUser);
roleTable.refresh(false);
}
}
}
I found the issue. Some old code was still in my class to draw the a Nattable.
The code was a GridFactory that places the table on a grid. Removed this line fixed my problem.

MongoDB date format in strict mode

Using MongoDB java driver, applying toJson() method on Document will get a JSON representation of this document with JsonMode set to STRICT.
The following epoch format is used for dates: { "$date" : "dateAsMilliseconds" }
Using mongoexport, we get an ISO-8601 format.
Seen in official doc ( https://docs.mongodb.com/manual/reference/mongodb-extended-json/ ) :
In Strict mode, date is an ISO-8601 date format with a mandatory time zone field following the template YYYY-MM-DDTHH:mm:ss.mmm<+/-Offset>.
The MongoDB JSON parser currently does not support loading ISO-8601 strings representing dates prior to the Unix epoch. When formatting pre-epoch dates and dates past what your system’s time_t type can hold, the following format is used:
{ "$date" : { "$numberLong" : "dateAsMilliseconds" } }
I would appreciate if someone can explain me why there is no common format used between MongoDB java driver, mongoexport tool and official docs?
Thanks.
Obviously there is NO good reason for the Java driver to deviate from the official specification. The only exception are for those dates which cannot be expressed in the ISO8601 format (like B.C. dates...)
As a work around I have extended the JsonWriter class and provided two toJson static methods as an example of how to use it:
package whatever.package.you.like;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import org.bson.BSONException;
import org.bson.BsonContextType;
import org.bson.BsonDocument;
import org.bson.codecs.BsonDocumentCodec;
import org.bson.codecs.EncoderContext;
import org.bson.conversions.Bson;
import org.bson.json.JsonMode;
import org.bson.json.JsonWriter;
import org.bson.json.JsonWriterSettings;
import com.mongodb.MongoClient;
/**
* A {#link JsonWriter} extension that conforms to the "strict" JSON format
* specified by MongoDB for data/time values.
*
* The {#link JsonWriter} class provided in the MongoDB Java driver (version
* 3.2.2) does not conform to official MongoDB specification for strict mode
* JSON (see https://docs.mongodb.com/manual/reference/mongodb-extended-json/).
* This is specifically a problem with the date/time values which get filled
* with a milliseconds value (i.e. {$date: 309249234098}) instead of the ISO8601
* date/time (i.e. {$date: "2016-07-14T08:44:23.234Z"}) value which the
* specification calls for. This extension of {#link JsonWriter} conforms to the
* MongoDb specification in this regard.
*/
public class ConformingJsonWriter extends JsonWriter {
private final JsonWriterSettings settings;
private final Writer writer;
private boolean writingIndentedDateTime = false;
/**
* Creates a new instance which uses {#code writer} to write JSON to.
*
* #param writer
* the writer to write JSON to.
*/
public ConformingJsonWriter(final Writer writer) {
this(writer, new JsonWriterSettings());
}
/**
* Creates a new instance which uses {#code writer} to write JSON to and uses
* the given settings.
*
* #param writer
* the writer to write JSON to.
* #param settings
* the settings to apply to this writer.
*/
public ConformingJsonWriter(final Writer writer,
final JsonWriterSettings settings) {
super(writer, settings);
this.writer = writer;
this.settings = settings;
setContext(new Context(null, BsonContextType.TOP_LEVEL, ""));
}
private void writeIndentation(int skip) throws IOException {
for (Context context = getContext()
.getParentContext(); context != null; context = context
.getParentContext()) {
if (skip-- <= 0) {
writer.write(settings.getIndentCharacters());
}
}
}
private static String millisToIso8601(long millis) throws IOException {
SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
return dateFormat.format(new Date(millis));
}
#Override
protected void doWriteDateTime(final long value) {
if ((settings.getOutputMode() == JsonMode.STRICT)
&& (value >= -59014396800000L && value <= 253399536000000L)) {
try {
writeStartDocument();
if (settings.isIndent()) {
writingIndentedDateTime = true;
writer.write(settings.getNewLineCharacters());
writeIndentation(0);
} else {
writer.write(" ");
}
writer.write("\"$date\" : ");
writer.write("\"");
writer.write(millisToIso8601(value));
writer.write("\"");
writeEndDocument();
writingIndentedDateTime = false;
} catch (IOException e) {
throw new BSONException("Wrapping IOException", e);
}
} else {
super.doWriteDateTime(value);
}
}
#Override
protected void doWriteEndDocument() {
if (writingIndentedDateTime) {
try {
writer.write(settings.getNewLineCharacters());
writeIndentation(1);
writer.write("}");
if (getContext()
.getContextType() == BsonContextType.SCOPE_DOCUMENT) {
setContext(getContext().getParentContext());
writeEndDocument();
} else {
setContext(getContext().getParentContext());
}
} catch (IOException e) {
throw new BSONException("Wrapping IOException", e);
}
} else {
super.doWriteEndDocument();
}
}
/**
* Take a {#link Bson} instance and convert it to "strict" JSON
* representation with no indentation (read, "NOT pretty printed").
*
* #param bson
* The {#link Bson} instance to convert
* #return The JSON representation.
*/
public static String toJson(Bson bson) {
return toJson(bson, new JsonWriterSettings());
}
/**
* Take a {#link Bson} instance and convert it to JSON representation.
*
* #param bson
* The {#link Bson} instance to convert
* #param writerSettings
* {#link JsonWriterSettings} that specify details about how the
* JSON output should look.
* #return The JSON representation.
*/
public static String toJson(Bson bson,
final JsonWriterSettings writerSettings) {
BsonDocumentCodec encoder = new BsonDocumentCodec();
ConformingJsonWriter writer = new ConformingJsonWriter(new StringWriter(),
writerSettings);
encoder.encode(writer,
bson.toBsonDocument(BsonDocument.class,
MongoClient.getDefaultCodecRegistry()),
EncoderContext.builder().isEncodingCollectibleDocument(true)
.build());
return writer.getWriter().toString();
}
}

Gson optional and required fields

How should one deal with Gsonand required versus optional fields?
Since all fields are optional, I can't really fail my network request based on if the response json contains some key, Gsonwill simply parse it to null.
Method I am using gson.fromJson(json, mClassOfT);
For example if I have following json:
{"user_id":128591, "user_name":"TestUser"}
And my class:
public class User {
#SerializedName("user_id")
private String mId;
#SerializedName("user_name")
private String mName;
public String getId() {
return mId;
}
public void setId(String id) {
mId = id;
}
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
}
Is the any option to get Gson to fail if json would not contain user_id or user_name key?
There can be many cases where you might need at least some values to be parsed and other one could be optional?
Is there any pattern or library to be used to handle this case globally?
Thanks.
As you note, Gson has no facility to define a "required field" and you'll just get null in your deserialized object if something is missing in the JSON.
Here's a re-usable deserializer and annotation that will do this. The limitation is that if the POJO required a custom deserializer as-is, you'd have to go a little further and either pass in a Gson object in the constructor to deserialize to object itself or move the annotation checking out into a separate method and use it in your deserializer. You could also improve on the exception handling by creating your own exception and pass it to the JsonParseException so it can be detected via getCause() in the caller.
That all said, in the vast majority of cases, this will work:
public class App
{
public static void main(String[] args)
{
Gson gson =
new GsonBuilder()
.registerTypeAdapter(TestAnnotationBean.class, new AnnotatedDeserializer<TestAnnotationBean>())
.create();
String json = "{\"foo\":\"This is foo\",\"bar\":\"this is bar\"}";
TestAnnotationBean tab = gson.fromJson(json, TestAnnotationBean.class);
System.out.println(tab.foo);
System.out.println(tab.bar);
json = "{\"foo\":\"This is foo\"}";
tab = gson.fromJson(json, TestAnnotationBean.class);
System.out.println(tab.foo);
System.out.println(tab.bar);
json = "{\"bar\":\"This is bar\"}";
tab = gson.fromJson(json, TestAnnotationBean.class);
System.out.println(tab.foo);
System.out.println(tab.bar);
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface JsonRequired
{
}
class TestAnnotationBean
{
#JsonRequired public String foo;
public String bar;
}
class AnnotatedDeserializer<T> implements JsonDeserializer<T>
{
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException
{
T pojo = new Gson().fromJson(je, type);
Field[] fields = pojo.getClass().getDeclaredFields();
for (Field f : fields)
{
if (f.getAnnotation(JsonRequired.class) != null)
{
try
{
f.setAccessible(true);
if (f.get(pojo) == null)
{
throw new JsonParseException("Missing field in JSON: " + f.getName());
}
}
catch (IllegalArgumentException ex)
{
Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);
}
catch (IllegalAccessException ex)
{
Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
return pojo;
}
}
Output:
This is foo
this is bar
This is foo
null
Exception in thread "main" com.google.gson.JsonParseException: Missing field in JSON: foo
Answer of Brian Roach is very good, but sometimes it's also necessary to handle:
properties of model's super class
properties inside of arrays
For these purposes the following class can be used:
/**
* Adds the feature to use required fields in models.
*
* #param <T> Model to parse to.
*/
public class JsonDeserializerWithOptions<T> implements JsonDeserializer<T> {
/**
* To mark required fields of the model:
* json parsing will be failed if these fields won't be provided.
* */
#Retention(RetentionPolicy.RUNTIME) // to make reading of this field possible at the runtime
#Target(ElementType.FIELD) // to make annotation accessible through reflection
public #interface FieldRequired {}
/**
* Called when the model is being parsed.
*
* #param je Source json string.
* #param type Object's model.
* #param jdc Unused in this case.
*
* #return Parsed object.
*
* #throws JsonParseException When parsing is impossible.
* */
#Override
public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc)
throws JsonParseException {
// Parsing object as usual.
T pojo = new Gson().fromJson(je, type);
// Getting all fields of the class and checking if all required ones were provided.
checkRequiredFields(pojo.getClass().getDeclaredFields(), pojo);
// Checking if all required fields of parent classes were provided.
checkSuperClasses(pojo);
// All checks are ok.
return pojo;
}
/**
* Checks whether all required fields were provided in the class.
*
* #param fields Fields to be checked.
* #param pojo Instance to check fields in.
*
* #throws JsonParseException When some required field was not met.
* */
private void checkRequiredFields(#NonNull Field[] fields, #NonNull Object pojo)
throws JsonParseException {
// Checking nested list items too.
if (pojo instanceof List) {
final List pojoList = (List) pojo;
for (final Object pojoListPojo : pojoList) {
checkRequiredFields(pojoListPojo.getClass().getDeclaredFields(), pojoListPojo);
checkSuperClasses(pojoListPojo);
}
}
for (Field f : fields) {
// If some field has required annotation.
if (f.getAnnotation(FieldRequired.class) != null) {
try {
// Trying to read this field's value and check that it truly has value.
f.setAccessible(true);
Object fieldObject = f.get(pojo);
if (fieldObject == null) {
// Required value is null - throwing error.
throw new JsonParseException(String.format("%1$s -> %2$s",
pojo.getClass().getSimpleName(),
f.getName()));
} else {
checkRequiredFields(fieldObject.getClass().getDeclaredFields(), fieldObject);
checkSuperClasses(fieldObject);
}
}
// Exceptions while reflection.
catch (IllegalArgumentException | IllegalAccessException e) {
throw new JsonParseException(e);
}
}
}
}
/**
* Checks whether all super classes have all required fields.
*
* #param pojo Object to check required fields in its superclasses.
*
* #throws JsonParseException When some required field was not met.
* */
private void checkSuperClasses(#NonNull Object pojo) throws JsonParseException {
Class<?> superclass = pojo.getClass();
while ((superclass = superclass.getSuperclass()) != null) {
checkRequiredFields(superclass.getDeclaredFields(), pojo);
}
}
}
First of all the interface (annotation) to mark required fields with is described, we'll see an example of its usage later:
/**
* To mark required fields of the model:
* json parsing will be failed if these fields won't be provided.
* */
#Retention(RetentionPolicy.RUNTIME) // to make reading of this field possible at the runtime
#Target(ElementType.FIELD) // to make annotation accessible throw the reflection
public #interface FieldRequired {}
Then deserialize method is implemented. It parses json strings as usual: missing properties in result pojo will have null values:
T pojo = new Gson().fromJson(je, type);
Then the recursive check of all fields of the parsed pojo is being launched:
checkRequiredFields(pojo.getClass().getDeclaredFields(), pojo);
Then we also check all fields of pojo's super classes:
checkSuperClasses(pojo);
It's required when some SimpleModel extends its SimpleParentModel and we want to make sure that all properties of SimpleModel marked as required are provided as SimpleParentModel's ones.
Let's take a look on checkRequiredFields method. First of all it checks if some property is instance of List (json array) - in this case all objects of the list should also be checked to make sure that they have all required fields provided too:
if (pojo instanceof List) {
final List pojoList = (List) pojo;
for (final Object pojoListPojo : pojoList) {
checkRequiredFields(pojoListPojo.getClass().getDeclaredFields(), pojoListPojo);
checkSuperClasses(pojoListPojo);
}
}
Then we are iterating through all fields of pojo, checking if all fields with FieldRequired annotation are provided (what means these fields are not null). If we have encountered some null property which is required - an exception will be fired. Otherwise another recursive step of the validation will be launched for current field, and properties of parent classes of the field will be checked too:
for (Field f : fields) {
// If some field has required annotation.
if (f.getAnnotation(FieldRequired.class) != null) {
try {
// Trying to read this field's value and check that it truly has value.
f.setAccessible(true);
Object fieldObject = f.get(pojo);
if (fieldObject == null) {
// Required value is null - throwing error.
throw new JsonParseException(String.format("%1$s -> %2$s",
pojo.getClass().getSimpleName(),
f.getName()));
} else {
checkRequiredFields(fieldObject.getClass().getDeclaredFields(), fieldObject);
checkSuperClasses(fieldObject);
}
}
// Exceptions while reflection.
catch (IllegalArgumentException | IllegalAccessException e) {
throw new JsonParseException(e);
}
}
}
And the last method should be reviewed is checkSuperClasses: it just runs the similar required fields validation checking properties of pojo's super classes:
Class<?> superclass = pojo.getClass();
while ((superclass = superclass.getSuperclass()) != null) {
checkRequiredFields(superclass.getDeclaredFields(), pojo);
}
And finally lets review some example of this JsonDeserializerWithOptions's usage. Assume we have the following models:
private class SimpleModel extends SimpleParentModel {
#JsonDeserializerWithOptions.FieldRequired Long id;
#JsonDeserializerWithOptions.FieldRequired NestedModel nested;
#JsonDeserializerWithOptions.FieldRequired ArrayList<ListModel> list;
}
private class SimpleParentModel {
#JsonDeserializerWithOptions.FieldRequired Integer rev;
}
private class NestedModel extends NestedParentModel {
#JsonDeserializerWithOptions.FieldRequired Long id;
}
private class NestedParentModel {
#JsonDeserializerWithOptions.FieldRequired Integer rev;
}
private class ListModel {
#JsonDeserializerWithOptions.FieldRequired Long id;
}
We can be sure that SimpleModel will be parsed correctly without exceptions in this way:
final Gson gson = new GsonBuilder()
.registerTypeAdapter(SimpleModel.class, new JsonDeserializerWithOptions<SimpleModel>())
.create();
gson.fromJson("{\"list\":[ { \"id\":1 } ], \"id\":1, \"rev\":22, \"nested\": { \"id\":2, \"rev\":2 }}", SimpleModel.class);
Of course, provided solution can be improved and accept more features: for example - validations for nested objects which are not marked with FieldRequired annotation. Currently it's out of answer's scope, but can be added later.
(Inspired by Brian Roache's answer.)
It seems that Brian's answer doesn't work for primitives because the values can be initialized as something other than null (e.g. 0).
Moreover, it seems like the deserializer would have to be registered for every type. A more scalable solution uses TypeAdapterFactory (as below).
In certain circumstances, it is safer to whitelist exceptions from required fields (i.e. as JsonOptional fields) rather than annotating all fields as required.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface JsonOptional {
}
Though this approach can easily be adapted for required fields instead.
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class AnnotatedTypeAdapterFactory implements TypeAdapterFactory {
#Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
Set<Field> requiredFields = Stream.of(rawType.getDeclaredFields())
.filter(f -> f.getAnnotation(JsonOptional.class) == null)
.collect(Collectors.toSet());
if (requiredFields.isEmpty()) {
return null;
}
final TypeAdapter<T> baseAdapter = (TypeAdapter<T>) gson.getAdapter(rawType);
return new TypeAdapter<T>() {
#Override
public void write(JsonWriter jsonWriter, T o) throws IOException {
baseAdapter.write(jsonWriter, o);
}
#Override
public T read(JsonReader in) throws IOException {
JsonElement jsonElement = Streams.parse(in);
if (jsonElement.isJsonObject()) {
ArrayList<String> missingFields = new ArrayList<>();
for (Field field : requiredFields) {
if (!jsonElement.getAsJsonObject().has(field.getName())) {
missingFields.add(field.getName());
}
}
if (!missingFields.isEmpty()) {
throw new JsonParseException(
String.format("Missing required fields %s for %s",
missingFields, rawType.getName()));
}
}
TypeAdapter<T> delegate = gson.getDelegateAdapter(AnnotatedTypeAdapterFactory.this, typeToken);
return delegate.fromJsonTree(jsonElement);
}
};
}
}
This is my simple solution that creates a generic solution with minimum coding.
Create #Optional annotation
Mark First Optional. Rest are assumed optional. Earlier are assumed required.
Create a generic 'loader' method that checks that source Json object has a value. The loop stops once an #Optional field is encountered.
I am using subclassing so the grunt work is done in the superclass.
Here is the superclass code.
import com.google.gson.Gson;
import java.lang.reflect.Field;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
...
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface Optional {
public boolean enabled() default true;
}
and the grunt work method
#SuppressWarnings ("unchecked")
public <T> T payload(JsonObject oJR,Class<T> T) throws Exception {
StringBuilder oSB = new StringBuilder();
String sSep = "";
Object o = gson.fromJson(oJR,T);
// Ensure all fields are populated until we reach #Optional
Field[] oFlds = T.getDeclaredFields();
for(Field oFld:oFlds) {
Annotation oAnno = oFld.getAnnotation(Optional.class);
if (oAnno != null) break;
if (!oJR.has(oFld.getName())) {
oSB.append(sSep+oFld.getName());
sSep = ",";
}
}
if (oSB.length() > 0) throw CVT.e("Required fields "+oSB+" mising");
return (T)o;
}
and an example of usage
public static class Payload {
String sUserType ;
String sUserID ;
String sSecpw ;
#Optional
String sUserDev ;
String sUserMark ;
}
and the populating code
Payload oPL = payload(oJR,Payload.class);
In this case sUserDev and sUserMark are optional and the rest required. The solution relies on the fact that the class stores the Field definitions in the declared order.
I searched a lot and found no good answer. The solution I chose is as follows:
Every field that I need to set from JSON is an object, i.e. boxed Integer, Boolean, etc. Then, using reflection, I can check that the field is not null:
public class CJSONSerializable {
public void checkDeserialization() throws IllegalAccessException, JsonParseException {
for (Field f : getClass().getDeclaredFields()) {
if (f.get(this) == null) {
throw new JsonParseException("Field " + f.getName() + " was not initialized.");
}
}
}
}
From this class, I can derive my JSON object:
public class CJSONResp extends CJSONSerializable {
#SerializedName("Status")
public String status;
#SerializedName("Content-Type")
public String contentType;
}
and then after parsing with GSON, I can call checkDeserialization and it will report me if some of the fields is null.

Embedded HTML control for Blackberry?

Is there any api for viewing html content from w/in your blackberry application? To be clear, I don't mean launching the browser on top of my app to view a page. But rather rendering the page w/in my app.
Yes. Check out the net.rim.device.api.browser.field package or the Blackberry Browser section of application integration.
Everything sort of finishes here:
Field field = browserContent.getDisplayableContent();
See:
JDE 4.0.0 API for the package
RIM app integration guide
Signed only api, as usual.
//BrowserField is available for 4.5 OS. RIM provide sample app for BrowserField Demo. you can find the sample example and run it.
/*
* BrowserFieldDemo.java
*
* Copyright © 1998-2010 Research In Motion Ltd.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.blackberry.browser;
import java.io.IOException;
import javax.microedition.io.HttpConnection;
import net.rim.device.api.browser.field.*;
import net.rim.device.api.io.http.HttpHeaders;
import net.rim.device.api.system.Application;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.Status;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.system.*;
/**
* This sample application demonstrates how to create a web browser using the
* net.rim.device.api.browser.field package.
*/
public final class BrowserFieldDemo extends UiApplication implements RenderingApplication
{
private static String REFERER = "referer";
private RenderingSession _renderingSession;
private HttpConnection _currentConnection;
private MainScreen _mainScreen;
/**
* Entry point for application
* #param args Command line arguments (not used)
*/
public static void main(String[] args)
{
BrowserFieldDemo app = new BrowserFieldDemo();
// Make the currently running thread the application's event
// dispatch thread and begin processing events.
app.enterEventDispatcher();
}
// Constructor
public BrowserFieldDemo()
{
_mainScreen = new MainScreen();
pushScreen(_mainScreen);
_renderingSession = RenderingSession.getNewInstance();
// Enable javascript
//_renderingSession.getRenderingOptions().setProperty(RenderingOptions.CORE_OPTIONS_GUID, RenderingOptions.JAVASCRIPT_ENABLED, true);
PrimaryResourceFetchThread thread = new PrimaryResourceFetchThread("http://www.google.com", null, null, null, this);
thread.start();
}
/**
* Processes an http connection
*
* #param connection The connection to the web content
* #param e The event triggering the connection
*/
void processConnection(HttpConnection connection, Event e)
{
// Cancel previous request
if (_currentConnection != null)
{
try
{
_currentConnection.close();
}
catch (IOException e1)
{
}
}
_currentConnection = connection;
BrowserContent browserContent = null;
try
{
browserContent = _renderingSession.getBrowserContent(connection, this, e);
if (browserContent != null)
{
Field field = browserContent.getDisplayableContent();
if (field != null)
{
synchronized (Application.getEventLock())
{
_mainScreen.deleteAll();
_mainScreen.add(field);
}
}
browserContent.finishLoading();
}
}
catch (RenderingException re)
{
Utilities.errorDialog("RenderingSession#getBrowserContent() threw " + re.toString());
}
finally
{
SecondaryResourceFetchThread.doneAddingImages();
}
}
/**
* #see net.rim.device.api.browser.RenderingApplication#eventOccurred(net.rim.device.api.browser.Event)
*/
public Object eventOccurred(Event event)
{
int eventId = event.getUID();
switch (eventId)
{
case Event.EVENT_URL_REQUESTED :
{
UrlRequestedEvent urlRequestedEvent = (UrlRequestedEvent) event;
PrimaryResourceFetchThread thread = new PrimaryResourceFetchThread(urlRequestedEvent.getURL(),
urlRequestedEvent.getHeaders(),
urlRequestedEvent.getPostData(),
event, this);
thread.start();
break;
}
case Event.EVENT_BROWSER_CONTENT_CHANGED:
{
// Browser field title might have changed update title.
BrowserContentChangedEvent browserContentChangedEvent = (BrowserContentChangedEvent) event;
if (browserContentChangedEvent.getSource() instanceof BrowserContent)
{
BrowserContent browserField = (BrowserContent) browserContentChangedEvent.getSource();
String newTitle = browserField.getTitle();
if (newTitle != null)
{
synchronized (getAppEventLock())
{
_mainScreen.setTitle(newTitle);
}
}
}
break;
}
case Event.EVENT_REDIRECT :
{
RedirectEvent e = (RedirectEvent) event;
String referrer = e.getSourceURL();
switch (e.getType())
{
case RedirectEvent.TYPE_SINGLE_FRAME_REDIRECT :
// Show redirect message.
Application.getApplication().invokeAndWait(new Runnable()
{
public void run()
{
Status.show("You are being redirected to a different page...");
}
});
break;
case RedirectEvent.TYPE_JAVASCRIPT :
break;
case RedirectEvent.TYPE_META :
// MSIE and Mozilla don't send a Referer for META Refresh.
referrer = null;
break;
case RedirectEvent.TYPE_300_REDIRECT :
// MSIE, Mozilla, and Opera all send the original
// request's Referer as the Referer for the new
// request.
Object eventSource = e.getSource();
if (eventSource instanceof HttpConnection)
{
referrer = ((HttpConnection)eventSource).getRequestProperty(REFERER);
}
break;
}
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.setProperty(REFERER, referrer);
PrimaryResourceFetchThread thread = new PrimaryResourceFetchThread(e.getLocation(), requestHeaders,null, event, this);
thread.start();
break;
}
case Event.EVENT_CLOSE :
// TODO: close the appication
break;
case Event.EVENT_SET_HEADER : // No cache support.
case Event.EVENT_SET_HTTP_COOKIE : // No cookie support.
case Event.EVENT_HISTORY : // No history support.
case Event.EVENT_EXECUTING_SCRIPT : // No progress bar is supported.
case Event.EVENT_FULL_WINDOW : // No full window support.
case Event.EVENT_STOP : // No stop loading support.
default :
}
return null;
}
/**
* #see net.rim.device.api.browser.RenderingApplication#getAvailableHeight(net.rim.device.api.browser.BrowserContent)
*/
public int getAvailableHeight(BrowserContent browserField)
{
// Field has full screen.
return Display.getHeight();
}
/**
* #see net.rim.device.api.browser.RenderingApplication#getAvailableWidth(net.rim.device.api.browser.BrowserContent)
*/
public int getAvailableWidth(BrowserContent browserField)
{
// Field has full screen.
return Display.getWidth();
}
/**
* #see net.rim.device.api.browser.RenderingApplication#getHistoryPosition(net.rim.device.api.browser.BrowserContent)
*/
public int getHistoryPosition(BrowserContent browserField)
{
// No history support.
return 0;
}
/**
* #see net.rim.device.api.browser.RenderingApplication#getHTTPCookie(java.lang.String)
*/
public String getHTTPCookie(String url)
{
// No cookie support.
return null;
}
/**
* #see net.rim.device.api.browser.RenderingApplication#getResource(net.rim.device.api.browser.RequestedResource,
* net.rim.device.api.browser.BrowserContent)
*/
public HttpConnection getResource( RequestedResource resource, BrowserContent referrer)
{
if (resource == null)
{
return null;
}
// Check if this is cache-only request.
if (resource.isCacheOnly())
{
// No cache support.
return null;
}
String url = resource.getUrl();
if (url == null)
{
return null;
}
// If referrer is null we must return the connection.
if (referrer == null)
{
HttpConnection connection = Utilities.makeConnection(resource.getUrl(), resource.getRequestHeaders(), null);
return connection;
}
else
{
// If referrer is provided we can set up the connection on a separate thread.
SecondaryResourceFetchThread.enqueue(resource, referrer);
}
return null;
}
/**
* #see net.rim.device.api.browser.RenderingApplication#invokeRunnable(java.lang.Runnable)
*/
public void invokeRunnable(Runnable runnable)
{
(new Thread(runnable)).start();
}
}
/**
* A Thread class to fetch content using an http connection
*/
final class PrimaryResourceFetchThread extends Thread
{
private BrowserFieldDemo _application;
private Event _event;
private byte[] _postData;
private HttpHeaders _requestHeaders;
private String _url;
/**
* Constructor to create a PrimaryResourceFetchThread which fetches the web
* resource from the specified url.
*
* #param url The url to fetch the content from
* #param requestHeaders The http request headers used to fetch the content
* #param postData Data which is to be posted to the url
* #param event The event triggering the connection
* #param application The application requesting the connection
*/
PrimaryResourceFetchThread(String url, HttpHeaders requestHeaders, byte[] postData, Event event, BrowserFieldDemo application)
{
_url = url;
_requestHeaders = requestHeaders;
_postData = postData;
_application = application;
_event = event;
}
/**
* Connects to the url associated with this object
*
* #see java.lang.Thread#run()
*/
public void run()
{
HttpConnection connection = Utilities.makeConnection(_url, _requestHeaders, _postData);
_application.processConnection(connection, _event);
}
}
/////////////////////////////////////////////////////////////////
/*
* SecondaryResourceFetchThread.java
*
* Copyright © 1998-2010 Research In Motion Ltd.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.blackberry.browser;
import java.util.Vector;
import javax.microedition.io.HttpConnection;
import net.rim.device.api.browser.field.BrowserContent;
import net.rim.device.api.browser.field.RequestedResource;
/**
* This class provides the ability to set up an http connection if a referrer
* exists (a browser making the request).
*/
class SecondaryResourceFetchThread extends Thread
{
/**
* Callback browser field.
*/
private BrowserContent _browserField;
/**
* Images to retrieve.
*/
private Vector _imageQueue;
/**
* True is all images have been enqueued.
*/
private boolean _done;
/**
* Sync object.
*/
private static Object _syncObject = new Object();
/**
* Secondary thread.
*/
private static SecondaryResourceFetchThread _currentThread;
/**
* Enqueues secondary resource for a browser field.
*
* #param resource - resource to retrieve.
* #param referrer - call back browsr field.
*/
static void enqueue(RequestedResource resource, BrowserContent referrer)
{
if (resource == null)
{
return;
}
synchronized( _syncObject )
{
// Create new thread.
if (_currentThread == null)
{
_currentThread = new SecondaryResourceFetchThread();
_currentThread.start();
}
else
{
// If thread alread is running, check that we are adding images for the same browser field.
if (referrer != _currentThread._browserField)
{
synchronized( _currentThread._imageQueue)
{
// If the request is for a different browser field,
// clear old elements.
_currentThread._imageQueue.removeAllElements();
}
}
}
synchronized( _currentThread._imageQueue)
{
_currentThread._imageQueue.addElement(resource);
}
_currentThread._browserField = referrer;
}
}
/**
* Constructor
*
*/
private SecondaryResourceFetchThread()
{
_imageQueue = new Vector();
}
/**
* Indicate that all images have been enqueued for this browser field.
*/
static void doneAddingImages()
{
synchronized( _syncObject )
{
if (_currentThread != null)
{
_currentThread._done = true;
}
}
}
/**
* Connects to the requested resource
*
* #see java.lang.Runnable#run()
*/
public void run()
{
while (true)
{
if (_done)
{
// Check if we are done requesting images.
synchronized( _syncObject )
{
synchronized( _imageQueue )
{
if (_imageQueue.size() == 0)
{
_currentThread = null;
break;
}
}
}
}
RequestedResource resource = null;
// Request next image.
synchronized( _imageQueue )
{
if (_imageQueue.size() > 0)
{
resource = (RequestedResource)_imageQueue.elementAt(0);
_imageQueue.removeElementAt(0);
}
}
if (resource != null)
{
HttpConnection connection = Utilities.makeConnection(resource.getUrl(), resource.getRequestHeaders(), null);
resource.setHttpConnection(connection);
// Signal to the browser field that resource is ready.
if (_browserField != null)
{
_browserField.resourceReady(resource);
}
}
}
}
}
///////////////////////////////////////////////////////////////////
/*
* Utilities.java
*
* Copyright © 1998-2010 Research In Motion Ltd.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.blackberry.browser;
import java.io.IOException;
import java.io.OutputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import net.rim.device.api.io.http.HttpHeaders;
import net.rim.device.api.io.http.HttpProtocolConstants;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.util.StringUtilities;
/**
* This class provides common functions required by the
* BrowserContentManagerDemo and BrowserFieldDemo. This class allows the
* aforementioned classes to make a connection to a specified url.
*/
class Utilities
{
/**
* Connect to a web resource
* #param url The url of the resource
* #param requestHeaders The request headers describing the connection to be made
* #param postData The data to post to the web resource
* #return The HttpConnection object representing the connection to the resource, null if no connection could be made
*/
static HttpConnection makeConnection(String url, HttpHeaders requestHeaders, byte[] postData)
{
HttpConnection conn = null;
OutputStream out = null;
try
{
conn = (HttpConnection) Connector.open(url);
if (requestHeaders != null)
{
// From
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec15.html#sec15.1.3
//
// Clients SHOULD NOT include a Referer header field in a (non-secure) HTTP
// request if the referring page was transferred with a secure protocol.
String referer = requestHeaders.getPropertyValue("referer");
boolean sendReferrer = true;
if (referer != null && StringUtilities.startsWithIgnoreCase(referer, "https:") && !StringUtilities.startsWithIgnoreCase(url, "https:"))
{
sendReferrer = false;
}
int size = requestHeaders.size();
for (int i = 0; i < size;)
{
String header = requestHeaders.getPropertyKey(i);
// Remove referer header if needed.
if ( !sendReferrer && header.equals("referer"))
{
requestHeaders.removeProperty(i);
--size;
continue;
}
String value = requestHeaders.getPropertyValue( i++ );
if (value != null)
{
conn.setRequestProperty( header, value);
}
}
}
if (postData == null)
{
conn.setRequestMethod(HttpConnection.GET);
}
else
{
conn.setRequestMethod(HttpConnection.POST);
conn.setRequestProperty(HttpProtocolConstants.HEADER_CONTENT_LENGTH, String.valueOf(postData.length));
out = conn.openOutputStream();
out.write(postData);
}
}
catch (IOException e1)
{
errorDialog(e1.toString());
}
finally
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e2)
{
errorDialog("OutputStream#close() threw " + e2.toString());
}
}
}
return conn;
}
/**
* Presents a dialog to the user with a given message
* #param message The text to display
*/
public static void errorDialog(final String message)
{
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
Dialog.alert(message);
}
});
}
}