Tuesday, December 31, 2013

Use p4merge for git diff and merge on mac (update based on p4merge for mac 2018)

(update based on mac P4Merge 2018)

Assuming p4merge is already installed on mac in the default /Application folder.

1. create a sh script file called mymerge.sh with below content in /usr/local/bin folder
#!/bin/sh 
/Application/p4merge.app/Contents/Resources/launchp4merge $*


2. create a sh script file: /usr/local/bin/extMerge with the following content
#!/bin/sh mymerge.sh $*

3.create another script file: /usr/local/bin/extDiff with the following content
#!/bin/sh
[ $# -eq 7 ] && /usr/local/bin/extMerge "$2" "$5"
4. set the execute permission for the above files
$ sudo chmod +x /usr/local/bin/mymerge.sh
$ sudo chmod +x /usr/local/bin/extMerge $ sudo chmod +x /usr/local/bin/extDiff
5. edit .gitConfig file under the user home directory as below
[merge] tool = extMerge [mergetool "extMerge"] cmd = extMerge \"$BASE\" \"$LOCAL\" \"$REMOTE\" \"$MERGED\" trustExitCode = false prompt = false [diff] external = extDiff tool = extDiff [difftool "extDiff"] cmd = mymerge.sh \"$LOCAL\" \"$REMOTE\" trustExitCode = false prompt = false

6. run
git diff
or
git difftool
or
git mergetool
on an updated repository to see the result.

Link:
https://community.perforce.com/s/article/2848
http://git-scm.com/book/en/Customizing-Git-Git-Configuration

Monday, December 30, 2013

Publish local git repository on mac for backup

Unlike other source management system, commit in git only saves the change in your local box before pushing the change into remote repository. If your local box stops working, you will lose all you committed work in git.

To avoid happening, before you are ready to push the local git repository's change into remote git repository, you can publish your local git repository using git daemon, and then backup it in another git repository on another box.

Steps to publish a local git repository on mac:
cd YourGitFolder
git daemon --base-path=. --export-all --enable=receive-pack --reuseaddr --informative-errors --verbose

From a different box, run the below clone commands to get the repository
git clone git://FirstBoxIPAddress/

Note client can’t push into the change into the server's active git branch. Before pushing, user on the server should change to another branch.

By the way, the following command can be used to monitor the commit history for all branch
gitk --all
to avoid block the terminal app, append & at the end
gitk --all &




Monday, December 16, 2013

Configure client certificate mapping on iis for mutual authentication

IIS Client certificate settings:
There are two places for client certificate settings in iis manager. It is very important to understanding the difference of these two settings.

The first place is in SSL settings under each web application's setting. There is a client certificate radio button. If it is selected as required, it means, when client connects to server using ssl, server will challenge the client's certificate, and the client certification must be signed by a root CA trusted by server (existing in server's Trusted root CA store). For example, a web application can allow anonymous authentication, but require client to use ssl with "Require" client certificate. If so, as long as client certificate is signed by a trusted CA, the client can finish the request without any one-to-one or many-to-one setting described in the second place.

The second place is web site scope setting of Configuration Editor under "Default web site".  The setting is for authenticating a client using client certificate. Note under web application's authentication setting, you can only set Anonymous, Basic, Digest, Form... authentication, there is not a client certificate setting for you to enable. So in order to enable client certificate authentication for your web app, you should disable all authentication items for your app under its authentication settings. And then using configuration editor under the default web site to enable it by configuring either one-to-one mapping or one-to-many mapping. Otherwise, as you already disabled all authentication method, even if the client certificate is trusted by server, the client cannot be authenticated by server and causes the request to fail. Note if you has enabled other kind authentication under authentication settings, the client certificate mapping is really not necessary.

Generate client certificate
(Steps are from: http://msdn.microsoft.com/en-us/library/ff650751.aspx)
1.Generated root certificate for creating client cert.
Open a Visual Studio command prompt and browse to the location where you want to save the certificate files. Run the following command to create the root CA:
makecert -n "CN=RootCaClientTest" -r -sv RootCaClientTest.pvk RootCaClientTest.cer

2. Create a Certificate Revocation List File from the Root Certificate with following command:
makecert -crl -n "CN=RootCaClientTest" -r -sv RootCaClientTest.pvk RootCaClientTest.crl

3. Install Your Client Root Certificate Authority on the Client and Server Machines with following steps:
In the command console, type MMC and then click OK.
In the Microsoft Management Console, on the File menu, click Add/Remove Snap-in.
In the Add Remove Snap-in dialog box, click Add.
In the Add Standalone Snap-in dialog box, select Certificates and then click Add.In the Certificates snap-in dialog box, select the Computer account radio button (because the certificate needs to be made available to all users), and then click Next.
In the Select Computer dialog box, leave the default Local computer: (the computer this console is running on) selected and then click Finish.
In the Add Standalone Snap-in dialog box, click Close.
In the Add/Remove Snap-in dialog box, click OK.
In the left pane, expand the Certificates (Local Computer) node, and then expand the Trusted Root Certification Authorities folder.
Under Trusted Root Certification Authorities, right-click the Certificates subfolder, click All Tasks, and then click Import.
On the Certificate Import Wizard welcome screen, click Next.
On the File to Import screen, click Browse.
Browse to the location of the signed root CA RootCaClientTest.cer file copied in Step 1, select the file, and then click Open.
On the File to Import screen, click Next
On the Certificate Store screen, accept the default choice and then click Next.
On the Completing the Certificate Import Wizard screen, click Finish.

4. Install the Certificate Revocation List File (CLR) on the Server and Client Machines, which is checked during the certificate validation process.

In the command line, type MMC, add Certificates snap-in, and then click Add.
In the Certificates snap-in dialog box, select the Computer account radio button (because the certificate needs to be made available to all users), and then click Next.
In the Select Computer dialog box, leave the default Local computer: (the computer this console is running on) selected and then click Finish.
In the left pane, expand the Certificates (Local Computer) node, and then expand the Trusted Root Certification Authorities folder.
Under Trusted Root Certification Authorities, right-click the Certificates subfolder, select All Tasks, and then click Import.
On the Certificate Import Wizard welcome screen, click Next.
On the File to Import screen, click Browse.
On the Files of Type screen, select Certificate Revocation List.
Browse to the location of the signed root CA RootCaClientTest.crl file copied in Step 1, select the file, and then click Open.
On the File to Import screen, click Next.
On the Certificate Store screen, accept the default choice and then click Next.
On the Completing the Certificate Import Wizard screen, click Finish.

5. Create and Install Your Temporary Client Certificate

Open a Visual Studio command prompt and browse to the location where the root CA certificate and private key file you created are stored.
Run the following command for creating a certificate signed by the root CA certificate:
makecert -sk MyKeyName -iv RootCaClientTest.pvk -n "CN=tempClientcert" -ic RootCaClientTest.cer -sr currentuser -ss my -sky signature -pe 

In this command:
-sk specifies the key container name for the certificate. This needs to be unique for each certificate you create.
-iv specifies the private key file from which the temporary certificate will be created. You need to specify the root certificate private key file name that was created in the previous step and make sure that it is available in the current directory. This will be used for signing the certificate and for key generation.
-n specifies the key subject name for the temporary certificate. The convention is to prefix the subject name with "CN = " for "Common Name".
-ic specifies the file containing the root CA certificate file generated in the previous step.
-sr specifies the store location where the certificate will be installed. The default location is currentuser. For certificate authentication, this is the default location that Microsoft Internet Explorer uses for when browsing Web sites that require a client certificate.
-ss specifies the store name for the certificate. My is the personal store location of the certificate.
-sky specifies the key type, which could be either signature or exchange. Using signature makes the certificate capable of signing and enables certificate authentication.
-pe specifies that the private key is generated in the certificate and installed with it in the certificate store. When you double-click the certificate on the General tab, you should see the message “You have a private key that corresponds to this certificate” displayed at the bottom. This is a requirement for certificate authentication. If the certificate does not have the corresponding private key, it cannot be used for certificate authentication.

6. The steps to generate iis server certificate for ssl connection  is not included here, please refer http://jonathanblog2000.blogspot.ca/2013/12/how-to-deploy-aspnet-project-to-iis-by.html.

Configure IIS for client certificate authentication (one-to-one mapping)
(http://www.iis.net/learn/manage/configuring-security/configuring-one-to-one-client-certificate-mappings)
1. Getting the Certificate Blob
Export the client cert file TempClientCert.cer from MMC certificate snap-in with Base64 encoding.  Right click on your client .cer file, and open it in notepad.
Remove -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----
Format the certificate blob to be a single line.
Save this file as clientCertBlob.txt

2. Configure IIS for client certificate One to One Mapping
Start  IIS Manager,
Select the web site (Default Web Site, this can not be done on web application) that is being configured and open Configuration Editor icon
Type "system.webServer/security/authentication/iisClientCertificateMappingAuthentication" in the Section drop down box.
Select the enabled field and change the value to true
Select the oneToOneCertificateMappingsEnabled property grid entry and change the value to true
Select the oneToOneMappings property grid entry and click Edit Items... in the Actions Task Pane
Click Add in the Collection Editor task list
Copy the single string certificate blob from above and paste it into the certificate field
Set the userName and password that clients will be authenticated as.
Set the enabled field to true
Close Collection Editor
Click Apply in the Actions Task Pane

3: Enabling Client Certificate Authentication For A Web Site Using SSL
Once a mapping has been created and the feature has been enabled, a site must be configured to use client certificates.
From  IIS Manager UI, select the SSL web application you want to use client certificates
Select the SSL settings module
Under Client certificates: select the Require or Accept radio button
Click Apply in the Actions Task Pane
Disable all authentication method for the web application.


4: Verifying It All Works (using firefox)

Export client certificate with private key to a file.
Import client certificate (with private key) into firefox browser by opening option->advanced->Certificate tab. Select view certificate, and import the certificate into "Your Certificate" tab. Once it is done, it will show the certificate under RootCaClientTest node.
Use https connection to visit the iis web application. You will be prompted to select a client certificate.


Configure IIS for client certificate authentication (many-to-one mapping)
(https://blogs.iis.net/webtopics/archive/2010/04/27/configuring-many-to-one-client-certificate-mappings-for-iis-7-7-5.aspx)
If you are within an enterprise environment, and each developer already has his own corporate certificate, it is easier to setup many-to-one client certificate for iis mutual authentication.

Similar to one-to-one mapping, select the configuration editor under the default web site, and set enabled to true
Set manyToOneCertificateMappingsEnabled to True
Select manyToOneMappings and click on the extreme end at the Ellipsis button to launch the new window for configuring mappings.
Under this new window go ahead and Add a new item. You can modify the properties from within the window
Click on the Ellipsis button for rules and this will give you an option to add multiple patterns for matching based on certificate properties. For example, you can set certificateField to "Issuer" and certificateSubField to "CN", and matchCriteria to "SSO_CA", it will map the client certificate issued by SSO_CA to the specified user account.
Set the userName and password that clients will be authenticated as.
Apply the change.
Disable all authentication methods under web application's authentication settings.
Request the server from browser and you should be prompted for client certificate.

SQL server, SQLExpress,SQLExpress user instance and localDB

MS SQL server has many versions available, sometimes it creates more confusion than help when an asp.net app works with visual studio but not after it is deployed to iis.


SQL server is the easiest one to understand, it has a single instance to be accessed.

SQL Express is a simplified version of SQL server. Note it requires a different installation to install. 

SQL Express User Instance and LocalDB: These are more for easy integration with Visual Studio, so the database are private to the application. But it causes permission issue if you need to work with iis deployment or developing environment switch. If you are not quite familiar with them like me, it makes sense  to avoid them in your project, as the effort to fix the configuration or permission issues may take longer than using a shared sqlExpress database instance. At least, it is easy to debug and understand if anything goes wrong. Simply is always better. 

To debug asp.net project after iis deplyment, attach visual studio to w3wp.exe.

If your asp.net application does not work after deploying to iis, the following issues may cause it:
1. The permission issue from SQL Express User Instance and LocalDB, change the database connection to SQL server or SQLExpress shared instance.

2. After that, for "opening database errors" when connecting to database, it may happen because the SQL server or SQLExpress instance is missing, first check whether the instance is available, open "SQL server configuration manager" under sql server 2012's "Configuration tools" application, select SQL Server Service node, and see whether the shared instance service is there.

3. For database permission issue, you may want to change the logon user for SQL server or SQLExpress service to local system to avoid any permission issue.

4. For Asp.net application pool permission issue, open iis manager, change all application pool's advanced settings' identity to local system.

5. if an error happen say "'NT AUTHORITY\SYSTEM" account cannot access the database, then either use database authentication instead of Windows authentication in connection string or open database manager studio, and make sure 'NT AUTHORITY\SYSTEM' exists under security/logins node. then change the "Default database" to the application's database, and also under "User Mapping", select the application database, and "public" adn "db_owner" role.

Sunday, December 15, 2013

Deploy VS 2012 asp.net project to iis with ssl

Deploy asp.net project to iis from visual studio 2012
1. In visual studio 2012, open the asp.net project.
2. right click the project, and select "publish web site"" menu
3. select profile dropdown list, and <new> to create new profile
4. set the profile name, for example "iis"
5. select deploy method to "web deploy"
6. set server name to localhost, and site name to "Default Web Site/MyAppName"
7. then publish the application

Enable SSL for iis web app
1. From iis manager, select the root item
2. select "Server Certificates" icon, and click "create self-signed certificate" item
3. select the web site and click "binding..." (ssl only applies to web site, not individual app"), for port 443, select ssl certificate to the one just created.
4. select the web application, and click on "ssl settings" icon, check require SSL checkbox
5. visit the site with https:// and a certificate exception will be prompted.

Create certificate with customized common name
1. IIS only creates certificate which uses computer name as common name. If the web site name is different from computer name, then you need to create certificate differently.
2.Download selfssl7 from the link of http://blogs.iis.net/thomad/archive/2010/04/16/setting-up-ssl-made-easy.aspx, and unzip selfssl7.exe to local
3. run the below command to create the certificate
selfssl7 /N cn=torn00461340a.amer.global.corp.sap /K 1024 /V 18250 /X /F torn00461340a.pfx /W password
4. import the certificate to iis.






Tuesday, December 10, 2013

Common git commands

Common basic git command

git init:
To create a new local git repository to run the git command

git branch new-branch-name
To create a new branch
This will use the current workspace's HEAD to create a branch. This command will create the new branch in repository, but it will not switch the current workspace to the new branch; use "git checkout <newbranch>" to switch to the new branch.

git checkout branch
switch the current workspace to the specified branch.
To combine the operation of create a branch and checkout it in a single command, use the following command:
"git checkout -b NewBranchName referenced-headername"
If the current branch has local added but uncommitted change, then you cannot switch to another branch by using git checkout. you need first commit or reset the change in current branch.

git branch
git branch -a
Execute "git branch" will show all local branches available in the repository, the current head branch is started with *.
"git branch -a" will list all remote and local branches

git add:
Add local change
take a stage snapshot of the local workspace change for future commit, it just creates snapshot for the current change in the workspace, no new head will be added in git repository. You can use git add multiple times and then commit them altogether

git commit
git commit --amend
To commit local change to repository
Git commit only commit the change from the snapshot created by git add.
--amend will add then new staged change into the previous commit. Running this when there is nothing staged lets you edit the previous commit’s message without altering its snapshot. After the commit message is changed in vi, for windows, press esc key and then type ":wq" to save the change and exit the editor.

git branch -d BranchName
delete a branch in local repository

git rebase SomeBranch
To apply the change in SomeBranch to the current branch from their common parent base head.

git pull
To pull server change without local committed change

git pull --rebase
integrate remote change to local branch

git fetch origin remoteBranchName
git checkout remoteBranchName
fetch a remote branch content to local and then checkout the branch. This command can be used when someone sends you a branch which contains some temporary change.

git push origin master
To push the local committed change to server branch

git clean -f -d
Remove local untracked file and folder

git checkout .
Revert all local change in existing files that are not staged with "git add" command.

git reset
revert the staged change made with "git add" command. the local change in workspace is still kept as local, unstaged change.  (git reset FilePath can also be used for single file )

git reset --hard
remove the staged change created by git add, and revert the local change back to the most recent commit. The local change will be lost

git revert PreviousCommitHash
make a commit to revert the previous change specified in previousCommitHash

git reset HEAD^
remove the last commit but keep the change in local

git add -u
Add all deleted file into added change for commit.

git cherry-pick head1
merge the change from one branch to another
1. get the sha1 number for the change
2. goto the dest branch and update the code to latest
3. run git cherry-pick sha1
4. commit the change to the new branch

git stash
save the current change with default name

git stash push PathOfFileOrFolder
save the current change for a particular file or folder

git stash save "name"
save a named change to stash

git stash list
show the existing stash

git stash pop
git stash pop stash@{0}
pop up a saved stash

git stash drop
delete the top stash without using it.

git stash apply
apply the last saved stash change, but do not delete it


Common comparison commands

git diff
compare local workspace change with added change (or committed change if no staged change available)

git difftool --cached
compare the difference between the added snapshot (before commit) and the last committed change

git difftool HEAD^ HEAD
compare the difference between the last commit and the previous commit

git diff HEAD
compare local workspace un-added change with the last committed change

git diff head1
compare the specified commit (head1) with the last commit (HEAD)

git diff commit1 commit2
compare all the changes made after commit1 (commit1's change is excluded), but including the changes made in commit2

git diff head1 head2
compare the specified commit head1 with head2

git diff head1^ head1
compare the change made by commit head1 with its parent

gitk -all &
show the visual commit history for all branches without block the terminal command

Git Tag for version management

git tag -a yourReleaseVersionNumber -m "release comment"
after a version is released, a git tag should be created to stamp the code that is associated with this release

git tag
show the existing tags

git show yourReleaseVersionNumber
show the commit message for the tag

git tag -d yourReleaseVersionNumber
delete an existing tag

git push origin yourReleaseVersionNumber
push the tag to remove repository

git checkout yourReleaseVersionNumber
checkout an existing tag. After checkout a tag (v1.0) as detached header, you need to use checkout -b to create a new branch (v1.1) based on the tagged code base, and then check in the change into the new branch (v1.1)


Update forked repository

assume you already in your local master branch, then follow the below steps

1. add remote (original) repo and call it upstream
git remote add upstream https://github.com/manishjanky/ngx-select-dropdown.git

2. fetch the change from remote upstream
git fetch upsteam

3. update local master to remote upstream's master
git rebase upstream/master

4. push your local master change to your remote forked master
git push origin master --force

Monday, December 9, 2013

Understanding ios dispatch queue and tasks

Type of dispatch queues:
serial queue:
A task on the queue will be executed only after its previous queued task is returned, the tasks are executed based on first in first out order, so at any given time, only one task from the queue is executed. Note independent serial queues (which have different names) are processed concurrently with respect to each other, so serial only applies to the tasks within a specific serial queue.

concurrent queue:
the tasks on the queue are still executed in first in first out order, but the next tasks may be executed before the previous task returns, so there may be multiple tasks run at the same time.

Find which queue is serial or concurrent?
All global queues returned by dispatch_get_global_queue are concurrent queues
The main thread queue returned by dispathc_get_main_queue is a serial queue
Queues created by dispatch_queue_create may be serial or concurrent queue depending on the second parameter, if it is null or DISPATCH_QUEUE_SERIAL, then it is a serial queue. If the parameter is DISPATCH_QEUE_CONCURRENT, then it is a concurrent queue.


Ways to submit a queued task:
dispatch_async:
When the task is submitted, the call will return immediately, the queue will decide how to invoke it based on whether the current queue is concurrent or serial.
dispatch_sync
When the task is submitted, the call is blocked and cannot continue until the block is executed and returned.

Notice when submitting a block task to dispatch queue, the dispatch_block_t does not take any parameter or does not return any value. This should not be a big issue, as block can access the variables in caller's scope with enclosure.

Other notes:
1. Queue created by dispathc_queue_create method must be released by dispatch_queue_release method.
2. Any pending blocks submitted to a queue hold a reference to that queue, so the queue will not be released until all pending blocks have completed

Sunday, December 8, 2013

Understanding ios block

Syntax
Blocks for objective c is similar to anonymous functions javascript, one can pass it as parameters of methods or return it from methods. Blocks have a typed parameter list and an optional return type. Block can be assigned to a variable and then be called as a function. The syntax of block follows C convention instead objective C, so it uses ',' to separate parameter, and use () to invoke a block.

Declare a block is similar as declare a function pointer, you can specify the return type and parameter types, but not the parameter names as shown below:
NSInteger (^mathOperation)(NSInteger, NSInteger);
void (^simpleBlock)(void);

Likes any other variables, the block variable can be assign to a value as shown below, note the parameter names are set in the block definition. However, the returning type specification is optionals, if missing it is inferred from the actual return type. To specify return type, it needs to be put after ^
NSInteger (^mathOperation)(NSInteger x, NSInteger y) = ^NSInteger(NSInteger x,NSInteger y){    
  return x + y;
}; 

void (^simpleBlock)(void) = ^{
        NSLog(@"This is a block");

    };

Another point is the parameters in block type definition uses ',' to separate each other, like C convention as shown below
NSInteger sum = mathOperation(1,2);

Note for block parameter type declared in method declaration is different from block variable, it moves the parameter name to the end of the declaration, like shown below. 

-(void) myMethodTakeBlockAsParameter: (NSInteger(^)(NSIntegerNSInteger))mathOperation;

When actually call the method, do not specify the return type, but specify the parameter names as below


 [self myMethodTakeBlockAsParameter: ^(NSInteger int1, NSInteger int2){
      return int1+ int2;
   }];


Variable Enclosing Scope
Block can access the data within its definition scope, similar to javascript enclosure function. If you implement a method and that method defines a block, the block has access to the local variables and parameters of the method (including stack variables), including instance variables. This access is read-only, but if a variable is declared with the __block modifier, its value can be changed within the block. Even after the method or function enclosing a block has returned and its local scope has been destroyed, the local variables persist as part of the block object as long as there is a reference to the block.

By default, the value is captured when the block is defined and is read-only. This means that if you change the external value of the variable after defining the block, then the value captured by the block is unaffected by the change. In below example, the anInteger's value in block is still 42:

    int anInteger = 42;
    void (^testBlock)(void) = ^{
        NSLog(@"Integer is: %i", anInteger);
    };
    anInteger = 84;
    testBlock();

If you need to change the value of a captured variable from within a block, you can use the __block storage type modifier on the original variable declaration. This means a live instance is used by the block and the original variable scope. For example if define the above variable as
__block  int anInteger = 42;
then the output will be 84.

When to use block
1. to enumerate NSArray or NSEnumerate object
enumerateObjectsUsingBlock

2. to specify a callback method as function parameter, such as completion callback block
3. To use by dispatch queue to specify a task to run sequentially or concurrently in sync or sync ways

Wednesday, December 4, 2013

Cordova ios webview debug issue

Cordova is a great open source framework. But it is free, which means you cannot expect too much from it. The following are two ios debug issues to make it work properly

1. To avoid webview thread lock crash:

In cordova.js, find method iOSExec.nativeCallback at line 1009, and replace it with the following code

iOSExec.nativeCallback = function(callbackId, status, message, keepCallback) {
    function f0(){
       return iOSExec.nativeEvalAndFetch(function() {
          var success = status === 0 || status === 1;
          var args = convertMessageToArgsNativeToJs(message);
          cordova.callbackFromNative(callbackId, success, status, args, keepCallback);
       });
    }
    setTimeout(f0, 100);

};

2. To avoid missing plugin call
In cordova.js line 949, change it to following
    if (true || !isInContextOfEvalJs && commandQueue.length == 1) {
        if (bridgeMode != jsToNativeModes.IFRAME_NAV) {
            // This prevents sending an XHR when there is already one being sent.
            // This should happen only in rare circumstances (refer to unit tests).
            if (execXhr && execXhr.readyState != 4) {
                execXhr = null;

            }

Sunday, December 1, 2013

Understanding logic of cordova inappbrowser plugin

When inappbrowser plugin open method is called to open a new url,  the plugin native code will create a new webview and set its delegate to a new webview's controller to handle the url loading event. So there will be two different web views existing, once for the original html page and one for the inappbrowser url.

Once the inappbrowser url is loaded, the inappbrowser's webview didStartLoad and didFinishLoad event will be called, the inappbrowser native code will report the event as plugin callback to javascript. Note as the original plugin method was called on the original html page, so the plugin js callback onloadstart, onloadstop will also be invoked on the original plugin js method implemented on original html page.

In addition, since the onloadstart or onloadstop was triggered by url loading system from inappbrowser webview, so those events will be fired again whenever the inappbrowser loads an new url after the initial open url is loaded, so if you click a link in inappbrowser's html page to load another url, then the onloadstart and onloadstop events will be fired again for the new url

The html content shown in the inappbrowser webview really does not matter, it can be a simple html page or a text file, which has nothing to do with cordova, although it should also work if a cordova app is loaded.

Certainly on exit event will only be fired once when the inappbrowser is closed.