03 October 2018

Converting Weak Listener to Rx Observable

Andrea Maglie has a nice writeup on how to nicely convert classic observable with listeners to Rx observable. Here is the code snippet:

public Observable<String> observableListenerWrapper() {
    return Observable.create(new Observable.OnSubscribe<String>() {

        @Override
        public void call(Subscriber<? super String> subscriber) {
            ValueUpdateListener listener = new ValueUpdateListener() {

                @Override
                public void onValueChanged(@NonNull String value) {
                    if (subscriber.isUnsubscribed()) {
                        valueHolder.unregisterListener(this);
                    } else {
                        subscriber.onNext(value);
                    }
                }
            };

            valueHolder.registerListener(listener);
        }
    });
}

where ValueHolder holds a collection on weak references to listeners:

class ValueHolder {
  private final List<WeakReference<ValueUpdateListener>> fObservers = new CopyOnWriteArrayList<>();
  
  private void notifyListeners(String newValue) {
    for (WeakReference<ValueUpdateListener> ref : fObservers) {        
      final var listener = ref.get();
      if (listener != null ) {
        listener.onValueChanged(newValue);
      }
      else {
        fObservers.remove(ref)        
      }
    }
  }
 
  public void addListener(ValueUpdateListener listener) {
    fObservers.add(new WeakReference<ValueUpdateListener>(listener));
  }
}

After the above code is converted to Kotlin and RxJava 2 we have the following

object Consumer {
    private var mDisposable: Disposable? = null

    fun initWith(valueHolder: ValueHolder) {
        mDisposable = Observable.create<String> { emitter ->
            valueHolder.registerListener(object : ValueUpdateListener {

                override fun onValueChanged(value: String) {
                    if (emitter.isDisposed) {
                        valueHolder.unregisterListener(this)
                        return
                    }

                    emitter.onNext(value);
                }
            })
        }
        .subscribe(
            { println(it) },
            { it.printStackTrace() }
        )
    }
}

which… does not work.

Apparently, the object implementing listener gets garbage collected. Consequently, when ValueHolder goes thru the list of listeners it notices that weak reference points to nothing and removes it from listener collection. First attempt to fix this was to extract listener object to a variable in hope it would be captured in emitter lambda.

object Consumer {
    private var mDisposable: Disposable? = null

    fun initWith(valueHolder: ValueHolder) {
        mDisposable = Observable.create<String> { emitter ->
            val listener = object : ValueUpdateListener {

                override fun onValueChanged(value: String) {
                    if (emitter.isDisposed) {
                        valueHolder.unregisterListener(this)
                        return
                    }

                    emitter.onNext(value);
                }
            }

            valueHolder.registerListener(listener)
        }
        .subscribe(
            { println(it) },
            { it.printStackTrace() }
        )
    }
}

That did not help. Somehow, the lambda was not capturing the reference to listener. Moving listener object all the way out to singleton corrected the issue, as it survived garbage collection and Rx observable continued to work as expected.

object Consumer {
    private var mDisposable: Disposable? = null
    private lateinit var mListener: ValueUpdateListener

    fun initWith(valueHolder: ValueHolder) {
        mDisposable = Observable.create<String> { emitter ->
            mListener = object : ValueUpdateListener {

                override fun onValueChanged(value: String) {
                    if (emitter.isDisposed) {
                        valueHolder.unregisterListener(this)
                        return
                    }

                    emitter.onNext(value);
                }
            }

            valueHolder.registerListener(mListener)
        }
        .subscribe(
             { println(it) },
             { it.printStackTrace() }
        )
    }
}

Conclusion

Be cautious when mixing lambdas, Rx and weak references, as the end result can be unexpected.

Updated on 2018-12-23

Added implementation of ValueHolder.

30 August 2018

Step by Step Guide to Static Website Hosting on Firebase

Prerequisites: folder with content you want to host (HTML files, images etc.) e.g. d:\mysite

Host your site on Firebase server

  1. prepare the content for hosting by moving all files to subfolder public
    1. create subfolder public
    2. move all files to the subfolder
  2. if you don’t have a Google account (does not have to be tied to Gmail address - click on ‘Use my current email address instead’) sign up for one
  3. sign up for Firebase here
  4. install npm
  5. open command prompt
    1. e.g. press windows key
    2. type ‘command’
    3. press Enter
  6. install Firebase tools
    1. in command prompt execute npm install -g firebase-tools
  7. connect your machine to Firebase
    1. execute firebase login
  8. open command prompt in the folder with your website e.g. d:\mysite
    1. one way of accomplishing this would be to navigate to folder in Windows Explorer and type cmd in the address bar
  9. run firebase init command
    1. answer Yes by pressing Enter
    2. select Hosting
      1. moving down with arrow keys
      2. press space
      3. press Enter
    3. select [create a new project]
      1. press Enter
      2. when asked about public directory, just press Enter
      3. when asked about single-page app, just press Enter as you probably have an ordinary site
      4. when (if) asked to overwrite index.html, just press Enter as you do not want to overwrite your content
  10. now go to Firebase console to create a ‘project’ for your site
    1. click on Add project
    2. enter a name for your project
      1. take note of project id
    3. check both checkboxes, or uncheck both to enable Create project button
    4. click the Create project button
  11. now go back to console window
    1. type firebase use --add
    2. your newly created project should be the only item in the list
    3. press Enter
    4. when asked about alias, type in production
    5. press Enter
  12. enter firebase deploy and wait a few seconds
    1. you are notified that you site is available at https://mysite-project-id.firebaseapp.com

Now, whenever you make modifications to your web site

  1. open command prompt in d:\mysite
  2. execute firebase deploy

Connect your domain name to the web site

If you have a domain name, there are a few additional steps that need to be done.

  1. go to Firebase
  2. click on your project
  3. click on hosting on left-hand side
  4. click on ‘Connect domain’ button
  5. enter domain name in the appropriate edit box
  6. click on ‘Continue’

Here it gets a bit tricky. Firebase will provide a string of text that needs to be set in a new TXT DNS record for your domain.

  1. go to DNS control panel for your domain, or otherwise set the TXT record
  2. click on ‘Verify’ in Firebase console
  3. if not successful, appropriate message will be displayed
    1. try waiting a couple of minutes as these settings need time to propagate
  4. when you succeed in verifying domain ownership you’ll need to add two more DNS records for your domain
  5. again, this might take some time to start working

Error messages in Firebase console regarding this part of procedure are a bit vague and sometimes plainly misleading. So don’t give up if you don’t succeed the first time. Verify that records in the DNS control panel are same as Firebase needs them to be. Verify that you do not have any extra CNAME or A records left over from before that might interfere with new config. Try everything once more. Try to delete connection to domain and then try again. Try to use Advanced setup for connecting the domain instead of Basic setup.

Conclusion

With this, you get your own web site with your own domain with support for HTTPS which is more important every day since browsers tend to mark plain HTTP sites as ‘insecure’.

25 July 2018

Build C# Project Without Visual Studio

I was recently at a computer without Visual Studio installed and was in a need of a build of a desktop C# application. Source was available on GitHub so I thought maybe since .NET comes with Windows, just maybe everything needed for successful build of a .sln is already there. Running

msbuild MySolution.sln

produced an error about missing NuGet dependencies. After downloading nuget.exe and trying again, msbuild complained about resources and missing resgen tool. Resgen regularly comes only with Visual Studio… and not installing it was the idea from the start. At this moment a thought about trying to build it online crossed my mind. After some research, Appveyor seemed the right service for this job. Signing up went fine, and connecting the GitHub project was two clicks work. Then, I started the build, everything was fine, only the output exe file was nowhere to be found.

The fix was to add an artifact with bin\debug folder.

Appveyor artifacts settings

After that, contents of output folder are available for download as zip archive.