Scout/HowTo/3.8/Uncached images and icons
Scout |
Wiki Home |
Website |
Download • Git |
Community |
Forums • Blog • Twitter • G+ |
Bugzilla |
Bugzilla |
This how-to describes how to work with uncached images and icons.
Contents
- 1 Background
- 2 Solutions
- 2.1 Custom field: UncachedImageField
- 2.2 Extending SwingScoutImageField
- 2.3 Overwrite SwingEnvironment.createIconLocator
- 2.4 Extending SwingScoutImageField and overwriting SwingEnvironment.createFormField
- 2.5 Use ImageField.setImage() instead of ImageField.setImageId()
- 2.6 Not re-using iconId
- 2.7 Use MD5 checksum as iconId
- 3 Recommended solution
Background
When using icons/images in a table or a form an IconLocator
(which in turn uses a [IconProviderService
]) is used. The default IconLocator
used for the Swing environment is org.eclipse.scout.rt.ui.swing.SwingIconLocator
, the one for the SWT environment is SWTIconLocator
. The SwingIconLocator caches the images when they are first requested. This means that whenever a setIconId()
method on a form field or table column is called with an iconId
that has previously been used, the first image retrieved under that name will be returned, even if the image has changed since then (because it has changed on disk, for example).
There are various options to work around this but while all of them will work for form fields (AbstractImageField
), not all of them work for table columns. This page tries to list the various methods that can be used together with their advantages and disadvantages.
Solutions
Custom field: UncachedImageField
Synopsis: | Create a "deep" copy of the ImageField model and renderer and modify it to use an uncached IconLocator |
Advantages: |
|
Disadvantages: |
|
Status: | Not recommended |
Detailed steps needed: |
public class SwingUncachedIconLocator extends SwingIconLocator { private static final IScoutLogger LOG = ScoutLogManager.getLogger(SwingUncachedIconLocator.class); public static final Pattern IMAGE_WITH_STATE_PATTERN = Pattern.compile("(.*)(_active|_disabled|_mouse|_mouse_over|_open|_over|_pressed|_rollover|_selected)", Pattern.CASE_INSENSITIVE); private final Object m_cacheLock = new Object(); private IIconLocator m_iconLocator; public SwingUncachedIconLocator(IIconLocator iconLocator) { super(iconLocator); m_iconLocator = iconLocator; } @Override public Image getImage(String name) { if (name == null || AbstractIcons.Null.equals(name) || name.length() == 0) { return null; } Image img; synchronized (m_cacheLock) { img = createImageImpl(name); if (img == null) { img = autoCreateMissingImage(name); } if (LOG.isDebugEnabled()) { LOG.debug("load image '" + name + "' as " + img); } if (img == null) { warnImageNotFound(name); } } return img; } private Image createImageImpl(String name) { IconSpec iconSpec = m_iconLocator.getIconSpec(name); if (iconSpec != null) { Image img = Toolkit.getDefaultToolkit().createImage(iconSpec.getContent()); if (img != null) { //decorate window icon in development mode if (Platform.inDevelopmentMode() && name != null && name.matches("^(window\\d+|tray)$")) { img = decorateForDevelopment(img); } } return img; } return null; } }
private SwingUncachedIconLocator m_uncachedIconLocator; protected SwingUncachedIconLocator getUncachedIconLocator() { if (m_uncachedIconLocator == null) { m_uncachedIconLocator = new SwingUncachedIconLocator(getSwingEnvironment().getScoutSession().getIconLocator()); } return m_uncachedIconLocator; } and modify the setImageFromScout() method as follows: protected void setImageFromScout(String imageId, Object image) { if (image == null) { if (imageId != null) { // try to use uncached image image = getUncachedIconLocator().getImage(imageId); if (image == null) { // as fallback, use cached image image = getSwingEnvironment().getImage(imageId); } } } getSwingImageViewer().setImage(image); } |
.
Extending SwingScoutImageField
Synopsis: | Provide a new plugin in which UncachedSwingScoutImageField extends SwingScoutImageField and uses the UncachedIconLocator. |
Advantages: |
|
Disadvantages: |
|
Status: | Not recommended |
Detailed steps needed: Note: This solution has not been tried (though it should work) |
public class SwingScoutUncachedImageField extends SwingScoutImageField { private SwingUncachedIconLocator m_uncachedIconLocator; protected SwingUncachedIconLocator getUncachedIconLocator() { if (m_uncachedIconLocator == null) { m_uncachedIconLocator = new SwingUncachedIconLocator(getSwingEnvironment().getScoutSession().getIconLocator()); } return m_uncachedIconLocator; } @Override protected void setImageFromScout(String imageId, Object image) { if (image == null) { if (imageId != null) { // try to use uncached image image = getUncachedIconLocator().getImage(imageId); if (image == null) { // as fallback, use cached image image = getSwingEnvironment().getImage(imageId); } } } getSwingImageViewer().setImage(image); } }
<plugin> <extension point="org.eclipse.scout.rt.ui.swing.formfields"> <formField scope="global" active="true" modelClass="org.eclipse.scout.rt.client.ui.form.fields.imagebox.IImageField" name="imagefield"> <uiClass class="org.eclipse.minicrm.scout.rt.ui.swing.form.fields.SwingScoutUncachedImageField"> </uiClass> </formField> </extension> </plugin> |
.
Overwrite SwingEnvironment.createIconLocator
Synopsis: | Overwriting SwingEnvironemtn.createIconLocator to always return a non-caching IconLocator |
Advantages: |
|
Disadvantages: |
|
Status: | Not recommended |
Detailed steps needed: |
protected SwingIconLocator createIconLocator() { return new SwingUncachedIconLocator(getScoutSession().getIconLocator()); } |
.
Extending SwingScoutImageField and overwriting SwingEnvironment.createFormField
Synopsis: | Extend |
Advantages: |
|
Disadvantages: |
|
Status: | Not recommended |
Detailed steps needed: |
public class SwingScoutUncachedImageField extends SwingScoutImageField { private SwingUncachedIconLocator m_uncachedIconLocator; protected SwingUncachedIconLocator getUncachedIconLocator() { if (m_uncachedIconLocator == null) { m_uncachedIconLocator = new SwingUncachedIconLocator(getSwingEnvironment().getScoutSession().getIconLocator()); } return m_uncachedIconLocator; } @Override protected void setImageFromScout(String imageId, Object image) { if (image == null) { if (imageId != null) { // try to use uncached image image = getUncachedIconLocator().getImage(imageId); if (image == null) { // as fallback, use cached image image = getSwingEnvironment().getImage(imageId); } } } getSwingImageViewer().setImage(image); } }
@Override public ISwingScoutFormField<?> createFormField(JComponent parent, IFormField field) { if (field instanceof IImageField /* && add further conditions here */) { SwingScoutUncachedImageField ui = new SwingScoutUncachedImageField(); ui.createField((IImageField) field, this); decorate(field, ui); return ui; } return super.createFormField(parent, field); } |
.
Use ImageField.setImage() instead of ImageField.setImageId()
Synopsis: | Instead of passing the loaded image to an IconProviderService and then calling setImageId(imageId) on the ImageField, the content is directly passed to the ImageField using setImage(content) |
Advantages: |
|
Disadvantages: |
|
Status: | Recommended |
Detailed steps needed: |
public class MyForm extends AbstractForm { private byte[] content;
content = SERVICES.getService(IMyProcessService.class).loadImage(keyValue); getImageField().setImage(content);
public void updateImageFile(File file) throws ProcessingException { if (file != null) { content = new byte[(int) file.length()]; DataInputStream dis; try { dis = new DataInputStream(new FileInputStream(file)); dis.readFully(content); dis.close(); getImageField().setImage(content); } catch (FileNotFoundException e) { throw new ProcessingException(e.getMessage()); } catch (IOException e) { throw new ProcessingException(e.getMessage()); } } }
content = null; getImageField().setImage(content); |
.
Not re-using iconId
Synopsis: | Instead of using the key of the record showing on the form as imageId, the imageId is increased every time the image content changes (or even whenever the image is set). |
Advantages: |
|
Disadvantages: |
|
Status: | Not recommended |
Detailed steps needed: |
String imageId = "Image-" + keyValue + staticCounter++; content = SERVICES.getService(IMyProcessService.class).loadImage(imageId); org.eclipse.minicrm.client.Activator.getDefault().cacheImage(imageId, content); getUncachedImageField().setImageId(imageId); |
.
Use MD5 checksum as iconId
Synopsis: | Instead of using a constantly increasing imageId, independently of whether the image changed or not the MD5 checksum over the image data is used. This checksum only changes if the image content changed as well. The probability for identical MD5 checksums for different images is negligable. |
Advantages: |
|
Disadvantages: |
|
Status: | Recommended |
Detailed steps needed: |
if (content != null) { String md5; try { md5 = Base64Utility.encode(EncryptionUtility.signMD5(content)); } catch (NoSuchAlgorithmException e) { throw new ProcessingException("Could not create MD5 hash for person portrait: " + e.getMessage()); } String imageId = md5; content = SERVICES.getService(IMyProcessService.class).loadImage(imageId); org.eclipse.minicrm.client.Activator.getDefault().cacheImage(imageId, content); getUncachedImageField().setImageId(imageId); } else { getUncachedImageField().setImageId(null); }
@Override protected void execDecorateCell(Cell cell, ITableRow row) throws ProcessingException { String keyValue = getKeyValueColumn().getValue(row); String imageId = getValue(row); // this is the MD5 checksum if (!org.eclipse.minicrm.client.Activator.getDefault().isImageCached(imageId)) { byte[] content = SERVICES.getService(IMyProcessService.class).loadImage(keyValue); org.eclipse.minicrm.client.Activator.getDefault().cacheImage(imageId, content); } cell.setIconId(imageId); cell.setText(null); } |
.
Recommended solution
Synopsis: | The recommended solution is to combine two of the methods above: |
Advantages: |
|
Disadvantages: |
|
.
.
.
.
.
.