Android,Mobile Development,ORMLite

Database Operations with ORM Lite16 May

In the first blog post, we discovered about ORMLite as a formidable ORM for use in android applications. Here we will delve deeper into how to perform various database operations using ORMLite

Query Operation in ORM Lite

The DAOs provide basic methods to query for an object that matches an id (queryForId) and query for all objects (queryForAll), iterating through all of the objects in a table . However, for more custom queries, there is the queryBuilder() method which returns a QueryBuilder object for the DAO with which you can construct custom queries to return a sub-set of your tables.

Query Builder is something that assists in building SQL query (SELECT) statements for a particular table in a particular database.

1.Query for all

Returns the list of all records in the table we have in build function queryForAll();

// get our dao
 RuntimeExceptionDao<DemoORMLite, Integer> DemoORMLiteDao = getHelper().getDemoORMLiteDao ();

// query for all of the data objects in the database
 List<SimpleData> list = simpleDao.queryForAll();

2. Query for id

Returns the record corresponding to given id we have in build function queryForId(id);

TEntity entity = <strong>this</strong>.dao.queryForId(id);

3. Query for particular field name

Here we use QueryBuilder to query for field “lastname” and return the list of records that have last_name =”lastname”

Public List<DemoORMLite> RetrieveByLastName(String lastname)throws SQLException {
QueryBuilder<TEntity,Integer> queryBuilder = dao.queryBuilder();
List list;
queryBuilder.where().eq("last_name", lastname);
list = queryBuilder.query();
return list;
}

Deleting a record in ormlite

DeleteBuilder assists in building sql DELETE statements for a particular table in a database.

Sample Code that deletes elements from table by argument

DatabaseHelper helper = OpenHelperManager.getHelper(App.getContext(), DatabaseHelper.class);</pre>
//get helper
 Dao dao = helper.getDao(YOUR_CLASS.class);

//get your Dao
 DeleteBuilder<CanteenLog, Integer> deleteBuilder = dao.deleteBuilder();
 // CanteenLog here is table name

deleteBuilder.where().eq("FIELD_NAME", arg);

deleteBuilder.delete();

Order by in ormlite

Syntax: orderBy(String columnName, boolean ascending)

Add “ORDER BY” clause to the SQL query statement to order the results by the specified column name. Use the ascending boolean to get a ascending or descending order. This can be called multiple times to group by multiple columns.

QueryBuilder<Visit, Integer> qb = getHelper().getyourdaoname().queryBuilder();
 qb.where().eq("Field_name", fieldname);
 qb.orderBy("order_according_to_this_field", false);

Using Max in Ormlite


long max = fooDao.queryRawValue(

"select max(modified) from foo where userid = ?", id);
 // now perform a second query to get the max row
 Foo foo = fooDao.queryBuilder().where().eq("modified", max).queryForFirst();

Set default value to column in ORMLite

@DatabaseField(defaultValue = "unknownName", canBeNull = true)

Here we explored various simple ORMLite operations. If you have used ORMLite in more advanced way, Please feel free to drop a comment below !!

Android,ORMLite

ORMLITE – Light Weight Object Relational Mapping – An Introduction07 May

OVERVIEW

ORM Lite provides a lightweight Object Relation Mapping between Java classes and SQL databases. ORM Lite supports JDBC connections to MySQL, Postgres, H2, SQLite, Derby, HSQLDB, and Microsoft SQL Server. ORM Lite also supports native database calls on Android OS.

Using ORM Lite with Android
Downloading ORMLITE

To get started with ORM Lite, We need to download the ORM Lite jar files. These can be downloaded from ORM Lite release page

Once we have downloaded ORM Lite, we will need to add it as an external library to our android project. Just Drop the jar file into your project’s libs/ subdirectory.
We only need the ormlite-android-4.14.jar, not the ORM lite-core or any other packages.

Getting Started with ORM Lite

To get started with ORM lite we need to create our own database helper class which should extend the OrmLiteSqliteOpenHelper class. This class creates and upgrades the database when the application is installed and also provide the DAO(Data Access Object) classes used by other classes. The helper class must implement the methods


onCreate(SQLiteDatabase sqliteDatabase, ConnectionSource connectionSource)

onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion)

onCreate creates the database when app is first installed while onUpgrade handles the upgrading of the database tables when we upgrade our app to a new version.

The helper should be kept open across all activities in the app with the same SQLite database connection reused by all threads. If we open multiple connections to the same database, stale data and unexpected results may occur. It is recommended to use the OpenHelperManager to monitor the usage of the helper – it will create it on the first access, track each time a part of our code is using it, and then it will close the last time the helper is released.

Once we define our database helper and are managing it correctly, We will use it in our Activity classes. An easy way to use the OpenHelperManager is to extend OrmLiteBaseActivity for each of your activity classes – there is also OrmLiteBaseListActivity, OrmLiteBaseService, and OrmLiteBaseTabActivity. These classes provide a helper protected field and a getHelper() method to access the database helper whenever it is needed and will automatically create the helper in the onCreate() method and release it in the onDestroy() method.

Here is sample DatabaseHelper class

http://ormlite.com/android/examples/DatabaseHelper.java

Creating Table using ORM Lite
There are a few things to notice when we use ORM Lite:

  1. We just annotate our class as a table and its members as fields and we’ re almost done with creating a table
  2. ORM Lite handles all of the basic data types without any explicit work on your part (integers, strings, floats, dates, and more).
  3. It is mandatory to have a no argument constructor in our class

In onCreate method of Databasehelper we create tables with help of TableUtils like

TableUtils.createTable(connectionSource, SimpleData.class);,

SimpleData here is name of table

An example code to create Table in the onCreate callback:

 /**
 * This is called when the database is first created. Usually you should call createTable statements here to create
 * the tables that will store your data.
 */
 @Override
 public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
 try {
 TableUtils.createTable(connectionSource, SimpleData.class);
 } catch (SQLException e) {
 Log.e(DatabaseHelper.class.getName(), "Can't create table", e);
 throw new RuntimeException(e);
 }

Here is sample ORMLite table class


/**
 * A simple demonstration object we are creating and persisting to the database.
 */
 public class SimpleData {

// id is generated by the database and set on the object automagically
 @DatabaseField(generatedId = true)
 int id;
 @DatabaseField(index = true)
 String string;
 @DatabaseField
 long millis;
 @DatabaseField
 Date date;
 @DatabaseField
 boolean even;

SimpleData() {
 // needed by ormlite
 }

public SimpleData(long millis) {
 this.date = new Date(millis);
 this.string = (millis % 1000) + "ms";
 this.millis = millis;
 this.even = ((millis % 2) == 0);
 }

@Override
 public String toString() {
 StringBuilder sb = new StringBuilder();
 sb.append("id=").append(id);
 sb.append(", ").append("string=").append(string);
 sb.append(", ").append("millis=").append(millis);
 SimpleDateFormat dateFormatter = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.S");
 sb.append(", ").append("date=").append(dateFormatter.format(date));
 sb.append(", ").append("even=").append(even);
 return sb.toString();
 }
 }

Hope that the blog gets you running on ORMLite, In the subsequent posts, we will explore more about ORMLite and how to use it to manipulate/retrieve data objects.

References:

  1. ORMLite Sample table class (http://ormlite.com/android/examples/SimpleData.java)
  2. ORMLite example: (http://ormlite.com/android/examples/)
Node.js,Real Time Programming

Hiring a Nodejs Developer and our expertise with Node.js26 Apr

As the time tells, Node.js is the latest hit number on the market. People are trying to adopt the technology for the latest web products and ventures. The demand for the developers skilled with node.js is increasing much more rapidly than the number of node.js developers available on the block. To complicate the situation further, there are many a developers who say that they know node.js but they actually know only HTTP programming and can’t drive the actual horsepower out of the node.js(The problem common to all technologies afaik). This makes it a real task for someone looking to hire a team of node.js developers or node.js freelancer. I will try here to make this task easier by putting together a few points which you should consider while hiring a node.js developer.

Make sure that You and Your Developer Understand that why are you using Node.js

Well, its pretty easy to get carried away with the new technology but nodejs is very much different from PHP,Python or Ruby in terms of how its intended to be used. As Node.js official site quotes

Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices.

What this means is that nodejs is meant for Event driven programming and this is what it does the best. Ofcourse, we can use it for normal HTTP applications as well but using node.js for normal HTTP applications will definitely prove to be an overkill and definitely would be a dragon to maintain in long run. Therefore, nodejs is well suited for the kind of applications that require realtime interaction between the users. The examples include but not limited to Facebook Ticker, Chat Application, Online HTML5 Games or any other application that requires realtime events to be pushed to the clients. In more technical words, node.js is very well suited for long polling kind of applications which require very less CPU cycles and hence supports very high number of concurrent connections. The developer must understand what a realtime programming is and should not confuse it with AJAX.

Experience with Nodejs Development

Since Node.js development is relatively new in the market, its very hard to find developers experienced than a particular number of years in Node.js development. Infact, Node.js has come into limelight for not more than 2 years at the time of this writing so its almost impossible to find the developer having experience more than this number. Its pretty easy to find a Node.js developer with decent amount of hands-on experience though. However, Node.js is a javascript based framework so make sure that developer is having a decent amount of javascript experience otherwise he wont be able to cop up with the problems along the way. Experience with Node.js frameworks like Express, Connect and Geddy is more than welcome since frameworks enable rapid development. Experience with socket.io or other frameworks enabling too/fro communication with the node.js servers using HTML5 Websockets / Flash also comes in handy. The developer’s public commits on github or contributions on other coding portals can also give quite a healthy idea of what to expect. If you are looking for a Nodejs team, Please ensure that you check the credentials of each of the developers in your team.

Databases with Node.js development

Since Node.js works the best for long polling scenarios, it becomes sometimes imperative to use a NOSQL database like Redis, Mongo, CoucheDB etc with Node.js(I know it can be debated on a separate thread .. :) ), its anyways a good idea to hire a developer having experience with one or all of them.

Security

Securing a nodejs application can get really tricky. Since Node.js is javascript based, the developer must be aware of javascript vulnerabilities along with the methods to mitigate them. Its good to interview them for various security loops that they have seen till date and how they have resolved them.

Where to Find The Developers

You can find a lot of nodejs developers with competent experience on portals like odesk, elance etc. For a full time basis you can put your requirements on normal job portals like simplyhired, dice, craigslist etc.

Enbake provides full blown Nodejs Development Services tailored to your requirements and You can contact us as well for your needs or just to drop a Hi !!

Have Fun with Node.js !!

Server Admin

RSYNC24 Apr

Rsync stands for remote sync. Rsync is used for synchronization of files and directories between two locations. So it can serve as a backup process also.

Important features of rsync :-

    • Speed: File transfer with rsync is fast and efficient because it checks local files against remote files in small chunks, or blocks, and transfers only the blocks that differ between the files.
    • Security: Rsync allows encryption of data using ssh protocol during transfer.
    • Less Bandwidth: Rsync uses compression and decompression of data block by block at the sending and receiving end respectively. So the bandwidth used by rsync will be always less compared to other file transfer protocols.
    • Privileges: No special privileges are required to install and execute rsync

Syntax :-

$ rsync [options]

[destination]
Source and destination could be either local or remote. In case of remote, we also need to specify the login name, remote server name and location.

Install rsync :-

  • On Ubuntu :- $ sudo apt-get install rsync
  • On Red Hat Enterprise Linux (RHEL) / CentOS 4.x or older version : –
    # up2date rsync
  • On RHEL / CentOS 5.x or newer (or Fedora Linux) : -# yum install rsync

Lets go through various common operations that we can perform using rsync.

Synchronize between two directories on same system :-
Let’s say, there are two folder on the personal computer rsync1 and rsync2. So we have to synchronise the data in these two folders ( Assuming these folders are located in present working directory). The command to sync the data between two will be like:-

$ rsync -vv -e ssh rsync1/* rsync2

rsync1/* for sync all the files and sub directories in the rsync1 directory.

Options
-v = verbose (-vv for more details)
-z = compress file data
-e ssh = for enable the secure remote connection
-a = Recursive mode, Preserves timestamp, Preserves owner and group
-u = Do not overwrite a file at the destination, if it is modified
-d = Synchronize only directory tree from source to the destination
–progress = Displays detailed progress of rsync execution
-i = Option displays the item changes.
- -max-size = not to copy files more than max size given.

$ rsync -azvu --delete rsync1/. rsync2/.

For deleting the extra files from destination
–times = Set the timestamps of the destination file to match those of the source file, instead of using the time of transfer (that is, reflecting the existence of a new file on the destination host)
–exclude = Don’t transfer files whose names match glob-pattern. (glob-pattern like “*-*-*- *-*.*”)
–include = specify files to be transferred whoes name matches glob-pattern(“*-*-*-*-*.*”)

Synchronize Files From Local to Remote :-

$ rsync -avz -e ssh sync1/* 

username@machinename:path
sync1 is local system directory (source detination)
username@machinename:path (target destination).
We can also use rsh to enable the ssh key :-

$ remote_shell = "ssh -i pathToKey username@machinename"
 $ rsync -avz -e --rsh=\remote_shell\ sync1/* username@machinename:path

Synchronize Files From Remote to Local

$ rsync -avz -e ssh username@machinename:path sync1/*

sync1 is local system directory (target destination)
username@machinename:path (source destination)
We can also use rsh to enable the ssh key :-

remote_shell = "ssh -i pathToKey username@machinename"
$ rsync -avz -e --rsh=\remote_shell\ sync1/* username@machinename:path
Document Processing,RoR App Development,Ruby on Rails,Server Admin

Using Localtunnel in Rails Application11 Apr

Localtunnel, as the name indicates, allows you to share your local web server on the internet. This kind of technique is very useful in cases such as:-

  • You want to demonstrate your web applications to clients.
  • Testing the web applications for compatibility issues against various operating systems and web browsers
  • Testing your web applications for compatibility in various mobile devices.

It is very easy to use. Your web application will be shared on public internet in a couple of seconds while it is running on your local system. Lets go through a step by step procedure to tunnel our very own RoR apps:

STEP 1:
Firstly, you need to install the client library for Localtunnel. So just install it by installing the ‘localtunnel’ gem or write it in your Gemfile:-

gem 'localtunnel'

and run ‘bundle install’

STEP 2:
Run your local web server on any port! Let’s say you’re running on port 8080.

$rails s -p 8080

STEP 3:
Now run localtunnel by specifying it the on port to be shared as:-

$ localtunnel 8080

It will establish a connection between your local server running at 8080 and localtunnel.com. Note that for first time you run the localtunnel, it needs you to specify your ssh public key for authentication. Here’s an example:

$ localtunnel -k ~/.ssh/id_rsa.pub 8080

After running the above commands, you will see something like this: -
Port 8080 is now publicly accessible from http://xyz.localtunnel.com …

Enjoy Tunnelling your apps !!

cakephp

Implementing softdelete with cakephp06 Apr

Softdeleting in terms of database generally means marking the record as delete rather than physically removing the record from the db. It is specifically useful in cases like CRMs where we want to retain the data for future reference in the database.

There are a few behaviors to implement Softdelete behavior in cakephp. We researched on them and finally decided to use cakesyrup’s softdeleteable behavior. Another good option is to use CakeDC Utils SoftDelete Behavior but at the time of this writing the behavior required quite a few changes for it to be used effectively.

How to Use Softdeleteable in a cakephp application

  • Download the behavior from the sourceforge on the above said link and copy it to app/Model/Behavior
  • Add the behavior to the $actsAs member of the model that needs to be softdeleted:
    class User extends AppModel {
    var $actsAs = array('SoftDeletable');
    }
    
  • By Default it works on two fields in a table named deleted, delete_date, but if you want to use the different fields from your table, you can configure that in the options to the Softdeletable behavior:
    var $actsAs = array('SoftDelete'=> array('field' => '0'));
    
  • Thats it. Now whenever you perform a delete action on certain model, then it won’t delete that record permanently instead will set deleted as 1 hence it won’t be resulting in the finder queries that you shoot for the model.
    Enjoy retaining your records from deleting permanently !!
iOS Development,iOS Programming,iPhone Development,Mobile Development,Objective-C

Bundle and plist in Objective C – An Overview11 Mar

Bundle: A bundle is a structure used for packaging software on Mac OS X.
The main bundle is simply the bundle of the application that is running. So, for instance, the main bundle of the Apple mail program is /Applications/Mail.app.

Types of Bundle:
There are three general types of bundles:
Application bundles
Application bundles contain an executable and all its related resources, such as nib files, image files, and localized strings.
Plug-in bundles
Plug-in bundles provide code that extends or enhances the functionality of a host application in some way.
Framework bundles
Framework bundles contain dynamic shared libraries, as well as header files, images, and documentation.

How to access bundle resource:
When a user launches an application, it finds the code and resources in the main bundle that it immediately needs and loads them into memory. Thereafter, the application can dynamically (and lazily) load code and resources from the main bundle or subordinate bundles as required.

plist: This file is an XML-based text file that contains specific types of key-value pairs. These key-value pairs specify information about the bundle, such as its ID string, version number, development region, type, and other important properties.

Importance: A great way to store dictionary data that does not change during runtime is in a .plist file.

Example:

NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:@"Info.plist"];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:finalPath];

Notes

  • The settings bundle is for having settings that show up in the system Settings.app.
  • You can use a plist to store settings that you change within the app itself.

Enjoy your day !!

Google API,iOS Development,iPhone,iPhone Development,Mobile Development

Integrating Google oAuth APIs with iPhone Applications13 Feb

This blog is an effort to round up on how to consume Google’s REST APIs in an iPhone App.  As usual with RESTful APIs, the client specifies an action using an HTTP verb such as POST, GET, PUT, or DELETE and the resource by a globally-unique URI of the following form:

https://www.googleapis.com/apiName/apiVersion/resourcePath?parameters

The response will be returned by the API in JSON format.

The APIs use OAuth 2.0 protocol for authentication and authorization. Google supports several OAuth 2.0 flows that cover common web server, JavaScript, device, installed application, and server to server scenarios. Here we are dealing with installed application on device scenario. Steps to integrate the Google APIs in a normal iOS application are not different than the normal OAuth flow.

Register Application:
First of all , the application must be registered through Google API console. The result of this registration process is a set of values that are known to both Google and application (e.g. client_id, client_secret, redirect_uri, etc.). The set of values generated varies based on the type of application you are building.
Another thing that you need to do is to turn on the APIs that your application needs to access in API Console.

Obtain an Access Token from the Google Authorization Server:
This is the normal OAuth flow where we need to get the access token to call an API. For authorization call this url, it will show a gmail login window which needs user login credentials:

Parameters Value
response_type code
client_id the client_id obtained from the APIs Console.
redirect_uri the redirect_uri values registered at the APIs Console.
scope space delimited set of permissions the application requests.
state anything

In iPhone, application can fetch authorization code from following code:

NSString *tokenCode
   = [theWebView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('title')[0].innerHTML;"];

Authorization code will be something like “token = tokenValue”. Retrieve the tokenValue and use that to obtain an access token from Google by shooting the post request to following url.

<pre><em id="__mceDel">NSString *url = @"https://accounts.google.com/o/oauth2/token?";
NSString  *data =[NSString stringWithFormat: @"code=%@&client_id=testing.apps.googleusercontent.com&client_secret=OxVwVVgYqvlgjoidgdfghdDv&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code",token];


Response of above url will return the dictionary having token, expire time, refresh token and token type.

{
"access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
"expires_in":<expiry_time>,
"token_type":"Bearer",
"refresh_token":"1/xEoDL4iW3cxlI7yDbSRFYNG01kVKM2C-259HOF2aQbI"
}

After obtaining access token above, you are ready to call the Google API for your purpose.

Calling Google Api:
After application has obtained an access token, application can access a Google API by including it in either an access_token query parameter or an Authorization: Bearer HTTP header. Application can call on those api which are passed as scope in authorization URL.
For example, a call to theCalendar API using the access_token query string parameter looks like this:
https://www.googleapis.com/calendar/v3/users/me/calendarList?access_token=1/fFAGRNJru1FTz70BzhT3Zg

This will return entries on the user's calendar list. Similarly you can call any other API from the API console which requires an OAuth Token. Enjoy building the cool apps that integrate with Google API !!

Amazon EC2,Server Admin

Attaching an EBS volume to Amazon EC2 instance04 Feb

The EC2 backed Amazon’s instances come with a limited storage space by default(8GB to be specific mounted on /). However, some applications may require much more instance space. This can be accomplished by attaching an EBS storage to the your EC2 instance. Attaching extra storage on EC2 instance is pretty easy and you got to follow a few very simple steps:

  • Allocate a new EBS in your amazon AWS EC2 dashboard. The option comes under the heading volumes in the major head Elastic Block Storages. You will be asked for the size you want to allocate. The availability zone must be the same as the Amazon EC2 instance’s zone to which you want to connect this volume to.
  • In the list of volumes, attach the newly created EBS volume to the desired instance(As you select the volume using the checkbox, an option will appear on the top to attach it to an instance).
  • Now connect to your instance using ssh and run sudo fdisk -l.
    fdisk will show the list of volumes and your newly created volume should appear unpartioned in this list.
    Format the file system /dev/xvdf(ubuntu’s external name to your drive) using the following command:
    sudo mkfs.ext4 /dev/xvdf
  • Mount the volume onto the system and add it to /etc/fstab for mounting at the system start(Here we assume that you will mount to /vol, this can be any directory that you like):
    sudo mkdir -m 000 /vol
    echo "/dev/xvdf /vol auto noatime 0 0" | sudo tee -a /etc/fstab
    	sudo mount /vol 
  • Thats it, enjoy your extended partition !!
Continuous Integration,rails,Ruby on Rails

Introduction to Cruisecontrolrb – An RoR Integration Server14 Jan

What is Cruisecontrolrb
Cruisecontrol.rb is a simple and popular rails continuous integration server. Like continuous integration tools, its basic functionality is to build the source code of a project whenever a new commit is made to the main repository.

Cruisecontrolrb setup
Setting up cruisecontrol.rb for your app is very simple. You need to download it from https://github.com/thoughtworks/cruisecontrol.rb/downloads and setup onto your server as a simple rails application.
After the cruisecontrol is setup, you need to add your project to it using the following console command:-

./cruise add  -u

You can alternatively add your project from UI as well by running the cuisecontrol application and by going to Add Project menu.

Running Cruisecontrolrb
After configuring your project, you can continue running it as rails app on port no 3000 or for a continuous monitoring you can start it on port 3333 by running the following command:-

./cruise start

Now whenever any of the developer checks in something to the main repository, it will monitor the activity and start building the project by fetching the code from that main repository.

Configuring Cruisecontrolrb
Cruisecontrolrb provides a lot of configuration options to the developers. Along with the inbuilt options there are a plenty of opensource plugins available to use with cruisecontrolrb. An example configuration to notify the developers regarding build failed and build fixed, you can mention configure their email addresses in the ~/.cruise/site_config file on your server as following(supposing sendmail/smtp is already configured on server) :-

<em>project.email_notifier.emails = ['abc@xyz.com', 'xyz@abc.com']


You can checkout more configuration options here. If you want to manually build the project, you can open UI on port 3333 and press the build now button. Already go to command line and type the following:-

./cruise build

Additional functionality and Plugins
Cruisecontrolrb provides a plethora of flexibility to build additional functionality and plugins. There are several complex and basic callbacks exposed to developers to achieve the functionality required including but not limited to:-
polling_source_control
no_new_revisions_detected
new_revisions_detected
build_requested
queued
timed_out
build_initiated
configuration_modified
build_started
build_finished
build_broken
build_fixed
build_loop_failed
sleeping

Apart from that, its very easy to write a plugin in cruisecontrol.rb. All you need to do is just to create a simple file with the following format:-

class SamplePlugin < BuilderPlugin
		  def build_finished(build)
		    //you custom code
		    //goes here
		  end
		end
		Project.plugin :sample_plugin

and save it in .cuise/builder_plugins folder.

Enjoy the continuous  integration on your latest RoR app !!

Enbake Consulting 2009.
Home | About Us | Services | Contact US
PHVsPjxsaT48c3Ryb25nPndvb19hYm91dDwvc3Ryb25nPiAtIFNlbGVjdCBhIHBhZ2U6PC9saT48bGk+PHN0cm9uZz53b29fYWRfYmVsb3dfaW1hZ2U8L3N0cm9uZz4gLSAvaW1hZ2VzL2FkNDY4LmpwZzwvbGk+PGxpPjxzdHJvbmc+d29vX2FkX2JlbG93X3VybDwvc3Ryb25nPiAtIGh0dHA6Ly93d3cud29vdGhlbWVzLmNvbTwvbGk+PGxpPjxzdHJvbmc+d29vX2FsdF9zdHlsZXNoZWV0PC9zdHJvbmc+IC0gZGVmYXVsdC5jc3M8L2xpPjxsaT48c3Ryb25nPndvb19ibG9ja19pbWFnZTwvc3Ryb25nPiAtIC9pbWFnZXMvYWQzMzYuanBnPC9saT48bGk+PHN0cm9uZz53b29fYmxvY2tfdXJsPC9zdHJvbmc+IC0gaHR0cDovL3d3dy53b290aGVtZXMuY29tPC9saT48bGk+PHN0cm9uZz53b29fYmxvZzwvc3Ryb25nPiAtIHRydWU8L2xpPjxsaT48c3Ryb25nPndvb19ibG9nY2F0PC9zdHJvbmc+IC0gaHR0cDovL2Jsb2cuZW5iYWtlLmNvbS88L2xpPjxsaT48c3Ryb25nPndvb19jYXRfbWVudTwvc3Ryb25nPiAtIGZhbHNlPC9saT48bGk+PHN0cm9uZz53b29fY29udGFjdDwvc3Ryb25nPiAtIFNlbGVjdCBhIHBhZ2U6PC9saT48bGk+PHN0cm9uZz53b29fY3VzdG9tX2Nzczwvc3Ryb25nPiAtIDwvbGk+PGxpPjxzdHJvbmc+d29vX2N1c3RvbV9mYXZpY29uPC9zdHJvbmc+IC0gPC9saT48bGk+PHN0cm9uZz53b29fZmVhdHBhZ2VzPC9zdHJvbmc+IC0gPC9saT48bGk+PHN0cm9uZz53b29fZmVlZGJ1cm5lcl91cmw8L3N0cm9uZz4gLSA8L2xpPjxsaT48c3Ryb25nPndvb19nb29nbGVfYW5hbHl0aWNzPC9zdHJvbmc+IC0gPHNjcmlwdCB0eXBlPVwidGV4dC9qYXZhc2NyaXB0XCI+DQoNCiAgdmFyIF9nYXEgPSBfZ2FxIHx8IFtdOw0KICBfZ2FxLnB1c2goW1wnX3NldEFjY291bnRcJywgXCdVQS0xNjU2MTE3Ni0xXCddKTsNCiAgX2dhcS5wdXNoKFtcJ19zZXREb21haW5OYW1lXCcsIFwnLmVuYmFrZS5jb21cJ10pOw0KICBfZ2FxLnB1c2goW1wnX3RyYWNrUGFnZXZpZXdcJ10pOw0KDQogIChmdW5jdGlvbigpIHsNCiAgICB2YXIgZ2EgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwnc2NyaXB0XCcpOyBnYS50eXBlID0gXCd0ZXh0L2phdmFzY3JpcHRcJzsgZ2EuYXN5bmMgPSB0cnVlOw0KICAgIGdhLnNyYyA9IChcJ2h0dHBzOlwnID09IGRvY3VtZW50LmxvY2F0aW9uLnByb3RvY29sID8gXCdodHRwczovL3NzbFwnIDogXCdodHRwOi8vd3d3XCcpICsgXCcuZ29vZ2xlLWFuYWx5dGljcy5jb20vZ2EuanNcJzsNCiAgICB2YXIgcyA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlUYWdOYW1lKFwnc2NyaXB0XCcpWzBdOyBzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGdhLCBzKTsNCiAgfSkoKTsNCg0KPC9zY3JpcHQ+PC9saT48bGk+PHN0cm9uZz53b29fbGF5b3V0PC9zdHJvbmc+IC0gYmxvZy5waHA8L2xpPjxsaT48c3Ryb25nPndvb19sb2dvPC9zdHJvbmc+IC0gPC9saT48bGk+PHN0cm9uZz53b29fbWFudWFsPC9zdHJvbmc+IC0gaHR0cDovL3d3dy53b290aGVtZXMuY29tL3N1cHBvcnQvdGhlbWUtZG9jdW1lbnRhdGlvbi92aWJyYW50Y21zLzwvbGk+PGxpPjxzdHJvbmc+d29vX25hdl9leGNsdWRlPC9zdHJvbmc+IC0gMzYwLDM3NywxMzAsNzM3PC9saT48bGk+PHN0cm9uZz53b29fc2hvcnRuYW1lPC9zdHJvbmc+IC0gd29vPC9saT48bGk+PHN0cm9uZz53b29fc2hvd19hZDwvc3Ryb25nPiAtIGZhbHNlPC9saT48bGk+PHN0cm9uZz53b29fc2hvd19tcHU8L3N0cm9uZz4gLSBmYWxzZTwvbGk+PGxpPjxzdHJvbmc+d29vX3N0ZXBzPC9zdHJvbmc+IC0gU2VsZWN0IEZvcm1hdDo8L2xpPjxsaT48c3Ryb25nPndvb190YWJiZXI8L3N0cm9uZz4gLSBmYWxzZTwvbGk+PGxpPjxzdHJvbmc+d29vX3RoZW1lbmFtZTwvc3Ryb25nPiAtIFZpYnJhbnRDTVM8L2xpPjwvdWw+