tag:blogger.com,1999:blog-8935379393157412312024-02-19T15:24:59.512+01:00Thomas M. Hofmann's Blogdig deeperThomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-893537939315741231.post-51788273131608820172019-07-16T23:08:00.000+02:002019-07-16T23:08:59.090+02:00I published GoogleCalendarTools on GitHubI just published a small tool to manipulate Google Calendar entries from the command line while being to able to mass- / batch-process several entries at once. For details see <a href="https://github.com/thomasmhofmann/google-calendar-tools" target="_blank">this GitHub repo</a>.<br />
<br />
While I was writing this tool a had a chance to learn <a href="https://picocli.info/" target="_blank">picocli</a> and the <a href="https://developers.google.com/api-client-library/java/" target="_blank">Google API Java client</a>.<br />
<br />
<br />Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-67391046191533488402014-11-16T19:49:00.002+01:002014-11-17T16:43:10.578+01:00Introducing Xposed Media Scanner Optimizer for AndroidLet me introduce my new Xposed Module. It allows you to customize the way the media scanner works on Android.<br />
<div>
It solves several problems that I have with the media scanner. As Xposed module developer one needs to reboot the device quite often. The media scanner runs on each reboot and eating much battery as the device stays awake while the scanner is running and doing lots of I/O.</div>
<div>
For non developer the Xposed Media Scanner Optimizer also provides useful functionality.</div>
<div>
<br /></div>
<div>
First of all let me explain how the media scanner works on Android. Each time the device has bootet it runs and scans all volumes/storage. Depending on the size of the storage available on the device and the SD Card and the number of files this usually takes between 5 and 20 minutes. For each file and directory it finds it creates an entry in the so-called Android Media Store. For special file types like images, videos and music files it also scans for meta data like EXIF information for images and mp3 tags for music files. Applications may query the Media Store to find specific files bases on the file type or meta data. Well known applications that do so are the Gallery and the Music Player. Other applications may also do so.</div>
<div>
The media scanner runs as a background service and is not visible to the user. </div>
<div>
<br /></div>
<div>
The Xposed Media Scanner Optimizer provides the following tweaks to the media scanner:</div>
<div>
<ul>
<li>The service can be run as a foreground service allowing the user to see when and how long it runs. Foregound services are visible to the user via a notification.</li>
<li>When the scanner has finished details of how long each scan took are shown using a notification.</li>
<li>The scanner can be run in a mode where only specific directories will be scanned. This reduces scan times and thus saves battery. It also allows you to control what shows up in the Gallery or Music Player apps.</li>
<li>It is possible to tell the scanner that only specific file types (e.g. music files) should be scanned on a directory basis.</li>
<li>It allows you to completely delete the contents of the media store.</li>
<li>It allows you to trigger a scan whenever you want to</li>
<li>On some Android phones the scanner is scheduled to run several times after a boot which is completely unnecessary and in my opinion is a bug. These repetitive scans can be prevented. </li>
</ul>
All files that will not be scanned will not show up in the Gallery or Music Player. I for myself use this feature to restrict what is shown in the Music Player for example. I only want to see music files from specific directories on my SD card to show up in the music player.</div>
<div>
The directories that contain my music files also contain images for the album and the artist. These files would normally show up in the Gallery. There, I only want to see the contents of the directories where I keep my pictures. I don't want to have the gallery cluttered with album artwork or images of artists from the music directories.</div>
<div>
<br /></div>
<div>
The Xposed Media Scanner Optimizer settings application can be started from the launcher and allows you to configure the following settings:</div>
<div>
<br /></div>
<div>
<ul>
<li>Repetitive Scans: If this preference is checked repetitive scans at boot time will be prevented. Repetitive scans are not necessary and only cause greater battery drain. Depending on the Android version and vendor repetitive scans may occur and can be prevented by checking this preference.</li>
<li>Service Importance: When checked the media scanner will run as foreground service and its operation will be visible to the user via a notification.</li>
<li>Thread Priority: If checked the media scanner will be forced to run with background thread priority even if it is running as a foreground service. This is the suggested setting. If unchecked the thread priority depends on whether it runs as a foreground service or not.</li>
<li>Directories: If checked the media scanner will only scan directories that have a .scanMedia file (i.e. an empty file with a dot as first part of the name similar to the .noMedia file). Use a file explorer app to create this file. This file can be placed in a directory that may contain subdirectories. The subdirectories will be scanned as well. If unchecked all directories will be scanned (except for the ones containing a .noMedia file - this default behavior of the media scanner is not altered by the Xposed Media Scanner Optimizer).</li>
<li>Media Types: If checked only certain file types will be scanned. To define which file types the scanner will scan in a directory create the following files: .scanMusic, .scanVideo and .scanPictures. So for a directory containing a collection of music albums you would need to create a .scanMusic file at the directory that contains all albums if you only want the music files scanned. Images contained in the album directories would then be skipped.</li>
<li>Result Notifications: If checked a notification will be shown when the scanner has finished including information about the scan time.</li>
<li>Trigger Media Scanner: When touched a media scanner is triggered.</li>
<li>Delete Media Store Contents: When touched the contents of the media store are deleted. This will not delete the actual files on the storage. Trigger a scan or reboot to let the scanner populate the media store after deleting its contents. This is useful if the media stores already contains entries that you do not want like images from all directories in the Gallery. First check the preferences to restrict what will be scanned, empty the media store and trigger a rescan. After the rescan only what has been scanned depending on your preferences will appear in the Gallery.</li>
<li>Android Logging: If this preference is checked details of the media scanner and the Xposed module are written to the Android log. This is useful for development and to troubleshoot. For end users this should stay unchecked.</li>
<li>Xposed Log: If this preference is checked details will be written to the Xposed log. Since the Xposed log is persisted this is also useful for troubleshooting. For end users this should stay unchecked.</li>
</ul>
Depending on the number of files on your storage media scan times can be reduced significantly.</div>
<div>
<br /></div>
<div>
The Xposed module is available from the <a href="http://repo.xposed.info/module/com.thomashofmann.xposed.mediascanneroptimizer" target="_blank">Xposed repository</a>. Please use <a href="http://forum.xda-developers.com/xposed/modules/xposed-xposed-media-scanner-optimizer-t2942609" target="_blank">this XDA Developer forum thread</a> for feedback and discussions.</div>
<div>
<br /></div>
<div>
It should work on Android 4.1.2 (API 16) and up.</div>
<div>
<br /></div>
<div>
If you like the module feel free to donate :)</div>
<div>
<br /></div>
Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-17272264764991923172014-07-05T18:11:00.000+02:002014-07-05T18:11:39.764+02:00Fixing random reboots on Samsung Kitkat Firmwares like I9505XXUGNF1<span style="background-color: white; color: #222225; font-family: Arial, Helvetica, sans-serif; line-height: 17.290000915527344px;">After updating my SGS 4 to I9505XXUGNF1 I like many others experienced random crashes which at first glance seemed to be reboots of the device. Looking at the logcat I figured out that actually the system server process crashes due to an ArrayIndexOutOfBoundsException. After reading the source code I figured what the problem is and wrote this small module to workaround the crash. My S4 is running without a single crash since.</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; color: #222225; line-height: 17.290000915527344px;"><br /></span>
<span style="color: #222225;"><span style="background-color: white; line-height: 17.290000915527344px;">Here are some technical details:</span></span></span><br />
<span style="font-family: Arial, Helvetica, sans-serif;"><span style="color: #222225;"><span style="background-color: white; line-height: 17.290000915527344px;"><br /></span></span>
<span style="color: #222225;"><span style="background-color: white; line-height: 17.290000915527344px;">The logcat shows:</span></span></span><br />
<span style="color: #222225; font-family: Arial, Helvetica, sans-serif; font-size: x-small;"><span style="background-color: white; line-height: 17.290000915527344px;"><br /></span></span>
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): !@*** FATAL EXCEPTION IN SYSTEM PROCESS: ActivityManager</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): java.lang.ArrayIndexOutOfBoundsException: length=14; index=-1</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ProcessList.computeNextPssTime(ProcessList.java:580)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.requestPssAllProcsLocked(ActivityManagerService.java:16951)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.updateOomAdjLocked(ActivityManagerService.java:17766)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService.trimApplications(ActivityManagerService.java:17831)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor.activityIdleInternalLocked(ActivityStackSupervisor.java:2992)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.activityIdleInternal(ActivityStackSupervisor.java:3961)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityStackSupervisor$ActivityStackSupervisorHandler.handleMessage(ActivityStackSupervisor.java:3983)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at android.os.Handler.dispatchMessage(Handler.java:102)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at android.os.Looper.loop(Looper.java:157)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;">06-27 16:29:22.471 E/AndroidRuntime(28244): at com.android.server.am.ActivityManagerService$AThread.run(ActivityManagerService.java:2376)</span><br />
<span style="background-color: white; font-family: 'Courier New', Courier, monospace; font-size: 14px;"><br /></span>
<span style="color: #222225;"><span style="background-color: white; line-height: 17.290000915527344px;"><span style="font-family: Arial, Helvetica, sans-serif;">The problem is in the method </span><span style="font-family: Courier New, Courier, monospace;"><a href="https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r1/services/java/com/android/server/am/ProcessList.java" target="_blank">computeNextPssTime</a></span><span style="font-family: Arial, Helvetica, sans-serif;">.</span></span><span style="font-family: Arial, Helvetica, sans-serif; line-height: 17.290000915527344px;"></span></span><br />
<span style="color: #222225; font-family: Arial, Helvetica, sans-serif;"><br /></span>
<span style="color: #222225; font-family: Arial, Helvetica, sans-serif;">The last statement in the method accesses either the array named </span><span style="background-color: white; line-height: 16.426668167114258px; white-space: pre;"><span style="font-family: Courier New, Courier, monospace;">sFirstAwakePssTimes </span><span style="font-family: Arial, Helvetica, sans-serif;">or </span></span><span style="background-color: white; font-family: Courier New, Courier, monospace; line-height: 16.426668167114258px; white-space: pre;">sSameAwakePssTimes</span><span style="background-color: white; font-family: Arial, Helvetica, sans-serif; line-height: 16.426668167114258px; white-space: pre;">. An </span><span style="background-color: white;"><span style="font-family: Courier New, Courier, monospace;">ArrayIndexOutOfBoundsException </span></span><span style="background-color: white; font-family: Arial, Helvetica, sans-serif;">happens when the parameter </span><span style="background-color: white;"><span style="font-family: Courier New, Courier, monospace;">procState </span></span><span style="background-color: white; font-family: Arial, Helvetica, sans-serif;">is wrong. My first guess was that -1 is used as index into the array.</span><br />
<span style="background-color: white;"><span style="font-family: Arial, Helvetica, sans-serif;">Looking at the call stack one can see that the parameter<a href="https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r1/services/java/com/android/server/am/ActivityManagerService.java" target="_blank"> is a value taken from</a> an instance of the class </span></span><span class="typ" style="background-color: white; color: #660066; font-family: monospace; line-height: 16.426668167114258px; white-space: pre;"><a href="https://android.googlesource.com/platform/frameworks/base/+/android-4.4.2_r1/services/java/com/android/server/am/ProcessRecord.java" target="_blank">ProcessRecord</a>.</span><span style="background-color: white; font-family: Arial, Helvetica, sans-serif;"> The value is initialized with -1 when that instance is created.</span><br />
<span style="background-color: white;"><span style="font-family: Arial, Helvetica, sans-serif;"><br /></span></span>
<span style="background-color: white;"><span style="font-family: Arial, Helvetica, sans-serif;">Somehow this invalid value for the </span><span style="font-family: Courier New, Courier, monospace;">procState </span><span style="font-family: Arial, Helvetica, sans-serif;">is being used and finally ends up being used as the index into the array.</span></span><br />
<span style="background-color: white;"><span style="font-family: Arial, Helvetica, sans-serif;"><br /></span></span>
<span style="font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white;">I don't know what causes this (could be timing issues or some changes that Samsung made). Nevertheless, it is possible to prevent the system server to crash by changing what the </span></span><span style="background-color: white; color: #222225; line-height: 17.290000915527344px;"><span style="font-family: Courier New, Courier, monospace;">computeNextPssTime </span></span><span style="background-color: white; color: #222225; font-family: Arial, Helvetica, sans-serif; line-height: 17.290000915527344px;">method does. </span><br />
<span style="background-color: white; color: #222225; font-family: Arial, Helvetica, sans-serif; line-height: 17.290000915527344px;"><br /></span>
<span style="background-color: white; color: #222225; font-family: Arial, Helvetica, sans-serif; line-height: 17.290000915527344px;">Using the amazing <a href="http://repo.xposed.info/module/de.robv.android.xposed.installer" target="_blank">Xposed Framework</a> I created <a href="http://repo.xposed.info/module/com.thomashofmann.xposed.samsungkitkatsystemservercrashfix" target="_blank">an Xposed module</a> that will check the </span><span style="background-color: white; color: #222225; line-height: 17.290000915527344px;"><span style="font-family: Courier New, Courier, monospace;">procStat </span></span><span style="background-color: white; color: #222225; font-family: Arial, Helvetica, sans-serif; line-height: 17.290000915527344px;">parameter value and in case of an invalid valued (like -1) will return a default value for the result of the method. This works pretty well to workaround the problem. Of course, it does not fix the actual problem but my phone is running well since then.</span><br />
<span style="background-color: white; color: #222225; font-family: Arial, Helvetica, sans-serif; line-height: 17.290000915527344px;"><br /></span>
<span style="color: #222225; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; line-height: 17.290000915527344px;">Whenever -1 or any other invalid value is passed to </span></span><span style="background-color: white; color: #222225; font-family: 'Courier New', Courier, monospace; line-height: 17.290000915527344px;">computeNextPssTime</span><span style="background-color: white; color: #222225; font-family: 'Courier New', Courier, monospace; line-height: 17.290000915527344px;"> </span><span style="background-color: white; color: #222225; line-height: 17.290000915527344px;"><span style="font-family: Arial, Helvetica, sans-serif;">a message is logged to Android's log with the tag SSCF. Using logcat you can see how often this happens and how often the system server would crash if not using the module.</span></span><br />
<span style="background-color: white; color: #222225; line-height: 17.290000915527344px;"><span style="font-family: Arial, Helvetica, sans-serif;"><br /></span></span>
<span style="color: #222225; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; line-height: 17.290000915527344px;">Since the problematic code is in AOSP I wonder why this seems to happen on newer Samsung Kitkat ROMs only. To me the problem seems to occur with an increasing chance the more processes are running. Maybe this is why users are starting to see random reboots only after a few days (when more apps might have been installed). At least this is what happened to me.</span></span><br />
<span style="color: #222225; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; line-height: 17.290000915527344px;"><br /></span></span>
<span style="color: #222225; font-family: Arial, Helvetica, sans-serif;"><span style="background-color: white; line-height: 17.290000915527344px;">Discussion can be found <a href="http://forum.xda-developers.com/xposed/modules/samsung-kitkat-systemserver-crash-fix-t2806046" target="_blank">on this XDA forum thread</a>.</span></span>Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com1tag:blogger.com,1999:blog-893537939315741231.post-9190283977965146362013-10-16T14:18:00.000+02:002013-10-16T14:18:28.898+02:00Using Command Parameter Value Convertes with Eclipse e4This post describes how value converters for command parameters can be used in e4.<br />
<br />
In e4, commands can have parameters. Each parameter has a value. When you specify theses values in the application model they are all strings.<br />
<br />
Suppose you want to pass an Integer value for a command parameter. In your handler you would like to get the value injected as an Integer instance so that you do not need to perform the conversion in the handlers @CanExecute or @Execute method.<br />
<br />
The necessary conversion can be done by the e4 framework before the handler is invoked. Take the following approach.<br />
<h3>
</h3>
<h3>
</h3>
<h3>
Implement the converter</h3>
<div>
Create a new class that inherits from <i>org.eclipse.core.commands.AbstractParameterValueConverter. </i>Implement the abstract methods. These are:</div>
<div>
<br /></div>
<div>
<i>Object convertToObject(String parameterValue) </i>and <i>String convertToString(Object parameterValue)</i></div>
<div>
<br /></div>
<div>
In the <i>convertToObject </i>method convert the string value of the command parameter to an instance of the type you require. For String to Integer conversion this could be done by simple calling <i>Integer.valueOf(parameterValue). </i>The signature of the method declares to throw <i>ParameterValueConversionException</i> in case the conversion fails. Create an instance of this exception and throw it in case the parameter value cannot be converted. Unfortunately, this exception is currently (Kepler 4.3.1) swallowed in the eclipse e4 framework so you won't have any idea why things are not working. You might want to log the problem for this reason right in the converter.</div>
<div>
<br /></div>
<div>
Now comes the tricky part. The <i>convertToString </i>method does not do the reverse of the convertToObject method (I thought so at first). It is called with the string value and needs to return a string. For simple String to Integer conversion you can just return the same string that is passed to the method. You might want to treat a null value different and return the string <i>"null"</i> for example.</div>
<h3>
</h3>
<h3>
</h3>
<h3>
Register the converter</h3>
<div>
In the bundle that supplies the value converter extend the org.eclipse.ui.commands extension point and add a commandParameterType.</div>
<div>
<br /></div>
<div>
The commandParameterType has three properties: <i>id</i>, <i>type</i> and <i>converter</i>.</div>
<div>
<br /></div>
<div>
Choose an <i>id</i> that is specific to your bundle name. For the <i>type</i> enter the class name of the objects you want to convert the string value to, e.g. <i>java.lang.Interger</i>. The converter property takes the fully qualified class name of the class that is called to convert the value (the one you implemented before). The class must be in a package that is exported by the bundle.</div>
<h3>
</h3>
<h3>
</h3>
<h3>
Specify the TypeID for the Command Parameter</h3>
<div>
The last step is to specify the <i>TypeID </i> for the command parameter in the application model. Each command parameter has a property <i>TypeID</i> that usually does not need to be filled with a value. If you want to make use of the converter just registered enter the ID of the <i>commandParameterType</i> you registered through the extension point above. Do <b>not</b> enter the type of the objects you want to convert to (e.g. java.lang.Integer).</div>
<div>
<br /></div>
<div>
Now the command parameter values should be converted automatically. Make sure to adjust the signature of your handler methods so that they are found and called.</div>
<div>
<br /></div>
<div>
For debugging purposed you can set a breakpoint in <i>org.eclipse.core.commands.ParameterizedCommand.generateCommand(Command, Map) </i>and of course in your converter class.</div>
<div>
<br /></div>
<div>
<br /></div>
Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-53769538831511135542012-04-15T00:10:00.002+02:002012-04-15T00:10:50.863+02:00IBM ED Search for Android - Part 4<h2>
Change Indicators</h2>
A result of a search or the list of persons in a bluegroup might change over time. If you trigger a reload of the search result new persons might be added, others might be removed.<br />
<br />
To better understand these changes the app uses <i>change indicators</i>. These are narrow colored lines to the left of the person picture. New persons in a search result will have a green line, persons removed will have a red line and persons where the information in the directory has changed since the last time it was loaded will have a yellow line.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://4.bp.blogspot.com/-GhZxHVVud9U/T4n1KufgIkI/AAAAAAAAGRk/5PzUdL_5gFo/s1600/Screenshot_2012-04-14-23-49-54_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://4.bp.blogspot.com/-GhZxHVVud9U/T4n1KufgIkI/AAAAAAAAGRk/5PzUdL_5gFo/s400/Screenshot_2012-04-14-23-49-54_pix.png" width="240" /></a><a href="http://1.bp.blogspot.com/-1Uk6QBMksZ4/T4n1LH3kPdI/AAAAAAAAGRo/XPwGCoZzTII/s1600/Screenshot_2012-04-14-23-50-08_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://1.bp.blogspot.com/-1Uk6QBMksZ4/T4n1LH3kPdI/AAAAAAAAGRo/XPwGCoZzTII/s400/Screenshot_2012-04-14-23-50-08_pix.png" width="240" /></a></div>
<br />
<br />
It should be pointed out that persons that are no longer part of the search result will not be deleted from the device. Instead they are marked with the red change indicator. This has the following advantages:<br />
<ul>
<li>If you still want to have that person record available in the app you can choose to star the person and it will be available under <i>All Starred Persons</i></li>
<li>You can see who has been removed from a bluegroup</li>
</ul>
The change indicators are displayed in the person search result until they are actively<i> confirmed. </i>To confirm them trigger the action with the check mark icon from the action bar. After confirming the changes all indicators are removed and the persons that are no longer part of the search result (the ones that had the red change indicator) are actually deleted.<br />
<br />In case the change indicators are not confirmed and the search result is reloaded again new change indicators will be added to the existing ones.<br />
<br />
Therefor, it is possible that more than one change indicator will be shown for a person. When a person was added to a bluegroup for example and later the information about that person changed in the directory a green and yellow change indicator will be shown. Likewise, when the person was first added and then later removed a green and red change indicator will appear.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://3.bp.blogspot.com/-0RQvo3_yiC8/T4n1LylxZTI/AAAAAAAAGRw/295ckIYVJKs/s1600/Screenshot_2012-04-14-23-56-09_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://3.bp.blogspot.com/-0RQvo3_yiC8/T4n1LylxZTI/AAAAAAAAGRw/295ckIYVJKs/s400/Screenshot_2012-04-14-23-56-09_pix.png" width="240" /></a></div>
<br />
<br />
Note that persons marked with a red change indicator might have left the company and are no longer in the directory. When you try to load the person details for such a person the app will display a short message that the person could not be found in the directory. <br />
<br />
You can also apply a filter for change indicators to restrict the results shown to only new, changed or deleted persons. A future post will cover filtering. <br />
<br />
<br />Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-24662152466762973852012-04-14T23:21:00.000+02:002012-04-14T23:21:00.312+02:00IBM ED Search for Android - Part 3<h2>
Shortcuts to starred search results</h2>
When the app is launched the list of starred search results is immediately visible. Touching one the entries in the list opens the search result view.<br />
<br />
Yet, there is a quicker way to access your most often needed search result(s). This could be the list of members of a bluegroup you frequently need to access in order to call someone.<br />
<br />
Android has the concept of shortcuts that can be placed directly on the launcher's home screen. Such a shortcut is able to launch into a specific activity in the app.<br />
<br />
You can create a shortcut on the launcher's screen for any starred search result. Tapping the shortcut will immediately load the search result and save a little bit of time when looking up contacts.<br />
<br />
To create a shortcut a search result must be starred and thus be available for offline use.<br />
<br />
Each launcher has a different but similar method to create a shortcut. Usually, you can either use the menu or long press the launcher's home screen background to create a shortcut. On Ice Cream Sandwich (Android 4.x) shortcuts can also be created from the Widgets section in the app drawer.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://4.bp.blogspot.com/-n_6SaLqiiE8/T4npYBaFyfI/AAAAAAAAGRA/CmZiIlOEXAg/s1600/Screenshot_2012-04-12-16-29-44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://4.bp.blogspot.com/-n_6SaLqiiE8/T4npYBaFyfI/AAAAAAAAGRA/CmZiIlOEXAg/s400/Screenshot_2012-04-12-16-29-44.png" width="240" /></a></div>
<br />
<br />
So start to create a shortcut for the IBM ED Search app by dragging the widget to the launcher's home screen.<br />
<br />
<br />
As soon as the widget is dropped the IBM ED Search app will open an activity to choose the starred search result for which the shortcut is to be created.<br />
<br />
On this screen the name of the shortcut can be changed. By default, the value for the name is the same as it is shown in the list of starred search result.<br />
<br />
There is also an option to force reloading of the search result when it is opened using the shortcut.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://4.bp.blogspot.com/-GKalDJ5wqS0/T4npop0DhcI/AAAAAAAAGRQ/cz69zVUXeYA/s1600/Screenshot_2012-04-12-16-29-54.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://4.bp.blogspot.com/-GKalDJ5wqS0/T4npop0DhcI/AAAAAAAAGRQ/cz69zVUXeYA/s400/Screenshot_2012-04-12-16-29-54.png" width="240" /></a></div>
<br />
<br />
Customize the name if you like and decide whether the result should be reloaded and then tap on one of the entries in the list. This will create the shortcut.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-pfdZJPzUvSU/T4nphMaFOVI/AAAAAAAAGRI/QiSnC9m5Odc/s1600/Screenshot_2012-04-12-16-31-36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://1.bp.blogspot.com/-pfdZJPzUvSU/T4nphMaFOVI/AAAAAAAAGRI/QiSnC9m5Odc/s400/Screenshot_2012-04-12-16-31-36.png" width="240" /></a></div>
<br />
For different kind of search results different icon overlays are used. A shortcut for the members of a bluegroup will have a different icon than the result for a management chain query.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-b8WfWPHF-XE/T4np4DLMe7I/AAAAAAAAGRY/uBkBOlZLKS8/s1600/Screenshot_2012-04-12-16-32-13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://1.bp.blogspot.com/-b8WfWPHF-XE/T4np4DLMe7I/AAAAAAAAGRY/uBkBOlZLKS8/s400/Screenshot_2012-04-12-16-32-13.png" width="240" /></a></div>
<br />
Deleting a shortcut from the launcher will not in any way delete the app or the data that is stored for it.<br />
<div id="-chrome-auto-translate-plugin-dialog" style="background-attachment: initial !important; background-clip: initial !important; background-color: transparent !important; background-image: initial !important; background-origin: initial !important; display: none; left: 0px; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; opacity: 1 !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: absolute !important; text-align: left !important; top: 0px; z-index: 999999 !important;">
<div style="-webkit-border-radius: 10px !important; background-color: #363636 !important; background-image: -webkit-gradient(linear, left top, right bottom, color-stop(0%, #000), color-stop(50%, #363636), color-stop(100%, #000)); border-color: #000000 !important; border-width: 0px !important; color: #fafafa !important; font-size: 16px !important; max-width: 300px !important; opacity: 0.8 !important; overflow: visible !important; padding: 8px !important; text-align: left !important; z-index: 999999 !important;">
<div class="translate">
</div>
<div class="additional">
</div>
</div>
<img onclick="document.location.href='http://translate.google.com/';" src="https://www.google.com/uds/css/small-logo.png" style="-webkit-border-radius: 20px; background-color: rgba(200, 200, 200, 0.3) !important; cursor: pointer !important; margin: 0 !important; padding: 3px 5px 0 !important; position: absolute !important; right: 1px !important; top: -20px !important; z-index: -1 !important;" /></div>Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-49551516337579760542012-04-12T14:16:00.001+02:002012-04-12T15:56:24.647+02:00IBM ED Search for Android - Part 2<h2>
This second blog post will focus on the Person Details Screen</h2>
When you touch a person entry in a search result the person details screen / view will open showing more information about the person you selected. When the details screen is opened for the first time for a person the app will try to download additional information about the person (department, secretary, etc.). Once this information has been downloaded it will be persisted and will be available offline. Notice how the number of entries in the details list grows when new data is downloaded.<br />
<br />
In case the details screen is opened and no connectivity to the directory server can be established only the information that is already loaded will be available. The next time the details screen is opened or when you trigger the reload action the app will again try to download all details.<br />
<br />
The details screen shows the person's bluepage picture, the name, job description and a list of details. The entries in the list of details show icons that can be touched to trigger an action associated with the detail.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://2.bp.blogspot.com/-cL-lryKEY0w/T4bGJivVUbI/AAAAAAAAGKk/GizTC7jde0A/s1600/Screenshot_2012-04-12-11-39-00_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://2.bp.blogspot.com/-cL-lryKEY0w/T4bGJivVUbI/AAAAAAAAGKk/GizTC7jde0A/s400/Screenshot_2012-04-12-11-39-00_pix.png" width="240" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-VKNgkhUff1M/T4bGunqxGFI/AAAAAAAAGLM/Csuw-KPgqeo/s1600/Screenshot_2012-04-12-11-39-11_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://1.bp.blogspot.com/-VKNgkhUff1M/T4bGunqxGFI/AAAAAAAAGLM/Csuw-KPgqeo/s400/Screenshot_2012-04-12-11-39-11_pix.png" width="240" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-i1XqYkcWrkI/T4bGve5xMAI/AAAAAAAAGLQ/GV0-jvHgR1A/s1600/Screenshot_2012-04-12-11-39-22_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://1.bp.blogspot.com/-i1XqYkcWrkI/T4bGve5xMAI/AAAAAAAAGLQ/GV0-jvHgR1A/s400/Screenshot_2012-04-12-11-39-22_pix.png" width="240" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://2.bp.blogspot.com/-_vrFKdfYQ5Q/T4bGwNPV16I/AAAAAAAAGLY/VRdGfhJHOpE/s1600/Screenshot_2012-04-12-11-39-45_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://2.bp.blogspot.com/-_vrFKdfYQ5Q/T4bGwNPV16I/AAAAAAAAGLY/VRdGfhJHOpE/s400/Screenshot_2012-04-12-11-39-45_pix.png" width="240" /></a></div>
<br />
<br />
For example, clicking the icon to the right hand side of the department detail will trigger a search for all persons in that department. Touching the icon on the email address detail will open the email application with the email address filled in as recipient. For the primary email address there is an action to start a Sametime chat with the person. From the manager detail you can either open the details for the person's direct manager or you can search for the complete management chain. You can also dial a telephone number this way or start google maps to display the work location or start the google maps navigation.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://1.bp.blogspot.com/-U1Hl23xAW4U/T4bGX6tP8oI/AAAAAAAAGKs/kCb8n2dKng4/s1600/departmentDetails.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="66" src="http://1.bp.blogspot.com/-U1Hl23xAW4U/T4bGX6tP8oI/AAAAAAAAGKs/kCb8n2dKng4/s320/departmentDetails.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://3.bp.blogspot.com/-g13hnfNrRIA/T4bGYefc5vI/AAAAAAAAGKw/lr-eJlIssac/s1600/emailDetails_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="http://3.bp.blogspot.com/-g13hnfNrRIA/T4bGYefc5vI/AAAAAAAAGKw/lr-eJlIssac/s320/emailDetails_pix.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://2.bp.blogspot.com/-uQUPcsiiUaU/T4bGYzVuScI/AAAAAAAAGK4/rV8sVp00eHE/s1600/managerDetails.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="61" src="http://2.bp.blogspot.com/-uQUPcsiiUaU/T4bGYzVuScI/AAAAAAAAGK4/rV8sVp00eHE/s320/managerDetails.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="http://2.bp.blogspot.com/-MFTLy7qFvWQ/T4bGZu5riNI/AAAAAAAAGLE/VZm1v-R7o9A/s1600/worklocationDetails.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="86" src="http://2.bp.blogspot.com/-MFTLy7qFvWQ/T4bGZu5riNI/AAAAAAAAGLE/VZm1v-R7o9A/s320/worklocationDetails.png" width="320" /></a></div>
<br />
<br />
Additional actions are available at the bottom of the screen in the action bar. You can start or reload all the information about a person for example. Reloading will also try to reload the person picture. Remember that you can long press an icon on the action bar to show a tool tip that further explains what the action will do.<br />
<br />
Since not all available actions for a person are shown in the action bar check out the additional actions available when you press the menu button or the action overflow (depending on whether your Android device has a menu button or not). Currently these actions are "Bluepages" and "IBM Connections" which will launch the browser and open up the associated person profile in either Bluepages or IBM Connections.<br />
<br />
Once you navigate away from the person details screen (e.g. by triggering a search for the members of the same department or the secretary information) the app will either show a list of search results in the former case or navigate directly to another person details screen in the later case. Using the back button you will return to the person details screen of the person you began with.<br />
<br />
You can always hit the icon on the left of the upper action bar to return to the home screen to conduct a new search. If you do so the history will be cleared and the back button will no longer bring you back to the previous screen.<br />
<br />
Long pressing / touching an entry in the details list opens up a context menu where you can choose to copy the text of the detail to the clipboard. This can be handy to copy the address for example.<br />
<br />
<div id="-chrome-auto-translate-plugin-dialog" style="background-attachment: initial !important; background-clip: initial !important; background-color: transparent !important; background-image: initial !important; background-origin: initial !important; display: none; left: 0px; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; opacity: 1 !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: absolute !important; text-align: left !important; top: 0px; z-index: 999999 !important;">
undefined</div>
Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-87446718019353772582012-04-08T15:37:00.000+02:002012-04-12T11:18:07.481+02:00IBM ED Search for Android - Part 1<div>
I've decided to write a series of blog posts about my Android App "IBM ED Search" to explain basic usage and some more advanced topics.<br />
<br />
The series will use version 2.0 that will be released real soon now.<br />
<br />
In this first post I will focus on the very basics of the application.<br />
<br />
Let's begin...<br />
<br />
<h2>
Initial Steps and the Search UI</h2>
<br />
Version 2 makes use of the excellent <a href="http://actionbarsherlock.com/" target="_blank">Actionbar Sherlock</a> library that makes the Android Ice Cream Sandwich Action Bar available on earlier Android versions. The UI looks and feels very similar on devices running Android 2.2 up to the latest version 4.0.<br />
<br />
After starting the app for the first time the home screen is shown. The action bar is located at the top and includes a small magnifying glass icon on the right hand side.<br />
A short introduction is shown below the action bar when the app is started for the first time and as long as no searches have been "starred".<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRU-RFOntQKMErK-D-iQiKI5C_IQAIv1tnn5Vj-fzkcqCo-77FO9n_I0RMW8sYke-DWrYtw3PN6DX7oyLWCEAGrfNewbEC7E9a5YdTkDRsdf9UxmCdQdlE3pXsxU3uTqGbLhfzMznecOCX/s1600/shot_Apr_06_2012_0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRU-RFOntQKMErK-D-iQiKI5C_IQAIv1tnn5Vj-fzkcqCo-77FO9n_I0RMW8sYke-DWrYtw3PN6DX7oyLWCEAGrfNewbEC7E9a5YdTkDRsdf9UxmCdQdlE3pXsxU3uTqGbLhfzMznecOCX/s640/shot_Apr_06_2012_0.png" width="384" /></a></div>
<br />
<br />
In order to search the directory an https connection to the IBM Intranet must be available. Lacking a VPN connection you will need to be on the Intranet WLAN to conduct any searches.<br />
<br />
Tapping the icon on the right hand side of the action bar will reveal the search field directly in the action bar. You can now enter the search term you want to search for.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO-RXAFoF8GMdkHonxj2zxj5idcSadRwtEuQ8Wezxs_Z72VoV9XHKuQW7-zj7C64IB-jN8zgkgBqvh-sQCiu2E3lMZ-KsGP9DuVyRJWbY3v9ZPA1AU7XSdSBQx62NlE_rBKOw06wuXgq4O/s1600/shot_Apr_06_2012_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiO-RXAFoF8GMdkHonxj2zxj5idcSadRwtEuQ8Wezxs_Z72VoV9XHKuQW7-zj7C64IB-jN8zgkgBqvh-sQCiu2E3lMZ-KsGP9DuVyRJWbY3v9ZPA1AU7XSdSBQx62NlE_rBKOw06wuXgq4O/s640/shot_Apr_06_2012_1.png" width="384" /></a></div>
<br />
<br />
When the search field is shown another icon is appears to the right of the search field. Tap it to choose the search category/type. A menu will open up to let you choose among different search types like searching for people's name, serial number, bluegroup by names and some more. Pick the search type from the menu and the search field will display a hint in grey on how the search value must be formatted.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOH0AbGIJXhp5tomG0TQ0xZimyyMmT3OoQISu40utsKJmA8_ZNREDxcsyMroWH7yH8tp83UQt8qo63BJKO9DillXFV9jiYVKSU7anKSFM4g9a5jmoPGZkOwk0XzW6DnjqrIgvx-ef-eLmb/s1600/shot_Apr_06_2012_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOH0AbGIJXhp5tomG0TQ0xZimyyMmT3OoQISu40utsKJmA8_ZNREDxcsyMroWH7yH8tp83UQt8qo63BJKO9DillXFV9jiYVKSU7anKSFM4g9a5jmoPGZkOwk0XzW6DnjqrIgvx-ef-eLmb/s640/shot_Apr_06_2012_3.png" width="384" /></a></div>
<br />
<br />
The keyboard will show a search button instead of the "Enter" key. Tap it to begin the search.<br />
<br />
When all is well and the directory server can be reached the search result will be displayed on a new screen. All matching persons or bluegroups (depending on what type of search was chosen) will be displayed in a list. <br />
<br />
<h2>
The People Search Result Screen</h2>
The action bar now shows the search type (e.g. Name) and the search term (e.g. Hofmann, Thomas) and the number of results in brackets.<br />
<br />
The list on this screen will include important information about the persons that match the search term, like name, email address, telephone number and job role. In addition, the bluepages person image is displayed on the left hand side.<br />
Person pictures are not loaded along with the search result. Depending on the preferences the application will either load some, all or none of the person pictures belonging to the persons in the result after the result is already displayed.<br />
<br />
Tap the "Load Pictures" icon in the action bar on the bottom of the search results screen to trigger the loading of missing pictures (tap and hold the action icons to see a tooltip about what they do). A progress bar at the top of the screen will report download progress.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbR_PfspsUR5M974dtw_dHrLt6oytvL3_xt961l-37TFrp2iWRj56HE8kMLIwfT8Hl-ubCyF3qR3VLrVmOyuML9fIVF3WChJ02Cmi7ucnkeg1Dt2R1n-7D9HUJnBW2IyVhgKMLQWiQkKHl/s1600/shot_Apr_06_2012_5_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbR_PfspsUR5M974dtw_dHrLt6oytvL3_xt961l-37TFrp2iWRj56HE8kMLIwfT8Hl-ubCyF3qR3VLrVmOyuML9fIVF3WChJ02Cmi7ucnkeg1Dt2R1n-7D9HUJnBW2IyVhgKMLQWiQkKHl/s640/shot_Apr_06_2012_5_pix.png" width="384" /></a></div>
<br />
<br />
Use the "Star" icon to "star" a search result. What this means is that you want to keep the search result's content for offline usage. Once starred, it will appear in the list on the home screen and can be recalled even without a connection to the directory server.<br />
<br />
Touching and holding an entry in the search result will open a context menu where you can choose different actions in the context of the entry like, dialing the phone number of the person, sending an email or conducting a search for e.g. the management chain of the person in that entry. The menu is rather long. Make sure you scroll down to see every possible action.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqrB-UkmqLo-KEEpMI28wrmnPGaSzykOMMc4tUi1iOqhHLG5Px_epeAV4hexO4fF_qc-kpRcftIy5YC8_oH5UufJtz7PP3cufx7yM3NkP7it2c63xx3Dor0Li5XbjoMLjZqGfRRNG1_XFm/s1600/shot_Apr_06_2012_8_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqrB-UkmqLo-KEEpMI28wrmnPGaSzykOMMc4tUi1iOqhHLG5Px_epeAV4hexO4fF_qc-kpRcftIy5YC8_oH5UufJtz7PP3cufx7yM3NkP7it2c63xx3Dor0Li5XbjoMLjZqGfRRNG1_XFm/s640/shot_Apr_06_2012_8_pix.png" width="384" /></a></div>
<br />
<br />
Tap the back key to return to the home screen. As an alternative you can also tap the application icon on the top left hand side of the action bar. This will always bring you back to the home screen no matter how deep inside the application you were navigating.<br />
<br />
Back on the home screen the introduction text is now replaced by the list of starred search results. Tap it to return to the list of persons that were included in the search result.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKgyS2MaEFLCFlRKq2WVU5tb5nbeh-M3Mpuyq-ygvABDLWn2E9JIo2Jwz-jh6eExmPdE_khsljJAJRGDh3cCsTRRUBe2qeFzAGDoNRrpHgwhs_dQOeIKxSyPJuAfZJOq9OFkuhFFwJb5yx/s1600/shot_Apr_06_2012_7_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKgyS2MaEFLCFlRKq2WVU5tb5nbeh-M3Mpuyq-ygvABDLWn2E9JIo2Jwz-jh6eExmPdE_khsljJAJRGDh3cCsTRRUBe2qeFzAGDoNRrpHgwhs_dQOeIKxSyPJuAfZJOq9OFkuhFFwJb5yx/s640/shot_Apr_06_2012_7_pix.png" width="384" /></a></div>
<br />
<br />
The context menu of an person entry in a search result also allows you to star persons. All persons starred will appear on the home screen under the "Starred Persons" list entry (which will always be the first entry). This way you can pick the ones you want to have available for offline usage and quick access.<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJPDMCtDxKUx3UJfsEFyatN3TmiCIU6LACJomD9Na-TVJ_7-Oeppg8AJF2LELiosclJ3_fPMrl29ioS_KhhumjFoW_5gTR_FF1kTflmECwRcLDlX2hv6DjiTORlDukxilEtcGp3V6kJkQx/s1600/shot_Apr_06_2012_9_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJPDMCtDxKUx3UJfsEFyatN3TmiCIU6LACJomD9Na-TVJ_7-Oeppg8AJF2LELiosclJ3_fPMrl29ioS_KhhumjFoW_5gTR_FF1kTflmECwRcLDlX2hv6DjiTORlDukxilEtcGp3V6kJkQx/s640/shot_Apr_06_2012_9_pix.png" width="384" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj20TOGdv48pRXBwSqnRWYV-qCLnr8eHpQpuySlYUuQs69Ete1IH8T2bEQfZkqliak_2uRrgSjtsCA41uV3awo1Ywyj7sm95MlHQjmVBBTti9iEFXHRgxnOKPRizQ8imiUamGptWbT4HCKI/s1600/shot_Apr_06_2012_10_pix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj20TOGdv48pRXBwSqnRWYV-qCLnr8eHpQpuySlYUuQs69Ete1IH8T2bEQfZkqliak_2uRrgSjtsCA41uV3awo1Ywyj7sm95MlHQjmVBBTti9iEFXHRgxnOKPRizQ8imiUamGptWbT4HCKI/s640/shot_Apr_06_2012_10_pix.png" width="384" /></a></div>
</div>Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-82854552838591732812010-04-16T23:05:00.018+02:002010-05-05T00:46:41.782+02:00Equinox Aspects to the Rescue - Patching non-intrusiveFor my current project I implemented synchronization between <a href="http://jazz.net/projects/rational-team-concert/">Rational Team Concert </a>(RTC) and Rational <a href="http://www-01.ibm.com/software/awdtools/clearquest/">Clear Quest </a>(CQ) using the <a href="http://jazz.net/projects/rational-team-concert/integrations/defect-tracking#rcq">Clear Quest Connector</a>.<br />
<br />
The development team uses RTC as its primary tool to work with defects and work item whereas the test team and the customer is using the CQ web client. <br />
<br />
Due to a <a href="http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/index.jsp?topic=/com.ibm.team.connector.cq.doc/topics/c_limitations.html">limitation in the CQ Connector</a> all comments in a defect made in RTC are synched to CQ with the user ID of the CQ Connector. As a consequence, the users of the CQ web client are unable to determine who has made a comment.<br />
<br />
I created an <a href="http://jazz.net/jazz/web/projects/Rational%20Team%20Concert#action=com.ibm.team.workitem.viewWorkItem&id=108544">enhancement request</a> which unfortunately won't be implemented. <br />
<br />
So I thought I should roll my own solution to this problem.<br />
<br />
Being a fan of AOP and AspectJ, I thought I could use <a href="http://www.eclipse.org/equinox/incubator/aspects/index.php">Equinox Aspects</a> to intercept the call where the comment is saved and simply prepend the username. In fact it was just as easy. I only needed to figure out how to use AspectJ with Equinox Aspects in my eclipse installation. The following two guides helped me:<br />
<br />
<ul><li><a href="http://www.eclipse.org/equinox/incubator/aspects/equinox-aspects-quick-start.php">The Quick-Start Guide</a></li>
<li><a href="http://martinlippert.blogspot.com/2009/04/jax-2009-whats-new-in-equinox-aspects.html">Martin Lippert's "What's new in Equinox Aspects" slides</a></li>
</ul>The first thing to do was find a place in the code where I could easily apply my change. I downloaded the <a href="http://jazz.net/wiki/bin/view/Main/RtcSdk20">RTC SDK</a> and loaded the bundles into a fresh workspace using the target definition supplied in the SDK so that I could browse and search the codebase using eclipse's features like determining call hierarchies etc.<br />
<br />
I was looking for a (central) place that would be sufficient to intercept for all comments added. I quickly found such a place in the code in the model class <code>com.ibm.team.workitem.common.internal.model.Comments</code>.<br />
<br />
The method I picked looks like this:<br />
<br />
<pre class="brush: java">public IComment createComment(IContributorHandle creator, XMLString content) {
Comment comment= ModelFactory.eINSTANCE.createComment();
Utils.initNew(comment);
comment.setCreator(creator);
comment.setContent(content.getXMLText());
return comment;
}
</pre><br />
Looking at the call hierarchy for this method reveals that it is called from various places in the Eclipse UI and also on the server side (the REST services).<br />
<br />
<img border="0" src="http://1.bp.blogspot.com/_ONLM1ngeo-s/S8jG7n3AFDI/AAAAAAAAAMk/_Ljdu1Mp8Hk/snap2227.modified.png" wt="true" /></img><br />
<br />
Everything I need for the modification is right there - the content of the comment and the creator. I created the following pointcut to match the code:<br />
<br />
<pre class="brush: java">public pointcut createCommentMethodToIntercept(IContributorHandle creator, XMLString content, IComments comments) :
execution(public IComment Comments.createComment(IContributorHandle, XMLString))
&& args(creator, content)
&& target(comments);
</pre><br />
I am extracting the creator, the content and the comments object here.<br />
<br />
The actual modification is done using an around advice:<br />
<br />
<pre class="brush: java">@SuppressWarnings("restriction")
IComment around(IContributorHandle creator, XMLString content, IComments comments) : createCommentMethodToIntercept(creator, content, comments) {
try {
Comments concreteComments = (Comments) comments;
if(shouldApply(getWorkItemType(concreteComments))) {
ContributorImpl contributorImpl = (ContributorImpl) creator.getFullState();
String userId = contributorImpl.getName();
String xmlText = content.getXMLText();
System.out.println("Original content: " + xmlText);
content = XMLString.createFromXMLText(userId + ": " + xmlText);
System.out.println("Replaced content: " + content.getXMLText());
}
} catch(ClassCastException e) {
e.printStackTrace();
}
return proceed(creator, content, comments);
}</pre><br />
Since the modification should only affect defects that are synched to ClearQuest I am calling the <code>getWorkItemType</code> method which makes use of the comments object:<br />
<br />
<pre class="brush: java">private String getWorkItemType(Comments comments) {
WorkItem workItem = comments.fWorkItem;
String workItemType = workItem.getWorkItemType();
System.out.println("WorkItem type: " + workItemType);
return workItemType;
}</pre><br />
The <code>shoulApply</code> method just checks a system property for a list of work item types that should be affected.<br />
<br />
The aspect is then applied to the bundle containing the <code>Comments</code> class which is <code>com.ibm.team.workitem.common</code> by specifying the following line in the manifest file of the plug-in containing the aspect:<br />
<br />
<code>Eclipse-SupplementBundle: com.ibm.team.workitem.common</code><br />
<br />
Basically, this is all that needs to be done.<br />
<br />
Now, when I add a comment to a defect that gets synched to ClearQuest my name is prepended to the comment:<br />
<br />
<img border="0" src="http://2.bp.blogspot.com/_ONLM1ngeo-s/S8jTMsJlZgI/AAAAAAAAAMs/eG36J3UOeIk/snap2225.png"/><br />
<br />
<br />
Of course, Equinox Aspects must be installed correctly and running the weaving hook for the aspect to be applied at runtime.<br />
<br />
On the server side RTC also employs OSGI and bundles through a servlet bridge. Unfortunately, I wasn't able to get the aspect applied there.Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-31281591335683538572009-03-07T00:26:00.003+01:002009-03-07T00:39:08.856+01:00Apache and Subversion and LDAP againI just noticed that Jeremy Whitlock updated his article on <a href="http://blogs.open.collab.net/svn/2009/03/subversion-with-apache-and-ldap-updated.html">Subversion with Apache and LDAP</a>. This might be interessting to you in case you came here for <a href="http://thofmann.blogspot.com/2008/09/installing-subversion-on-windows-server.html">my post on the same subject</a>.Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-56404432665357559952008-09-27T23:54:00.000+02:002008-09-27T23:54:41.273+02:00Installing Subversion on Windows Server with Apache HTTP Server, SSL, Active Directory Authentication and Log Rotation<h2>Necessary Software</h2>First you will need to get hold of the software itself.<br /><ul><li>Subversion can be downloaded at <a href="http://www.tigris.org/">tigris.org</a>. <a href="http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100">This</a> is the direct link to the installation packages. This guide assumes you download the Subversion binaries for Win32 as zip file and not as installer (at the time of writing the appropriate package is name <a href="http://subversion.tigris.org/files/documents/15/43455/svn-win32-1.5.2.zip">svn-win32-1.5.2.zip)</a></li><br /> <li>Since we want to use SSL for communication with the subversion repository we need a Win32 binary of Apache HTTP server that has SSL support compiled in. <a href="http://www.apachelounge.com/download/">This site</a> has them. At the time of writing the latest 2.2.x release available is <a href="http://www.apachelounge.com/download/binaries/httpd-2.2.9-win32-x86-ssl.zip">httpd-2.2.9-win32-x86-ssl.zip</a>. The <a href="http://www.microsoft.com/downloads/details.aspx?familyid=9b2da534-3e03-4391-8a4d-074b9f2bc1bf&displaylang=en">Microsoft Visual C++ 2008 Redistributable Package (x86)</a> is needed to run the Apache binaries.<br /> Note that for Apache 2.0.x you will need a <a href="http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91">different svn distribution</a> since the Subversion Apache modules need to be linked to a specific Apache version.<br /></li></ul><h2>Installation<br /></h2><h3>Subversion<br /></h3><p>Subversion is installed by simply unzipping the contents of the zip file (see above) to a directory of your choice. For this guide the directory <code>c:\Program Files (x86)\Subversion</code> is assumed to be the installation directory. A directory name that does not include the version number has the benefit that a new version of Subversion can easily be installed by overwriting the older version files.</p><p>In order to use the Subversion executables from the command line (like svnadmin for administering a repository) it is recommended to <strong>not </strong>add the <code>c:\Program Files (x86)\Subversion\bin</code> directory to the computer's PATH environment variable. The problem that might occur if you do so is that DDLs like ssleay32.dll and libeay32.dll that are part of the Subversion distribution might conflict with other versions of these libraries installed by other applications.<br /></p><h3>Apache HTTP Server<br /></h3><p>The Apache HTTP Server is installed by unzipping the contents of the zip file to a directory of your choice. For this guide the directory <code>c:\Program Files (x86)\Apache2</code> is assumed to be the installation directory. Again, upgrading can be done by replacing the files in that directory.<br /></p><h2>Configuration<br /></h2><h3>Apache HTTP Server<br /></h3><p>The official Apache HTTP Server documentation can be found <a href="http://httpd.apache.org/docs/2.2/">here</a> . The <a href="http://httpd.apache.org/docs/2.2/platform/windows.html">platform specific notes for Windows</a> are especially interesting for this guide.<br /></p><h4>Basic Setup</h4><p>Apache uses config files found in the <code>conf </code>subdirectory of the Apache installation. For the purpose of this guide only the <code>httpd.conf</code>, <code>http-ssl.conf</code> and <code>http-dav.conf</code> files are of interest.</p><p>The first thing to do is to adjust the path directives in <code>httpd.conf</code>. By default, all paths point to <code>c:/Apache2</code>. If you decided to install into this directory you can skip this step. Otherwise all paths directives to <code>c:/Apache2</code> need to be replaced with <code>c:/Program Files (x86)/Apache2</code> . Notice that the original config file uses forward slashes as path separators. Apache understands both forward slashes and backslashes but the rotatelogs program used for log rotation only recognizes the forward slashes as path separator so using them everywhere will yield a more uniform configuration file. There should be nine places (not including the comments) where the path needs to be changed.<br /><br />Next, the <code>Listen 80</code> directive should be changed to the port you want the server to listen to for non SSL requests. For this guide it is changed to 12080.<br /><br />The following directives should also be adjusted to make sense for your installation: <code>ServerAdmin</code> and <code>ServerName</code>.</p><h4>Integrating Subversion</h4><p>Subversion is integrated into the Apache HTTP Server using two modules that are part of the Subversion distribution. The two modules need to be enabled in the <code>httpd.conf</code> file. Add the<br />following two lines to the Dynamic Shared Object (DSO) Support section:<br /></p><pre>LoadModule dav_svn_module "c:/Program Files (x86)/Subversion/bin/mod_dav_svn.so"<br />LoadModule authz_svn_module "c:/Program Files (x86)/Subversion/bin/mod_authz_svn.so"<br /></pre>These two modules need the DAV module that comes with Apache HTTP Server. Uncomment the following line:<br /><pre>LoadModule dav_module modules/mod_dav.so</pre><a name="LocationDirective"> Next, an URL needs to be defines to access the repositories. This is done using the following <code>Location </code>directive:</a><br /><pre><br /><location><br />DAV svn<br />SVNListParentPath on<br />SVNParentPath "c:/svn"<br /></location><br /></pre><br />Details about the Subversion-specific directives can be found in the <a href="http://svnbook.red-bean.com/nightly/en/svn.serverconfig.httpd.html">subversion book</a> . <code>SVNParentPath</code> points to the location of the repositories to expose using the <code>/svn</code> URL.<br /><h4>Enabling SSL</h4><h5>Configure SSL Modules<br /></h5>Uncomment the following line in the Dynamic Shared Object (DSO) Support section:<br /><pre>LoadModule ssl_module modules/mod_ssl.so<br /></pre><h5>Generating a Self-Sign Certificate<br /></h5><p>A certificate is needed for SSL communication. It can either be a certificate issued by a trusted Certification Authority (CA) or one created yourself (a so called self-signed certificate). This guide assumes that a self-signed certificate is to be used and thus must first be generated.</p><p>The following command executed in the <code>bin</code> directory of the Apache HTTP server installation directory will generate a certificate named <code>server.crt</code> that is valid for ten years (3650 days).</p><pre>openssl req -new -x509 -nodes -days 3650 -out server.crt -keyout server.key -config ../conf/openssl.cnf</pre><p>When you run the command from a command window you will be prompted for details about the certificate like name of your organization, country and so on. This information will be stored into the certificate and later be available to the clients accessing the Apache HTTP Server using https/SSL. Make sure you specify the same name as CN (common name) when you are generating the keys as the one in ServerName in the config files. Otherwise, Apache will not start with SSL enabled.<br /></p><p>The process also generates a private key file called <code>server.key</code> which is the private key used to sign the certificate created. OS-level access rights should be used to restrict access to the file.</p><h5>Enabling SSL Mode</h5><p>All SSL relevant settings are stored in <code>httpd-ssl.conf</code>. This file needs to be adjusted next. Again, the path to the actual installation needs to be changed like the <code>httpd.conf</code> file before.<br /></p><p>The next thing to adjust is the <code>Listen 443 </code>directive. Change it to <code>Listen 12443</code> to use 12443 as the SSL port (of course you may use different values for your setup).</p><p>The SSL Virtual Host Context section needs to be adjusted to match your installation. Like you did before in <code>httpd.conf </code>change the ServerName and ServerAdmin directives. The default port number of 443 also needs to be changed to 12443 everywhere.</p><p>The certificate created before is referenced in <code>http-ssl.conf </code>using the directives <code>SSLCertificateFile</code> and <code>SSLCertificateKeyFile</code>. Change it to point to the files created before (move them from the <code>bin</code> to the <code>conf</code> directory before):<br /></p><pre>SSLCertificateFile conf/server.crt<br />SSLCertificateKeyFile conf/server.key<br /></pre><p>Now, uncomment the following line in <code>httpd.conf</code>:</p><p></p><pre>Include conf/extra/httpd-ssl.conf</pre><p></p><p>The last step is to tell Apache to use the SLL mode. This requires passing the -D SSL start parameter when starting the server. See how this is done <a href="http://www.blogger.com/post-create.g?blogID=893537939315741231#service">below</a>.</p><h3>Configuring Log File Rotation</h3><p>Log files should not grow infinitely. The rotatelogs.exe program that comes bundled with the Apache HTTP Server can be used to implement log rotation. The relevant directive are found in <code>httpd.conf </code>and <code>httpd-ssl.conf</code><code></code>.</p>Replace the following directives in <code>httpd.conf</code> : <pre>ErrorLog "logs/error.log"</pre>with this one <pre>ErrorLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/error_%Y-%m-%d-%H_%M_%S.log" 10M'</pre><pre><br />CustomLog "logs/access.log" common</pre> with this one <pre>CustomLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/access_%Y-%m-%d-%H_%M_%S.log" 10M' common</pre><br />Replace the following directives in <code>httpd-ssl.conf</code><code></code> in the SSL Virtual Host Context section:<br /><pre>ErrorLog "logs/error.log"</pre> with this one <pre>ErrorLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/ssl_error_%Y-%m-%d-%H_%M_%S.log" 10M'</pre><pre><br /><br />CustomLog logs/ssl_request_log \<br />"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"</pre>with this one <pre>CustomLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/ssl-request_%Y-%m-%d-%H_%M_%S.log" 10M' \<br />"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"</pre><pre><br />TransferLog logs/access_log</pre> with this one <pre>TransferLog '|"C:/Program Files (x86)/Apache2/bin/rotatelogs.exe" "C:/windows/system32/LogFiles/Apache2/ssl_access_%Y-%m-%d-%H_%M_%S.log" 10M'</pre><br /><h4>Authentication using Active Directory</h4><p>To use Active Directory for authentication when a request is made to access a Subversion repository it is necessary to add the following two lines to load the necessary modules:<br /></p><pre>LoadModule ldap_module modules/mod_ldap.so<br />LoadModule authnz_ldap_module modules/mod_authnz_ldap.so<br /></pre><br />The <code>Location</code> directive added <a href="http://www.blogger.com/post-create.g?blogID=893537939315741231#LocationDirective">above</a> needs to be augmented by the following lines:<br /><pre>Require valid-user<br />AuthType Basic<br />AuthBasicProvider ldap<br />AuthName "Subversion repositories"<br /><location>AuthLDAPBindDN CN=LDAP,CN=Users,DC=somedomain,DC=com<br />AuthLDAPBindPassword xxxx<br />AuthLDAPURL "ldap://server1.somedomain.com:389/cn=Users,dc=somedomain,dc=com?sAMAccountName?sub?(objectClass=*)"<br />AuthzSVNAccessFile c:/svn/svn-access.txt<br /></location></pre><br />so that it looks like this: <pre><br /><location><br />DAV svn<br />SVNListParentPath on<br />SVNParentPath c:/svn<br />Require valid-user<br />AuthType Basic<br />AuthBasicProvider ldap<br />AuthName "Subversion repositories"<br />AuthLDAPBindDN CN=LDAP,CN=Users,DC=somedomain,DC=com<br />AuthLDAPBindPassword xxxx<br />AuthLDAPURL "ldap://server1.somedomain.com:389/cn=Users,dc=somedomain,dc=com?sAMAccountName?sub?(objectClass=*)"<br />AuthzSVNAccessFile c:/svn/svn-access.txt<br /></location></pre><br />The LDAP user (AuthLDAPBindDN) and the associated password are necessary in case Active Directory is not configured <a href="http://support.microsoft.com/kb/326690">for anonymous queries</a>.<br /><br />For authorization via Active Directory take a look at <a href="http://pteropus.blogspot.com/2008/04/securing-subversion-via-ldap-followup.html">PTEROPUS' blog</a>.<br /><h4>WebDav for File Upload</h4><p>When there a lot of files to add to the repository it makes sense to do that directly on the server because it is much faster. For this the files to add need to be available on the server. One easy way to provide a file upload possibility for the users that want something imported into the repository by an admin on the server is to provide a WebDav share. Users can then connect to this share using clients that are built-into many operating systems (such as Web Folders on Windows).</p><p>To setup WebDav uncomment these two line in <code>httpd.conf</code>:</p><pre>LoadModule dav_fs_module modules/mod_dav_fs.so<br /><br />Include conf/extra/httpd-dav.conf<br /></pre> and add the following lines to file <code>httpd-dav.conf</code>:<br /><p></p><pre>Alias /svnupload c:/svnupload<br /><br /><location><br />DAV on<br />Options Indexes<br />AllowOverride None<br />Order deny,allow<br />Allow from all<br />Require valid-user<br />AuthType Basic<br />AuthBasicProvider ldap<br />AuthName "Subversion Repository Upload"<br />AuthLDAPBindDN CN=LDAP,CN=Users,DC=somedomain,DC=com<br />AuthLDAPBindPassword xxxx<br />AuthLDAPURL "ldap://server1.somedomain.com:389<br /></location><br /></pre>This will make the directory <code>c:\svnupload</code> accessible for uploading files using WebDav. The <code>httpd-dav.conf </code>file already contains a sample configuration called uploads which can be removed or commented out.<br /><p>After the config files have been adjusted it is time to setup the server to run as a service. First, the sytnax of the config files need to be checked by running the command from the Apache bin directory.:</p> <p>httpd.exe -t</p> <p>If there are errors reported Apache will not be able to start.</p><h4><a name="service">Running Apache as a Windows Service</a></h4>After the config files have been adjusted it is time to setup the server to run as a service. This can be acomplished by running the following command:<pre>httpd -k install -n "Apache for Subversion"</pre>See the <a href="http://httpd.apache.org/docs/2.2/platform/windows.html#winsvc">platform specific notes for Windows</a> for more options on installing the service.<br /><br />This will create a new key in the Windows registry under <code>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ApacheforSubversion</code>. A string value named <code>ImagePath</code> will contain the following value: <code>"C:\Program Files (x86)\Apache2\bin\httpd.exe" -k runservice</code><p>Use the registry editor to add <code>-D SSL</code> at the end of this value so that is will look like this: <code>"C:\Program Files (x86)\Apache2\bin\httpd.exe" -k runservice -D SSL</code> to enable the SSL mode.</p><h4>User Account Setup</h4>By default the Apache HTTPS Server runs under the Local System account when installed as a service. For security reasons it should run using a less privileged account. Use the Windows specific tools to create a new account for the server and the Services console to change the account under which the server is run.<br /><h3>Subversion</h3><p>Create a repository at the location pointed to before if non exists yet by running:</p><pre>svnadmin create c:\svn\test</pre><p> from the Subversion bin directory. This will create a repository test which you should now be able to access via <a href="http://localhost:12080/svn/test">http://localhost:12080/svn/test</a> and <a href="https://localhost:12443/svn/test">https://localhost:12443/svn/test</a><br /></p>Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com0tag:blogger.com,1999:blog-893537939315741231.post-55084610595833558272008-08-30T23:54:00.040+02:002010-05-05T22:38:50.245+02:00Fixing attachment download with Active Sync / Air Sync in Windows Mobile 6.1 and Exchange ServerI recently bought myself a new smartphone - an <a href="http://www.theunwired.net/?item=photoview-o2-xda-diamond-windows-mobile-professional-smartphone" target="_blank">XDA Diamond</a> - which runs Windows Mobile 6.1.<br />
<br />
After setting up Outlook Mobile to sync with an Exchange server I noticed that attachments in email would not download to the device. I searched google because I thought there must be many people having this problem but actually found only two of them.<br />
<br />
I thought that there must be a difference in the way Active Sync works between Windows Mobile 6.0 and 6.1 because syncing with my old MDA Varion III worked without any problems. So I analyzed the Active Sync protocol and found out that there actually is a difference regarding the way URLs are used.<br />
<br />
Let me show you what I mean.<br />
<br />
This is a request from my MDA Vario III as recorded in the HTTP server log:<br />
<br />
<pre><code>
2008-07-19 13:44:11 W3SVC1 EMELIE 192.168.0.100 POST
/Microsoft-Server-ActiveSync User=yyy&DeviceId=yyy
&DeviceType=PocketPC&Cmd=GetAttachment<span style="font-weight: bold;">&</span>
AttachmentName=Inbox<span style="font-weight: bold;">/</span>attachment%20name.EML
<span style="font-weight: bold;">/</span>1_multipart_xF8FF_2_NL_0808.gif&Log=V4TNASNC:0A0C0D0FS:
0A0C0D0SP:1C2I739S232418R0S0L0H0P 443 HOME\yyy
80.187.122.111 HTTP/1.1 MSFT-PPC/5.1.2200 - -
emelie.xxx.com <span style="font-weight: bold;">200</span> 0 0 176995 546 1312
</code></pre><br />
And this is the same request from my XDA Diamond:<br />
<pre><code>
2008-07-21 21:24:24 W3SVC1 EMELIE 192.168.0.100 POST
/Microsoft-Server-ActiveSync User=yyy&DeviceId=yyy
&DeviceType=PocketPC&Cmd=GetAttachment<span style="font-weight: bold;">&</span>
<span style="font-weight: bold;">amp;</span>AttachmentName=Inbox<span style="font-weight: bold;">%2F</span>attachment%20name.EML
<span style="font-weight: bold;">%2F</span>1_multipart_xF8FF_2_NL_0808.gif&Log=V4TNASNC:0A0C0D0FS:
0A0C0D0SP:3C2I739S1902R0S0L0H0P 443 HOME\yyy
91.89.177.111 HTTP/1.1 MSFT-PPC/5.2.1301 - -
emelie.xxx.com <span style="font-weight: bold;">500</span> 0 0 278 539 15
</code></pre><br />
The difference is that the request URL sent from the WM6.1 device contains URL-encoded URLs. The slash (/) is encoded as %2F. The log entry also shows that the HTTP status is 500 (Internal Server Error) for the URL-encoded request.<br />
<br />
So what's the problem? I don't see why it should not be legal to URL-encode the request like it is done in WM6.1. If it were Microsoft would probably not have implemented it this way:) and a lot of people would have the same problem<br />
So, I figured it must be something special on the IIS (the MS HTTP server) that causes this. This was confirmed by<a href="http://www.microsoft.com/communities/newsgroups/list/en-us/default.aspx?dg=microsoft.public.smartphone&tid=16807ac7-57c3-4e05-8e51-477f1604a00a&cat=&lang=&cr=&sloc=en-us&p=1" target="_blank"> this post</a>. It looked like the <a href="http://tomcat.apache.org/connectors-doc/reference/iis.html" target="_blank">Apache Jakarta ISAPI Redirector filter</a> (JK) would cause the problem. So I removed it from the Web site and suddenly I was able to download attachments with WM6.1!<br />
<br />
This was just a workaround of course since the ISAPI filter was installed on purpose. So I needed to find out what was causing the problem inside JK. The first thing I tried was looking for a newer version of JK. I downloaded version 1.2.26 but this did not improve things. After searching around a bit I dsicovered that there was a second Apache project called JK2 (version 2). I got that one and tried to configure it without any success. Later, I realized that the JK2 project is no longer being developed and JK (the first version) is still being developed. So using JK2 was not an option for me.<br />
<br />
Back to JK, I turned on tracing for the ISAPI filter and noticed the following entry in the trace file for the Active Sync request responsible for the downloading of the attachement:<br />
<br />
<pre><code>[Fri Aug 29 23:59:33.326 2008] [1004:4836] [debug]
jk_isapi_plugin.c (1199): Filter started
[Fri Aug 29 23:59:33.326 2008] [1004:4836] [emerg]
jk_isapi_plugin.c (1234): [/exchange/user@home.xxx.com
/Inbox/attachment name.EML/1_multipart_xF8FF_2_image.jpg]
<span style="font-weight: bold;">contains forbidden escape sequences</span>.
</code></pre>So it turned out that JK actually does not like the way the HTTP request is encoded.<br />
In order to fix this I downloaded the <a href="http://mirror.valtech.de/apache/tomcat/tomcat-connectors/jk/source/jk-1.2.26/tomcat-connectors-1.2.26-src.zip">source code for JK</a> and searched for the place where this message is written to the log. It is inside the callback method that is being called on the ISAPI filter for each request:<br />
<br />
<pre class="brush: c">DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,
DWORD dwNotificationType, LPVOID pvNotification)
{
...
rc = unescape_url(uri);
if (rc == BAD_REQUEST) {
jk_log(logger, JK_LOG_ERROR,
"[%s] contains one or more invalid escape sequences.",
uri);
write_error_response(pfc, "400 Bad Request",
HTML_ERROR_400);
return SF_STATUS_REQ_FINISHED;
}
else if (rc == BAD_PATH) {
jk_log(logger, JK_LOG_EMERG,
"[%s] <span style="font-weight: bold;">contains forbidden escape sequences.</span>",
uri);
write_error_response(pfc, "404 Not Found",
HTML_ERROR_404);
return SF_STATUS_REQ_FINISHED;
}
...
}
</pre><br />
<br />
As you can see this happens when the function unescape_url returns BAD_PATH which happens when the decoded character is a slash:<br />
<br />
<pre class="brush: c">static int unescape_url(char *url)
{
register int x, y, badesc, badpath;
badesc = 0;
badpath = 0;
for (x = 0, y = 0; url[y]; ++x, ++y) {
if (url[y] != '%')
url[x] = url[y];
else {
if (!isxdigit(url[y + 1]) || !isxdigit(url[y + 2])) {
badesc = 1;
url[x] = '%';
}
else {
url[x] = x2c(&url[y + 1]);
y += 2;
if (<span style="font-weight: bold;">url[x] == '/'</span> || url[x] == '\0')
<span style="font-weight: bold;">badpath = 1</span>;
}
}
}
url[x] = '\0';
if (badesc)
return BAD_REQUEST;
else <span style="font-weight: bold;">if (badpath)</span>
<span style="font-weight: bold;"> return BAD_PATH;</span>
else
return 0;
}
static char x2c(const char *what)
{
register char digit;
digit =
((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
digit *= 16;
digit +=
(what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
return (digit);
}
</pre><br />
<br />
What to do now? One possibility would be to change the code and rebuild JK from scratch. To do this I would need Visual C++ and probably some time to get it all worked out. Another way to fix this is to patch the JK DLL file so that HttpFilterProc does not enter the path of execution that causes the problem. This is exactly what I did.<br />
<br />
I disassembled the DDL and found the place to patch it:<br />
<br />
<pre>.text:6A6BAB0B loc_6A6BAB0B: ; CODE XREF: HttpFilterProc+22A j
.text:6A6BAB0B lea edx, [esp+39A8h+Str1]
.text:6A6BAB12 push edx
.text:6A6BAB13 call sub_6A6BB490
.text:6A6BAB18 add esp, 4
.text:6A6BAB1B cmp eax, 0FFFFFFFFh
.text:6A6BAB1E jnz short loc_6A6BAB47
.text:6A6BAB20 mov ecx, Time
.text:6A6BAB26 lea eax, [esp+39A8h+Str1]
.text:6A6BAB2D push eax
.text:6A6BAB2E push offset <span style="font-weight: bold;">aSContainsOneOrMoreInvalidEscapeSequences</span>_ ; "[%s] contains one or more invalid escap"...
.text:6A6BAB33 push 4
.text:6A6BAB35 push 0
.text:6A6BAB37 push 4CAh
.text:6A6BAB3C push offset aJk_isapi_plugin_c ; "jk_isapi_plugin.c"
.text:6A6BAB41 push ecx
.text:6A6BAB42 <span style="font-weight: bold;">jmp</span> loc_6A6BB000
.text:6A6BAB47 ; ---------------------------------------------------------------------------
.text:6A6BAB47
.text:6A6BAB47 loc_6A6BAB47: ; CODE XREF: HttpFilterProc+26E j
.text:6A6BAB47 cmp eax, 0FFFFFFFEh
.text:6A6BAB4A jmp short loc_6A6BAB97
.text:6A6BAB4C ; ---------------------------------------------------------------------------
.text:6A6BAB4C mov eax, Time
.text:6A6BAB51 lea edx, [esp+39A8h+Str1]
.text:6A6BAB58 push edx ; ArgList
.text:6A6BAB59 push offset aSContainsForbiddenEscapeSequences_ ; "[%s] contains forbidden escape sequence"...
.text:6A6BAB5E push 5 ; int
.text:6A6BAB60 push 0 ; Source
.text:6A6BAB62 push 4D2h ; int
.text:6A6BAB67 push offset aJk_isapi_plugin_c ; "jk_isapi_plugin.c"
.text:6A6BAB6C push eax ; Time
.text:6A6BAB6D call sub_6A6CEDF0
.text:6A6BAB72 push offset aDoctypeHtmlPublicW3cDtdHtml4_0TransitionalEn ; </pre>I replaced the original conditional jump instruction with an unconditional one (jmp) so that the problematic code is never executed.<br />
<br />
I hear that this is a problem for people who have a Citrix License Server installed on their IIS along with an Exchange (Front-End) server because Citrix uses Apache Tomcat and thus requires the functionallity of JK. Of course all other installation that use JK to redirect to an Apache Tomcat engine are affected.<br />
<br />
I will see if I find time to discuss this on the JK developers mailing list because I believe the best way to solve this is to change the source code and release a new version of JK. Of course, it needs to be discussed why the developers chose to reject request URls with encoded slashes inside first. Unfortunaltley, the code does not give any hints.<br />
<br />
So what can you do until this is hopefully fixed for real?<br />
<br />
Well, you could get the source code yourself, make the necessary change and rebuild the DLL or just patch it like I did.<br />
<br />
For the DLL version 1.2.26 (32bit) the offset to patch and the bytes to change are as follows:<br />
<pre><code>isapi_redirect.dll
0000AB4A: 75 EB
</code></pre>You can also download the patched version that I made <a href="http://thomashofmann.com/pub/isapi_redirect.dll">here</a>.<br />
<br />
Wow, this post got pretty long for the first one...<br />
<br />
Please leave a comment if you find this useful.<br />
<br />
References:<br />
Newgroup <a href="http://www.microsoft.com/communities/newsgroups/list/en-us/default.aspx?dg=microsoft.public.smartphone&tid=16807ac7-57c3-4e05-8e51-477f1604a00a&cat=&lang=&cr=&sloc=en-us&p=1" target="_blank">Discussion</a><br />
<a href="http://ppc-welt.info/community/showthread.php?t=133601" target="_blank">Thread </a>on German PPC Forum PPC-Welt<br />
<br />
Information in this post or the patched DLL must not be used commercially without mentioning this blog post.Thomas Hofmannhttp://www.blogger.com/profile/07204924500744120455noreply@blogger.com7