Thursday, December 14, 2017

Debugger 11: Watch expressions

Now that we have variables working, we might also want to include watch expressions to dynamically inspect code fragments.

Debug Framework Tutorials

For a list of all debug related tutorials see Debug Framework Tutorials Overview.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: Provide the Watch Expression Delegate

Watch points are implemented via an extension point. So switch to your plugin.xml and add a new extension point for org.eclipse.debug.core.watchExpressionDelegates.
The new delegate simply points to our debugModel identifier: com.codeandme.debugModelPresentation.textinterpreter and provides a class implementation:
public class TextWatchExpressionDelegate implements IWatchExpressionDelegate {

 @Override
 public void evaluateExpression(String expression, IDebugElement context, IWatchExpressionListener listener) {
  if (context instanceof TextStackFrame)
   ((TextStackFrame) context).getDebugTarget().fireModelEvent(new EvaluateExpressionRequest(expression, listener));
 }
}
Delegates can decide on which context they may operate. For our interpreter we could evaluate expressions on StackFrames, Threads or the Process, but typically evaluations do take place on a dedicated StackFrame.

Step 2: Evaluation

Now we apply the usual pattern: send an event, let the debugger process it and send some event back to the debug target. Once the evaluation is done we then will inform the provided listener of the outcome of the evaluation.
public class TextDebugTarget extends TextDebugElement implements IDebugTarget, IEventProcessor {

 @Override
 public void handleEvent(final IDebugEvent event) {

   [...]

   } else if (event instanceof EvaluateExpressionResult) {
    IWatchExpressionListener listener = ((EvaluateExpressionResult) event).getOriginalRequest().getListener();
    TextWatchExpressionResult result = new TextWatchExpressionResult((EvaluateExpressionResult)event, this);
    listener.watchEvaluationFinished(result);    
   }
 }
The TextWatchExpressionResult uses a TextValue to represent the evaluation result. As before with variables we may support nested child variables within the value. In case the evaluation failed for some reason we may provide error messages which do get displayed in the Expressions view.

Debugger 10: Editing variables

In the previous tutorial we introduced variables support for our debugger. Now lets see how we can modify variables dynamically during a debug session.

Debug Framework Tutorials

For a list of all debug related tutorials see Debug Framework Tutorials Overview.

Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.

Step 1: Allowing for editing and trigger update

First variables need to support editing. Then the variables view will automatically provide a text input box on the value field once clicked by the user. This is also a limitation: editing variables requires the framework to interpret an input string and process it accordingly to the target language.

The relevant changes for the TextVariable class are shown below:
public class TextVariable extends TextDebugElement implements IVariable {

 @Override
 public void setValue(String expression) {
  getDebugTarget().fireModelEvent(new ChangeVariableRequest(getName(), expression));
 }

 @Override
 public boolean supportsValueModification() {
  return true;
 }

 @Override
 public boolean verifyValue(String expression) throws DebugException {
  return true;
 }
}
verifyValue(String) and setValue(String) are used by the debug framework when a user tries to edit a variable in the UI. We do not need to update the value yet, but simply trigger an event to update the variable in the debugger.

Step 2: Variable update & refresh

As our primitive interpreter accepts any kind of text variables there is nothing which can go wrong here. Instead of sending an update event for the changed variable we simply use the already existing VariablesEvent to force a refresh of all variables of the current TextStackFrame:
public class TextDebugger implements IDebugger, IEventProcessor {

 @Override
 public void handleEvent(final IDebugEvent event) {

  [...]

  } else if (event instanceof ChangeVariableRequest) {
   fInterpreter.getVariables().put(((ChangeVariableRequest) event).getName(), ((ChangeVariableRequest) event).getContent());
   fireEvent(new VariablesEvent(fInterpreter.getVariables()));
  }
 }
}

Friday, July 14, 2017

EclipseCon Europe - Wouldn't it be cool to be on stage there?

I remember my first EclipseCon and how impressed I was by the quality of talks and the technical skills those speakers showed. Proposing a talk of my own was totally out of question.
A year later I thought: "proposing a talk does not harm, it will anyhow get rejected." So I spent 20 minutes on writing the proposal (took me 18 minutes to come up with a fancy title). Some months later I did my first talk at EclipseCon.

EclipseCon does have a lot of technical talks that require deep knowledge of certain aspects of eclipse. However often the most interesting talks are not even technical. I remember a talk from last year: Hearing and Feeling Eclipse - a story about how blind people experience and use our beloved IDE. It is also interesting how companies use Eclipse technology, how to overcome typical obstacles or ideas and proposals on how to improve Eclipse.

Such talks will attract developers and committers and introduce fresh ideas. Besides the audience is great, the spirit is awesome and it is perfect way to get in touch with people that work on similar topics.

Torkild wrote about the free tshirts you get, but he forgot about the beer and the band (which hopefully will also play this year).

So submit your talk right now. Its not a contract yet you have to fulfill, its a proposal :)


Monday, May 15, 2017

Extract eclipse svg images

When creating new icons for applications I like browsing existing eclipse svg images. The repository structure is nice when you know what to look for. But with all its subfolders it is not suited for interactive browsing.

While I am not worlds greatest bash script kiddie, I assembled a script that clones the repo and sorts its svg images. after execution you end up with a folder eclipse_images that hosts the svg files.

If you improve the script, please post it here so others can benefit.

#!/bin/bash

# create working dir
mkdir eclipse_images
cd eclipse_images/

# get images
git clone  git://git.eclipse.org/gitroot/platform/eclipse.platform.images.git

# extract all svg images
for line in `find eclipse.platform.images/ -iname "*.svg"`;
do
   echo line | awk -v source="$line" '{str=source; gsub(/\//, "_", str); gsub(/eclipse.platform.images_org.eclipse.images_eclipse-svg_/, "", str); gsub(/icons_full_/, "", str); gsub(/_icons_/, "_", str); print "mv \"" source "\" \""  str "\""}' | bash -sx
done

# remove rest of repository
rm -rf eclipse.platform.images

# extract subtype 'wizard banner'
mkdir "wizban"
for line in `find . -maxdepth 1 -iname "*_wizban_*.svg"`;
do
 mv "$line" "wizban"
done

# extract overlay images
mkdir "overlay"
for line in `find . -maxdepth 1 -regextype posix-extended -regex "^.*_ovr(16_.*)?.*.svg"`;
do
 mv "$line" "overlay"
done

# extract progress indicators
mkdir "progress"
for line in `find . -maxdepth 1 -regextype posix-extended -regex "^.*_(prgss|progress)_.*.svg"`;
do
 mv "$line" "progress"
done

# extract view images
mkdir "views"
for line in `find . -maxdepth 1 -regextype posix-extended -regex "^.*_e?view(16)?_.*.svg"`;
do
 mv "$line" "views"
done

# ... and all the rest
declare -a arr=("obj16" "elcl16" "clcl16" "etool16" "ctool16" "obj")
mkdir "images"
for token in "${arr[@]}"
do
 for line in `find . -maxdepth 1 -iname "*_${token}_*.svg"`;
 do
  mv "$line" "images"
 done
done

cd ..

Monday, April 24, 2017

Host your own eclipse signing server

We handled signing plugins with tycho some time ago already. When working in a larger company you might want to keep your certificates and passphrases hidden from your developers. For such a scenario a signing server could come in handy.

The eclipse CBI project provides such a server which just needs to get configured in the right way. Mikael Barbero posted a short howto on the mailing list, which should contain all you need. For a working setup example follow this tutorial.

To have a test vehicle for signing we will reuse the tycho 4 tutorial source files.

Step 1: Get the service

Download the latest service snapshot file and store it to a directory called signingService. Next download the test server, we will use it to create a temporary certificate and keystore.

Finally we need a template configuration file. Download it and store it to signingService/jar-signing-service.properties.

Step 2: A short test drive

Open a console and change into the signingService folder. There execute:
java -cp jar-signing-service-1.0.0-20170331.204711-10.jar:jar-signing-service-1.0.0-20170331.204711-10-tests.jar org.eclipse.cbi.webservice.signing.jar.TestServer
You should get some output giving you the local address of the signing service as long as the certificate store used:
Starting test signing server at http://localhost:3138/jarsigner
Dummy certificates, temporary files and logs are stored in folder: /tmp/TestServer-2590700922068591564
Jarsigner executable is: /opt/oracle-jdk-bin-1.8.0.121/bin/jarsigner
We are not ready yet to sign code, but at least we can test if the server is running correctly. If you try to connect with a browser you should get a message that HTTP method GET is not supported by this URL.

Step 3: Preparing the tycho project

We need some changes to our tycho project so it can make use of the signing server. Get the sources of the tycho 4 tutorial (checking out from git is fully sufficient) and add following code to com.codeandme.tycho.releng/pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <pluginRepositories>
  <pluginRepository>
   <id>cbi</id>
   <url>https://repo.eclipse.org/content/repositories/cbi-releases/</url>
  </pluginRepository>
 </pluginRepositories>

 <build>
  <plugins>
   <!-- enable jar signing -->
   <plugin>
    <groupId>org.eclipse.cbi.maven.plugins</groupId>
    <artifactId>eclipse-jarsigner-plugin</artifactId>
    <version>${eclipse.jarsigner.version}</version>
    <executions>
     <execution>
      <id>sign</id>
      <goals>
       <goal>sign</goal>
      </goals>
      <phase>verify</phase>
     </execution>
    </executions>
    <configuration>
     <signerUrl>http://localhost:3138/jarsigner</signerUrl>
    </configuration>
   </plugin>
   
  </plugins>
 </build>
</project>
The code above shows purely additions to the pom.xml, no sections were removed or replaced.

You may try to build your project with maven already. As I had problems to connect to https://timestamp.geotrust.com/tsa my local build failed, even if maven reported SUCCESS.

Step 4: Configuring a productive instance

So lets get productive. Setting up your keystore with your certificates will not be handled by this tutorial, so I will reuse the keystore created by the test instance. Copy the keystore.jks file from the temp folder to the signingService folder. Then create a text file keystore.pass:
echo keystorePassword >keystore.pass

Now we need to adapt the jar-signing-service.properties file to our needs:
### Example configuration file

server.service.pathspec=/jarsigner
server.service.pathspec.versioned=false

jarsigner.bin=/opt/oracle-jdk-bin-1.8.0.121/bin/jarsigner

jarsigner.keystore=/somewhere/signingService/keystore.jks
jarsigner.keystore.password=/somewhere/signingService/keystore.pass
jarsigner.keystore.alias=acme.org

jarsigner.tsa=http://timestamp.entrust.net/TSS/JavaHttpTS

  • By setting the versioned flag to false in line 4 we simplify the service web address (details can be found in the sample properties file).
  • Set the jarsigner executable path in line 6 according to your local environment.
  • Lines 8-10 contain details about the keystore and certificate to use, you will need to adapt them, but above settings should result in a working build.
  • The change in line 12 was necessary at the time of writing this tutorial because of connection problems to https://timestamp.geotrust.com/tsa.
Run your service using
java -jar jar-signing-service-1.0.0-20170331.204711-10.jar
Remember that your productive instance now runs on port 8080, so adapt your pom.xml accordingly.

Wednesday, March 15, 2017

Fancy tooltips

I always liked the tooltips available in eclipse editors. Having a browser widget that may capture focus is nice to display more complex help topics in the UI. Unfortunately the eclipse implementation is heavily bound to editors and cannot be used for other parts.

Well, up to now. For EASE I wanted to reuse these tooltips to display API documentation in a treeviewer. The result looks quite satisfactory:


I built some API to add these tooltips to any kind of SWT controls. While it may not be perfect it seems rather simple to use for me.
  final HoverManager hoverManager = new HoverManager(parent);
  hoverManager.addHover(fModulesComposite.getTreeViewer(), new IHoverContentProvider() {

   @Override
   public void populateToolbar(BrowserInformationControl control, ToolBarManager toolBarManager) {
    // nothing to do
   }

   @Override
   public String getContent(Object origin, Object detail) {
    return "<p>This is HTML content</p>";
   }
  });
To see these tooltips in action get a nightly build of EASE and open the Modules Explorer view.

Now I am wondering if there is any interest in making this API available for other eclipse projects.
When extracting the functionality I had to access some internal classes from org.eclipse.jface.text and JDT - mostly because of package private methods. Porting back these changes would be possible, still I am wondering if org.eclipse.jface.text would be the right place for it. Why should a generic view depend on jface.text just to get nice tooltip support?

So lets see if there is interest in adopting this feature and where to put it.