Wednesday, 25 November 2020

Smallest missing positive integer in an unsorted array

The question is to find the smallest missing positive integer, given an unsorted integer array. Consider the following table of arrays and their corresponding outputs for examples.

Input array Output value
1,2,3,3 4
3,4,-1,1 2
7,8,9,11,12 1
-1,-2 1

The simple solution is to sort the array and get the missing value. The challenge is to do it in O(n) and with constant space. Modifications to the array are allowed.

The complexity requirement indicates that we need an approach with multiple passes on the same array. Since modifications are allowed, we need a way of storing information within the array. The smallest output of the function is 1 and the largest output of the function will obviously be the length of the array + 1. So, we can see the possibility of some 1 to 1 mapping between the indices and the possible outputs of the function. The idea is if the value at an index can be modified to store information about whether the value (index + 1) is present in the array, we could use that information to reach the smallest index whose corresponding value , i.e. (index + 1), is not present in the array. That would be the answer we are looking for.

As seen from the input set, the numbers are signed and we know that sign bit is not part of the value of the number so we have to utilize that to store the availability information. As the availability information is boolean, the space required and available match.

Negative values in the array do not change the output at all and if all elements are negative we can just return 1 as the value we are looking for. So, we just need to focus on how we can use positive integers. So, we store information by marking the positive integers as negative to mark presence. We can map a positive integer to the index of its value - 1.

Let us consider 3 in the second array above. The presence of 3 will be marked by marking the value at index 2 (= 3 - 1) as negative. We have a problem here though. The value there is already negative. So, we need a way of knowing whether a value was already negative or we marked it so.

To overcome that problem, we modify the array a bit. We can move all negative values to the end of the array and store the index distinction. Now, while marking if we want to mark a value that is already negative, we don't care because that value does not contribute to the output anyway. From the remaining positive ones, whichever remains positive after this pass gives the indication of the result, i.e. the index of the value + 1.

public class SmallestMissingPositive {
public static void main(String[] args) {
System.out.println(leastPositive(new int[]{1,2,3,3}));
System.out.println(leastPositive(new int[]{3,4,-1,1}));
System.out.println(leastPositive(new int[]{7,8,9,11,12}));
System.out.println(leastPositive(new int[]{-1,-2}));
System.out.println(leastPositive(new int[]{1, -1,-2}));
}

private static int leastPositive(int[] nums){
int beginNegative = nums.length -1;

// sequence of negatives at the end
for(int i = nums.length -1; i >= 0; i--){
if(nums[i] < 0){
beginNegative = i;
}
else{
break;
}
}

// all negative values in nums
if(beginNegative == 0){
return 1;
}


// can transfer to beginNegative index now
beginNegative--;
for(int i = 0; i < nums.length; i++){
if(i == beginNegative){
break;
}
else{
if(nums[i] <0){
int temp = nums[i];
nums[i] = nums[beginNegative];
nums[beginNegative] = temp;

// swap

beginNegative--;
if(beginNegative ==i){
break;
}
// now we search for next index for beginNegative
for(int j = beginNegative; j > i; j--){
if(nums[j] < 0){
beginNegative = j;
}
else{
break;
}
}
}
}
}


for(int i = 0; i <= beginNegative; i++ ){
int idx = Math.abs(nums[i]) - 1;
if (idx >= nums.length){
continue;
}
if (nums[idx] > 0){
nums[idx] = nums[idx] * -1;
}
}
for (int i = 0; i <= beginNegative; i++) {
if (nums[i] > 0){
return i+1;
}
}
return beginNegative + 2;
}

}

Tuesday, 24 November 2020

Intersection of two sorted arrays

 In an interview, I was asked to find the intersection of two sorted arrays. My initial approach was to iterate over each element in first array and use binary search for it in the second array and handle the second array similarly. This solution has complexity of O(N * lg N). It also has one problem that when an element is common, it is found twice: when searched in second from iteration over first and vice versa. This can be easily avoided by searching in the output.

A simpler solution is to just use two pointers.

List<Integer> intersection(int[] a, int[] b){
    List<Integer> result = new ArrayList<>();
    
    int i = 0;
    int j = 0;
    for(; i< a.length &&  j < b.length;){
      if(a[i] == b[j]){
        result.add(a[i]);
        i++;
        j++;
      }else if (a[i] < b[j]){
        i++;       
       }else {
        j++;
       }
    }
    return result;
  }

This solution works in linear time. The solution can be further improved for the average case keeping the worst case complexity same.

Consider a large array as follows:

a = [10,11,12,13,14,15,16,17,...10000,1000000]

Now let's consider the case of finding intersection with a small array as follows:

b = [10000,1000000]

After we initialize the two pointers to the beginning of the arrays,  we keep moving in the first array for a long time. We can improve this by jumping to the next element higher or equal to the element being compared it. We could use binary search for that.

List<Integer> intersection(int[] a, int[] b){
    List<Integer> result = new ArrayList<>();
    
    int i = 0;
    int j = 0;
    for(; i< a.length &&  j < b.length;){
      if(a[i] == b[j]){
        result.add(a[i]);
        i++;
        j++;
      }else if (a[i] < b[j]){
        //i++;
        int low = i + 1;
        int high = a.length - 1;
        int mid = low + (high - low) / 2;
        while(low < high){
          if(a[mid] == b[j]){
            i = mid;
            break;
          }else if(a[mid] > b[j]){
            high = mid - 1;
          }else{
            low = mid + 1;
          }
          mid = low + (high - low) / 2;
        }
        if (low >= high){
          i = low;
        }
        
       }else {
        //j++;
        
        int low = j + 1;
        int high = b.length - 1;
        int mid = low + (high - low) / 2;
        while(low < high){
          if(b[mid] == a[i]){
            j = mid;
            break;
          }else if(b[mid] > a[i]){
            high = mid - 1;
          }else{
            low = mid + 1;
          }
          mid = low + (high - low) / 2;
        }
        if (low >= high){
          j = low;
        }
      }
    }
    return result;
  }

This improves the average case performance, especially for skewed inputs as mentioned earlier.

Saturday, 21 November 2020

Elasticsearch shard allocation failure

 Recently, I found a shard allocation failure in an Elasticsearch cluster. Checking the reason for the failure, I found the reason to be the following:

"Validation Failed: 1: this action would add [4] total shards, but this cluster currently has [4000]/[4000] maximum shards open;"

It is a bit unclear from the error message why the cluster considers 4000 as the maximum number of shards it should consider. Especially, given that on a different cluster I get 16000 as the maximum number of shards.

Looking at my cluster settings (including defaults), I found a property called "max_shards_per_node" and it was set to "1000". Since, I had 4 nodes, it added up perfectly.

Generally, this setting is put in so that the nodes have a proper balance between different types of work loads. However, we knew our scenarios of more shards was temporary so increasing the value to 1500 worked for the short term.

Monday, 14 September 2020

Gitk permissions on Alacritty

Recently, I started using Alacritty instead of the Terminal app provided by Apple. It sure is much faster.

There seems to be a new and annoying security feature introduced by Apple that has granular permissions for applications downloaded from the internet. So, Alacritty does not start with the same permissions as Terminal. Setting that up is a bit of a hassle. When I started gitk, it asked for some permission and I could not understand it then so I denied it. So, gitk did not work. I thought restarting Alacritty might request permissions again but that is not the case. The error I was getting is as follows:

 % gitk
Error in startup script: 58:102: execution error: Not authorised to send Apple events to System Events. (-1743)
    while executing
"exec osascript -e [format {
        tell application "System Events"
            set frontmost of processes whose unix id is %d to true
        end te..."
    invoked from within
"if {[tk windowingsystem] eq "aqua"} {
    exec osascript -e [format {
        tell application "System Events"
            set frontmost of processes ..."
    (file "/usr/local/bin/gitk" line 12261)

So, I had to find the right permission and grant that to get gitk working. The System Events permission is apparently under Automation section. Once that is granted, Alacritty can run gitk.

Monday, 20 January 2020

Learning Puppet III: Installing modules

In my previous post, we took a look at creating classes in Puppet and applying them locally. Let us look at installing Puppet modules next. Puppet does not install stdlib by default unlike other programming environments.

Common functionalities like reading a JSON file are part of stdlib module. It can often come in handy. Installing it is straightforward. However, after installation the modules are not loaded at run time.

Let us consider an example of reading a JSON file in a Puppet manifest. It can be an example of reading some config from an external resource.

Let us consider the module mod which we created in the previous post.

class mod::test_json{
      include mod

      $hash = loadjson('/tmp/load.json')
      [$message, $return_value] = $hash['output']
      notice("$message, $return_value")

}


To test, let us create a file at with the following content.

{
  "output": {
    "message": "The value is: ",
    "return_value": "0"
  }
}


To apply the manifest, we can try running the following:

puppet apply --modulepath=/path/to/parent/of/module -e "include mod::test_json"

This fails because loadjson is in stdlib. We need to enhance the module path to include stdlib as well. Modules are installed to the path /etc/puppetlabs/code/environments/production/modules. So, we need to modify our command as follows:

puppet apply --modulepath=/etc/puppetlabs/code/environments/production/modules:/vagrant/puppet -e "include elk::test_json"

Learning Puppet - II: Applying Classes

In my previous post about learning Puppet, I left a few open questions that I will attempt to answer as I continue learning.
  1. How can I apply Puppet classes?
  2. How can I install modules?
  3. How can I define functions in Puppet?
In the last post, I was unable to apply a class so I moved ahead without it. Let us look at that in this post.

To apply a class, I created a module, created its init.pp file and added a class in it.

Let us consider the module name to be mod. The init.pp will be located at mod/manifests/init.pp. I just keep it empty to begin with. The name of the class in that file has to match the module name though.

class mod{
}


Moving on to create a class of interest, let us add one to install Openjdk. The file would be mod/manifests/jdk.pp.

class mod::jdk () {
  notice("Installing openJDK")
  # Install java
  package { 'openjdk':
    ensure => installed,
    name => 'java-1.8.0-openjdk-headless',
    alias => 'openjdk',
  }
}


To apply the manifest, the command would be as follows:

puppet apply --modulepath=/path/to/parent/of/module -e "include mod::jdk"

Wednesday, 18 December 2019

jruby-launcher upgrade

Logstash uses JRuby. So, while trying to read the code regarding an issue I was having in Logstash, I had installed JRuby and I had set up multiple Ruby versions using rbenv.

To check whether the right version of Ruby is being used, I tried ruby -v. I was getting an error as follows.

2019-12-17T19:44:52.167+05:30 [main] WARN FilenoUtil : Native subprocess control requires open access to sun.nio.ch
Pass '--add-opens java.base/sun.nio.ch=org.jruby.dist' or '=org.jruby.core' to enable.
java.lang.IllegalCallerException: sun.nio.ch is not open to module org.jruby.dist
at java.base/java.lang.Module.addOpens(Module.java:763)
at org.jruby.dist/com.headius.backport9.modules.impl.Module9.addOpens(Module9.java:28)
at org.jruby.dist/com.headius.backport9.modules.Modules.addOpens(Modules.java:22)
at org.jruby.dist/org.jruby.util.io.FilenoUtil$ReflectiveAccess.(FilenoUtil.java:140)
at org.jruby.dist/org.jruby.util.io.FilenoUtil.getFilenoUsingReflection(FilenoUtil.java:111)
at org.jruby.dist/org.jruby.util.io.FilenoUtil.filenoFrom(FilenoUtil.java:107)
at org.jruby.dist/org.jruby.util.io.ChannelFD.initFileno(ChannelFD.java:42)
at org.jruby.dist/org.jruby.util.io.ChannelFD.(ChannelFD.java:32)
at org.jruby.dist/org.jruby.RubyIO.(RubyIO.java:174)
at org.jruby.dist/org.jruby.RubyFile.(RubyFile.java:281)
at org.jruby.dist/org.jruby.parser.Parser.parse(Parser.java:99)
at org.jruby.dist/org.jruby.Ruby.parseFileAndGetAST(Ruby.java:2709)
at org.jruby.dist/org.jruby.Ruby.parseFileFromMainAndGetAST(Ruby.java:2702)
at org.jruby.dist/org.jruby.Ruby.parseFileFromMain(Ruby.java:2686)
at org.jruby.dist/org.jruby.Ruby.parseFromMain(Ruby.java:642)
at org.jruby.dist/org.jruby.Ruby.runFromMain(Ruby.java:588)
at org.jruby.dist/org.jruby.Main.doRunFromMain(Main.java:415)
at org.jruby.dist/org.jruby.Main.internalRun(Main.java:307)
at org.jruby.dist/org.jruby.Main.run(Main.java:234)
at org.jruby.dist/org.jruby.Main.main(Main.java:206)
jruby 9.2.7.0 (2.5.3) 2019-04-09 8a269e3 OpenJDK 64-Bit Server VM 11.0.5+10 on 11.0.5+10 +jit [darwin-x86_64]

It didn't stop the printing of the version though.

A bit of googling pointed to the discussion at JRuby Github repo which further pointed to another discussion in jruby-launcher repo. I was not aware of the reason of the existence of jruby-launcher but it is an interesting take on a difficult situation.

From the discussion, it seemed that the fix was available in version 1.1.10. So, I tried looking at my Gemfile.lock to check the version I am using; but there was no version mentioned in that. However, it was present in the list of installed gems. I added the fixed version in the Gemfile and the issue was fixed.

Wednesday, 28 August 2019

Setting up jEnv

With OpenJDK, there is a need to have multiple JDK versions installed as there are many releases that are used by different projects. More and more projects are supporting multiple Java versions as well. This problem was not as prevalent in the Java ecosystem but the Ruby ecosystem had it long back. There are multiple tools to manage Ruby versions. The approach of rbenv is probably the simplest. On the same lines, we have jEnv for Java.

To setup jEnv, the steps are simple

  1. Install jEnv (brew install jenv)
  2. Setup shell profile (~/.bash_profile)
  3. export PATH="$HOME/.jenv/bin:$PATH"
    eval "$(jenv init -)"

  4. Install Java
  5. Set local or shell version of Java (jenv local openjdk64-1.8.0.192)

Thursday, 22 August 2019

Jacoco instrumentation error

In a Java project build, I was getting an error complaining about Jacoco failing to instrument a particular class. Looking at the complete stack trace of the error, it was an exception about index going out of bounds.

Caused by: java.lang.ArrayIndexOutOfBoundsException: 6
at org.jacoco.core.internal.BytecodeVersion.get(BytecodeVersion.java:41)
at org.jacoco.core.instr.Instrumenter.instrument(Instrumenter.java:87)
at org.jacoco.core.instr.Instrumenter.instrument(Instrumenter.java:123)
... 25 more
Fortunately, a little bit of search showed that it is related to Java. My initial version of Java was '1.8.0.121'. The fix was backported to '1.8.0.172'. I also had '1.8.0.192' installed. So, I switched versions and tried a clean build. It worked fine.

Tuesday, 16 April 2019

Learning Puppet: Idempotence

I am learning Puppet so I wanted to try out the manifests on a local VM. So, I created a Vagrant based Arch linux VM.

Vagrant.configure("2") do |config|
 config.vm.box = "archlinux/archlinux"
 config.vm.hostname = "learn.box"
 config.vm.synced_folder "puppet/", "/home/vagrant/puppet"
end


When I tried to bring the VM up, I got the following error.

Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel module.
Please verify that these guest additions are properly installed in the
guest. This is not a bug in Vagrant and is usually caused by a faulty
Vagrant box. For context, the command attempted was:

mount -t vboxsf -o uid=1000,gid=1000 vagrant /vagrant

The error output from the command was:

: Invalid argument


It did not stop the VM from coming up so I was able to test. I created the following manifest to start with and it worked fine.

file {
 '/tmp/motd': content => 'hello puppet'
}


I moved on to actually installing a package.

class logstash{
  package{
    "logstash":
      name => "logstash",
      alias => "logstash",
      ensure => "7.0"
  }

}

I tried to apply it using the following:

puppet apply logstash.pp -v

The package was not installed. I soon figured out it was because, I was not applying the class. So, I modified the manifest to the following:

  package{
    "logstash":
      name => "logstash",
      alias => "logstash",
      ensure => "7.0"
  }


Now Puppet tried to install the package but it failed with the following error.

Error: Parameter ensure failed on Package[logstash]: Provider pacman must have features 'versionable' to set 'ensure' to '7.0' (file: /home/vagrant/logstash.pp, line: 2)

Arch linux uses pacman as its package manager and Puppet was trying to install a specific version of the package using that. However, pacman does not support different versions of the same package. So, Puppet can't ensure a specific version. So, I changed the manifest to the following:

  package{
    "logstash":
      name => "logstash",
      alias => "logstash",
      ensure => "installed"
  }

Now, Puppet attempted to install the package again and hit the following error.

Error: Execution of '/usr/bin/pacman --noconfirm --needed --noprogressbar -Sy logstash' returned 1: error: you cannot perform this operation unless you are root.
Error: /Stage[main]/Main/Package[logstash]/ensure: change from 'absent' to 'present' failed: Execution of '/usr/bin/pacman --noconfirm --needed --noprogressbar -Sy logstash' returned 1: error: you cannot perform this operation unless you are root.


So, I attempted using sudo. The process was taking time. I waited for some time then interrupted it and tried to see the logs. The logs seemed fine so I started again and the this time I got the following error.

Error: Execution of '/usr/bin/pacman --noconfirm --needed --noprogressbar -Sy logstash' returned 1: :: Synchronizing package databases...
error: failed to update core (unable to lock database)
error: failed to update extra (unable to lock database)
error: failed to update community (unable to lock database)
error: failed to synchronize all databases
Error: /Stage[main]/Main/Package[logstash]/ensure: change from 'absent' to 'present' failed: Execution of '/usr/bin/pacman --noconfirm --needed --noprogressbar -Sy logstash' returned 1: :: Synchronizing package databases...
error: failed to update core (unable to lock database)
error: failed to update extra (unable to lock database)
error: failed to update community (unable to lock database)
error: failed to synchronize all databases


This is specific to pacman. As I had interrupted the execution, the lock file was not deleted, i.e. pacman had not cleaned up properly. I manually removed the lock file from /var/lib/pacman/db.lck and tried to apply the manifest again. It completed successfully and logstash was installed on the VM. Now, it is established that the script works but it might need manual intervention. In other words, the manifest needs to be idempotent so that manual intervention is minimal.

To achieve that, I modified the manifest as follows.

  package{
    "logstash":
      name => "logstash",
      alias => "logstash",
      ensure => "installed"
  }
  file{
    "/var/lib/pacman/db.lck":
      path => "/var/lib/pacman/db.lck",
      name => "/var/lib/pacman/db.lck",
      ensure => "absent"
  }

To test that the manifest is idempotent, I removed logstash and applied the manifest. Midway, I interrupted the execution and re-applied the manifest. This time re-application succeeded. The logs show that the lock file was created when the manifest was applied the previous time and interrupted. It was deleted when the manifest was applied again.

Info: Applying configuration version '1555432879'
Info: Computing checksum on file /var/lib/pacman/db.lck
Info: /Stage[main]/Main/File[/var/lib/pacman/db.lck]: Filebucketed /var/lib/pacman/db.lck to puppet with sum d41d8cd98f00b204e9800998ecf8427e
Notice: /Stage[main]/Main/File[/var/lib/pacman/db.lck]/ensure: removed
Notice: Applied catalog in 0.25 seconds


Key take-aways:
  1. Puppet is a framework that depends on providers to install packages and its capabilities are as good as the providers.
  2. Without idempotent behaviour, Puppet manifests will not be achieve much of automation.

Monday, 21 January 2019

Finding where a package is installed

Recently, I faced an issue on a CentOS box where I had to find the location of a package. My application was running on it and I wanted to modify its config. I was unable to pick the config in /etc so I figured it must be in the folder in which the application is installed.

The application was installed as a daemon but it seems the PATH was not updated. I searched in the usual locations of binaries and packages but I could not find it. The only option left for me was to find out where it was installed.

Using the following command showed all the files of the application package.

rpm -ql <my application>

From the list, I could see where my config file was placed during install.

Friday, 18 January 2019

Unexpected exit of Elasticsearch container

I was trying the Elasticsearch container image on docker hub. I was disappointed by the complete lack of error message in the following scenario.

I had my setup defined in a compose file. When I tryed to start the container, I got only the following log line.

OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a future release.

As it is a warning, I expected the container to start but it had not started. Digging a bit more, I found that the exit code was 137 which meant the container needed more memory than the docker daemon was configured for. The fix is quite easy of course but I think it could have been communicated better.

Friday, 5 October 2018

Correctly restarting syslog on Mac OS X

For some experimentation, I was recently looking at restarting syslog daemon. I expected it to be as simple as Linux system daemons. The init system of Mac appeared to be more like systemd in my initial searches. A few blogs instructed unload followed by load operation which sounded strange to me.

In systemd parlance, I expected unload operation to disable the daemon from starting up during initial booting. If you try to follow the process suggested in these blogs, you end up with an error stating "Operation not permitted while System Integrity Protection is engaged". This leaves two options:

  1. Disable System Integrity Protection
  2. Restart the system for every change in config
Of course, the answer is far too simple and none of these complexities need to handled. The right way of restarting the syslog daemon is as follows (using stop and start operations instead of unload and load):

sudo launchctl stop /System/Library/LaunchDaemons/com.apple.syslogd.plist
sudo launchctl start /System/Library/LaunchDaemons/com.apple.syslogd.plist

Tuesday, 25 September 2018

Hello OSGi

I found existing guides for OSGi to be a bit outdated. So, it is probably better to document the basic setup.

The plan is to first create a consumable bundle, i.e. a service bundle followed by a consumer bundle that uses the former.

I am also using maven for creating the projects so that the maven toolchain can be used for proper packaging.

For the consumable bundle, we start with defining an interface that exposes functionality of the bundle.

package main.java.interfaces;

public interface TestService {
    void hello();
}


We then implement it.

package main.java.impls;



import main.java.interfaces.TestService;



public class TestServiceImpl implements TestService {

    @Override

    public void hello() {

        System.out.println("Hello Duniya");
    }
}

We then add the activation logic for the bundle.

package main.java.provider;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

import main.java.interfaces.TestService;
import main.java.impls.TestServiceImpl;

public class ServiceActivator implements BundleActivator {
    private ServiceRegistration registration;

    @Override
    public void start(BundleContext bundleContext) throws Exception {
        registration = bundleContext.registerService(
                TestService.class.getName(),
                new TestServiceImpl(),
                null);
        System.out.println("ServiceActivator started");
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        registration.unregister();
    }
}



We complete this bundle by adding a POM for building it.




<?xml version="1.0" encoding="UTF-8"?>
<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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>felix_test</groupId>
    <artifactId>felix_test</artifactId>
    <version>1.0</version>
    <packaging>bundle</packaging>

    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>felix_test</Bundle-SymbolicName>
                        <Export-Package>main.java.interfaces</Export-Package>
                        <Bundle-Activator>main.java.provider.ServiceActivator</Bundle-Activator>
                        <Bundle-Vendor>Amitav Mohanty</Bundle-Vendor>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


The package of interfaces that are to be exposed are called out along with the activator so that the maven toolchain can build our bundle properly.

We move on to create the consumer bundle next. We start with the bundle activator.

package main.java.consumer;

import main.java.interfaces.TestService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class FelixTestActivator implements BundleActivator {
    @Override
    public void start(BundleContext bundleContext) throws Exception {
        ServiceReference reference = bundleContext.getServiceReference(TestService.class.getName());
        consumer = new TestConsumer((TestService)bundleContext.getService(reference));
        consumer.startTimer();
    }

    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        consumer.stopTimer();
    }

    private TestConsumer consumer;
}

We then add the consumer.


package main.java.consumer;

import main.java.interfaces.TestService;

import javax.swing.Timer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class TestConsumer implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        service.hello();
    }

    public TestConsumer(TestService service){
        super();
        this.service = service;
        timer = new Timer(1000, this);
    }

    public void startTimer(){
        timer.start();
    }

    public void stopTimer(){
        timer.stop();
    }

    private final TestService service;
    private final Timer timer;

}


The initiation of the consumer happens when the bundle is started.

The POM for building the consumer bundle is as follows:

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>consumer</groupId>
    <artifactId>consumer</artifactId>
    <version>1.0</version>
    <packaging>bundle</packaging>

    <dependencies>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
        </dependency>

        <dependency>
            <groupId>felix_test</groupId>
            <artifactId>felix_test</artifactId>
            <version>1.0</version>
            <scope>system</scope>
            <systemPath>${basedir}/../target/felix_test-1.0.jar</systemPath>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.0.2</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <extensions>true</extensions>
                <configuration>
                    <instructions>
                        <Bundle-SymbolicName>consumer</Bundle-SymbolicName>
                        <Bundle-Activator>main.java.consumer.FelixTestActivator</Bundle-Activator>
                        <Bundle-Vendor>Amitav Mohanty</Bundle-Vendor>
                    </instructions>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

The dependency on the other bundle is called out.

The "packaging" node in the POMs is essential for creating OSGi bundles.

Wednesday, 9 May 2018

Backward compatibility in user experience

Gmail recently launched a new design. I tried it out of curiousity. The first thing I noticed was that my muscle memory started failing me. Every time a new design comes, people focus on the look and feel of the design, its intuitiveness and other such factors but its compatibility with old design is not taken into account ever.

Power users tend to build muscle memory even for UIs. I tend to move my mouse to a particular location on the screen for the delete button for example.

With a product that has so many existing users, I think GMail should pay attention to these details as well when planning a new design. So, should other companies with similar products. An epic failure in the same lines is removal of start button by Microsoft. Of course a button position is not as critical; but the point is to have this criterion included in the design process.

Wednesday, 31 January 2018

Testing filebeat

For testing codecs like multiline, the recommendation is to try the Go playground website. However, before making a config live, it can be tested locally also; using input from stdin and printing output to the console. The following config can be used as skeleton.

filebeat.prospectors:
- input_type: stdin

output:
  console:
    pretty: true

Friday, 12 January 2018

Ola does not support Amex

As per their FAQs, Ola claims to support Amex cards.



However, when an attempt is made to add an Amex card, an error is shown as following.



The explanation provided by Ola is as follows.



I don't understand why is this clarity is not provided in the FAQ.

Tuesday, 2 January 2018

KActivityManager blocking unmounting

While un-mounting a partition to safely remove the external hard drive, I got an error about the device being busy. Both Dolphin and the command line said the same thing.

umount /run/media/myuser/New\ Volume/
umount: /run/media/myuser/New Volume: target is busy.


It seems kactivitymanagerd was using it.


[root@clown myuser]# fuser -vm /run/media/myuser/New\ Volume/
                    USER        PID ACCESS COMMAND
/run/media/myuser/New Volume:
                     myuser     612 ..c.. kactivitymanage


I tried to stop the daemon but I got the following error.

root@clown myuser]# /usr/bin/kactivitymanagerd
QStandardPaths: wrong ownership on runtime directory /run/user/1000, 1000 instead of 0


After some discussion on Freenode, I got to know that when graphical apps are run with escalated priviledges, similar error is found. I guessed I was getting the error because I was trying to stop the daemon as root user. I tried changing back to my regular user and stopping the daemon. It worked  and I was able to unmount the drive after that.

/usr/bin/kactivitymanagerd stop
Service stopped
Cleaning up...


Tuesday, 5 December 2017

Benchmarking elasticsearch and logstash pipeline

Elasticsearch and logstash pipelines can be elaborate or simple. Depending upon the setup, end to end benchmarking should be done time to time. One way to do it is to have a marker document (log line). We need to track when the marker is introduced into the pipeline and finally when it becomes available to query.

import urllib2

import json

from datetime import datetime

import sys

import time

print "Started at " + str(datetime.now())
if len(sys.argv) < 2:
   print "URL not specified.\nUsage: watch.py "
   exit(1)

count = 0
while count < 1:
    resp = urllib2.urlopen(sys.argv[1]).read()
    count = json.loads(resp)["hits"]["total"]
    if count > 0:
        print "Found at " + str(datetime.now())
        break
    time.sleep(2)


The above script can be used as follows:

python <script_file> "http://<host_name>:<port_number>/_search?q=message:<markerMessage>"

Thursday, 16 November 2017

Toggle Read-only behaviour of buffer in Emacs

When a file to which you have read access as regular user is opened in emacs, the buffer is marked read-only. If the user escalates to root and opens the same file in emacs, the buffer still remains read-only. To modify the file, the user needs to toggle the read-only behaviour of the buffer (for which the default key binding is C-x C-q) first.