Monthly Archives: May 2016


Installing CMS (Contest Management System) on multiple servers

For the needs of the 24th BOI (Balkan Olympiad in Informatics) that will be held in Cyprus in June 2016 we setup CMS (Contest Management System)  on multiple servers.

We used Ubuntu 14.04 LTS (Trusty Tahr) for the OS of the servers, the installations were made with encrypted LVM.

We used 3 servers in total, the first (alpha) would serve as a master that holds all services for the competition except for the workers that test the submissions. The other two (beta and gamma) were setup to hold the workers.

On Alpha:

We installed various packages that are needed for the installation of the system

sudo apt-get install build-essential fpc postgresql postgresql-client gettext python2.7 python-setuptools python-tornado python-psycopg2 python-sqlalchemy python-psutil python-netifaces python-crypto python-tz python-six iso-codes shared-mime-info stl-manual python-beautifulsoup python-mechanize python-coverage python-mock cgroup-lite python-requests python-werkzeug python-gevent patool;

We downloaded the CMS and created the basic configuration.

#Download stable version from GitHub
wget https://github.com/cms-dev/cms/releases/download/v1.2.0/v1.2.0.tar.gz
#Extract the archive
tar -xf v1.2.0.tar.gz 
cd cms
#Copy the sample configuration files to be modified
cp config/cms.conf.sample config/cms.conf
cp config/cms.ranking.conf.sample config/cms.ranking.conf

cms.conf

Using a text editor we modified file config/cms.conf. The changes we did are the following:

  • We replaced "database": "postgresql+psycopg2://cmsuser:[email protected]/database"
    With the username, the password and database name we will use for our database configuration in a while. e.g.
    "postgresql+psycopg2://myuser:[email protected]/mydatabase". Note: we chose a username and database name with no capital letters.
  • We replaced "secret_key": "8e045a51e4b102ea803c06f92841a1fb" with another random 32 character string containing a-z and 0-9 (hex characters)
  • We replaced "rankings": ["http://usern4me:[email protected]:8890/"] to some other username password configuration. e.g. "rankings": ["http://myUsern4me:[email protected]:8890/"]
  • We changed the "_section": "AsyncLibrary", to hold the configuration of the services as follows
"_section": "AsyncLibrary",

"core_services":
{
    "LogService": [["alpha", 29000]],
    "ResourceService": [["alpha", 28000], ["beta", 28000], ["gamma", 28000]],
    "ScoringService": [["alpha", 28500]],
    "Checker": [["alpha", 22000]],
    "EvaluationService": [["alpha", 25000]],
    "Worker": [["beta", 26000], ["beta", 26001], ["beta", 26002], ["beta", 26003], ["gamma", 26000], ["gamma", 26001], ["gamma", 26002], ["gamma", 26003]],
    "ContestWebServer": [["alpha", 21000]],
    "AdminWebServer": [["alpha", 21100]],
    "ProxyService": [["alpha", 28600]],
    "PrintingService": [["alpha", 25123]]
},

"other_services":
{
    "TestFileCacher": [["alpha", 27501]]
},

We replaced all instances of localhost with alpha . We did not put any workers on alpha for fairness.

cms.ranking.conf

Using a text editor we modified file config/cms.ranking.conf. The changes we did were to set

    "username":   "usern4me",
    "password":   "passw0rd",

to the values we set in the file cms.conf at the section rankings. In our example the values would be myUsern4me and myPassw0rd.

Setting up the system

After we were done with the configuration changes, we executed the following ./setup.py build so that the system would perform the basic checks and setup. Then, we executed sudo ./setup.py install to make the installation.
Following, we added the user we will use to start the competition system to the user group of the cms sudo usermod -a -G cmsuser george.

And we switched to the DB user to take the necessary actions.

Database Configuration

sudo su - postgres to switch to the DB user.
We created the DB user we defined in the cms.conf file before (myuser) with the password we defined using the command createuser myuser -P, the password in our case is myPassword.
After that, we created our database (mydatabase) and we assigned our user to be the owner with full access rights of that database using the following commands: createdb -O myuser mydatabase then psql mydatabase -c 'ALTER SCHEMA public OWNER TO myuser' finally psql mydatabase -c 'GRANT SELECT ON pg_largeobject TO myuser'.

Allowing connections from other machines

We needed to find the hba file for psql to allow incoming connections from other machines. We got the location of the file using psql -t -P format=unaligned -c 'show hba_file';. In our case the location was /etc/postgresql/9.3/main/pg_hba.conf. Using a text editor we added at the end of the file the following line:

host  mydatabase  myuser  0.0.0.0/0  md5

The above line instructs the system to allow our user to connect from any IP to our database.
For better security, you can use the following lines instead:

host  mydatabase  myuser  beta  md5
host  mydatabase  myuser  gamma  md5

Then we used psql -t -P format=unaligned -c 'show config_file'; which gave use the file /etc/postgresql/9.3/main/postgresql.conf, using a text editor we changed #listen_addresses = 'localhost' to listen_addresses = '*' to enable listening on all network devices.

On the terminal, we exited the console of the database user, we re-installed the cms using sudo ./setup.py install to create all database tables and we rebooted the machine using sudo reboot to apply group changes to the user as well.

Once the system reboots, execute cmsInitDB to create the tables for our database.

Create a file named updateHosts.sh and copy the following code in it. We will use it to update the host file of the machine so that it will identify the external IP of the machine first.

#!/bin/bash

FILE="/etc/hosts";
BACKUP="$FILE.original";

if [ -f "$BACKUP" ]; then
	echo "--- --- --- BackUp exists --- --- ---";
else
	cp "$FILE" "$BACKUP";
	if [ $? -ne 0 ]; then
		echo -e "--- --- --- Could not create backUp. Terminating! --- --- ---";
		exit -1;
	fi
	echo "--- --- --- BackUp Created --- --- ---";
fi

echo "--- --- --- Original '$FILE' --- --- ---";
cat "$BACKUP";

DEFAULT_DEVICE="p7p1";
DEVICE=${1:-$DEFAULT_DEVICE};
IP=`ifconfig "$DEVICE" | grep "inet addr" | cut -d ':' -f 2 | cut -d ' ' -f 1`;
HOST=`hostname`;

> "$FILE";
while read LINE; do
	if [[ "$LINE" == *"$HOST"* ]]; then
		echo -ne "$IP\t" >> "$FILE";
		echo "$LINE" | cut -f2- >> "$FILE";
	else
		echo "$LINE" >> "$FILE";
	fi
done < "$BACKUP";

echo "--- --- --- Updated '$FILE' --- --- ---";
cat "$FILE";

exit 0;

Execute chmod +x updateHosts.sh to make the script into an executable one.

The script needs to know the name of the network device it is supposed to tamper with. Type ifconfig in your terminal to find the available device names. The one you most likely are interested in would have an IP similar to the one of your PC or smartphone e.g. inet addr:192.168.10.5. If the device is an ethernet device, then you will find the following next to the device name Link encap:Ethernet. The name you need to copy could be eth0 or p7p1 or something similar to these.

Once you get the name execute sudo ./updateHosts.sh DEVICE_NAME and replace DEVICE_NAME with the name you copied in the previous step.

Starting the system on Alpha:

In a terminal execute cmsAdminWebServer 0 to start the administration server and use it to create a new contest from http://alpha:8889/.

Once you created you competition from http://alpha:8889/, open up 3 terminals to alpha.

On the first one execute cmsLogService 0 to start the logging service.

On the second execute cmsRankingWebServer to start the Ranking Server that can be reached at http://alpha:8890/.

And on the third one cmsResourceService 0 -a 1 to start the competition we just created along with all services that are needed for the competition from alpha. The competition can be reached at http://alpha:8888/.

Setting up the live replication of the database:

Switch to the postgres user sudo su - postgres and create an ssh key ssh-keygen.
Copy on both servers the key you just created using ssh-copy-id [email protected] and ssh-copy-id [email protected].

Next we will create a user called replicator that can be used solely for the replication process and set a random password of it:

psql -c "CREATE USER replicator REPLICATION LOGIN CONNECTION LIMIT -1 ENCRYPTED PASSWORD 'randomPassword';"

Then edit the file /etc/postgresql/9.3/main/pg_hba.conf (we found the location of pg_hba.conf using the command psql -t -P format=unaligned -c 'show hba_file';) and add the following lines:

host    replication     replicator     beta   md5
host    replication     replicator     gamma   md5

This will allow user replicator to connect from the machines beta and gamma.
You could give the following line, instead of the two above:

host    replication     replicator     0.0.0.0/0   md5

This will allow user replicator to connect from ANY IP.
Also, if you know from beforehand the IPs that beta and gamma are going to have, it is best to replace the above with two lines containing the IPs of the two machines as it is more secure.

host    replication     replicator     IP_address_of_beta/32   md5
host    replication     replicator     IP_address_of_gamma/32   md5

Following, using psql -t -P format=unaligned -c 'show config_file'; we got the path to the file postgresql.conf (/etc/postgresql/9.3/main/postgresql.conf) which we edited using a text editor. The following changes were made to the file:

  • We uncommented and changed #wal_level = minimal to wal_level = hot_standby
  • We uncommented and changed #archive_mode = off to archive_mode = on
  • We uncommented and changed #archive_command = '' to archive_command = 'cd .'
  • We uncommented and changed #max_wal_senders = 0 to max_wal_senders = 10
  • We uncommented and changed #hot_standby = off to hot_standby = on

Save the file and execute service postgresql restart to apply the changes.

Execute psql -c "ALTER USER replicator WITH CONNECTION LIMIT -1"; to remove the limit of connections for our replication user.
You can verify the settings using psql -c "SELECT rolname, rolconnlimit FROM pg_roles";, this command will show you the connection limits per user. If the value is -1 then it means that there is no restriction.

On Beta and Gamma:

We installed various packages that are needed for the installation of the system, we intended in using these machines both as the workers for the competition but also as live backups in case alpha goes down. Later on, we will enable live copying of the database in alpha to these machines.

sudo apt-get install build-essential fpc postgresql postgresql-client gettext python2.7 python-setuptools python-tornado python-psycopg2 python-sqlalchemy python-psutil python-netifaces python-crypto python-tz python-six iso-codes shared-mime-info stl-manual python-beautifulsoup python-mechanize python-coverage python-mock cgroup-lite python-requests python-werkzeug python-gevent patool;

We switched to the postgres user sudo su - postgres and we stopped the postgres service service postgresql stop.

Then edit the file /etc/postgresql/9.3/main/pg_hba.conf (we found the location of pg_hba.conf using the command psql -t -P format=unaligned -c 'show hba_file';) and add the following line:

host    replication     replicator     alpha  md5

Following, using psql -t -P format=unaligned -c 'show config_file'; we got the path to the file postgresql.conf (/etc/postgresql/9.3/main/postgresql.conf) which we edited using a text editor. The following changes were made to the file:

  • We uncommented and changed #listen_addresses = 'localhost' to listen_addresses = 'localhost,beta' for beta and to listen_addresses = 'localhost,gamma' for gamma
  • We uncommented and changed #wal_level = minimal to wal_level = hot_standby
  • We uncommented and changed #archive_mode = off to archive_mode = on
  • We uncommented and changed #archive_command = '' to archive_command = 'cd .'
  • We uncommented and changed #max_wal_senders = 0 to max_wal_senders = 1
  • We uncommented and changed #hot_standby = off to hot_standby = on

Execute pg_basebackup -h alpha -D /var/lib/postgresql/9.3/main/ -U replicator -v -P --xlog-method=stream to copy the database from Alpha. If you get the error pg_basebackup: directory "/var/lib/postgresql/9.3/main/" exists but is not empty then delete the contents of the folder with the command rm -rf /var/lib/postgresql/9.3/main/* and try again. You will be prompted for a password, use the random password we assigned the user replicator before. In this scenario it will be randomPassword.

Afterwards, create the file /var/lib/postgresql/9.3/main/recovery.conf and add the following content:

standby_mode = 'on'
primary_conninfo = 'host=alpha port=5432 user=replicator password=randomPassword sslmode=require'
trigger_file = '/tmp/postgresql.trigger'

Finally, start the service using service postgresql start and exit to stop using the postgres user.

Starting the workers on beta and gamma:

First, we downloaded the CMS:

#Download stable version from GitHub
wget https://github.com/cms-dev/cms/releases/download/v1.2.0/v1.2.0.tar.gz
#Extract the archive
tar -xf v1.2.0.tar.gz

Then, we copied the configuration files from alpha. Specifically, we copied the configuration files from alpha to 'cms/config/cms.conf' and 'cms/config/cms.ranking.conf' using the following commands:

scp [email protected]:~/cms/config/cms.conf ~/cms/config/cms.conf
scp [email protected]:~/cms/config/cms.ranking.conf ~/cms/config/cms.ranking.conf

Afterwards, we entered the CMS folder cd cms, build it ./setup.py build and installed it sudo ./setup.py install.
Following, we added the user we will use to start the competition system to the user group of the cms sudo usermod -a -G cmsuser george. We rebooted the machine using sudo reboot to apply all changes including the group change to the user.

Then we copied the updateHosts.sh file from alpha using scp [email protected]:~/updateHosts.sh ~/updateHosts.sh, executed chmod +x updateHosts.sh to make the script into an executable one. And, we executed sudo ./updateHosts.sh DEVICE_NAME. Like before you need to replace DEVICE_NAME with the network device name that will be used for the communication.

To start the workers, execute cmsResourceService 1 -a 1 on beta and cmsResourceService 2 -a 1 on gamma.

Next steps

In case you have DNS problems, modify the file /etc/hosts and add the following entries to it:

10.1.10.3       alpha
10.1.10.4       beta
10.1.10.5       gamma

In the end it should look something similar to this:

127.0.0.1    localhost
10.1.10.3    alpha
10.1.10.4    beta
10.1.10.5    gamma

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Internship Opportunities – 6 Weeks IT Professionals – IEEE2016

Reference Number: IEEE2016

XM is a trading name of Trading Point Holdings Ltd, a regulated next-generation financial services provider of online trading, with services to clients from over 196 countries worldwide. Founded in 2009 by market experts, XM has reached international recognition and has established a solid market presence on a global scale by virtue of its business ethics and high-quality services.

ic_launcherAs the corporate reputation of XM is built on customer loyalty, our mission is to ensure a secure and advanced trading environment for clients at all times. To achieve this, we hire highly talented professionals, who share our work morale and enthusiasm for success that made XM to what it is today.

Do you feel you have what it takes to be one of us? Our summer internships may just be the perfect opportunity for you to get a taste of this dynamic business. Join us, learn from us develop your skills with us and experience how you can rise to real challenges that shape your future. You may even take a great step forward and gain an opportunity to stay with us full time!

Your Benefits:

  • A unique opportunity to work with a world-leading broker
  • Gain a deep insight into how our company functions
  • Gain valuable experience as a member of our IT team
  • Work with the latest technology and learn from true professionals
  • See how your creative mind can bring value to our business

Main Requirements:

  • Final year students in Computer Science, Engineering or any other relevant degree (first or upper second class honours)
  • Very good working knowledge of Java / C++ / C# or PHP
  • Proactive, sharp and efficient
  • Excellent written and verbal skills in English
  • Eligible to work in Cyprus

Salary: € 480 for 6 weeks

Working hours: 09:00 – 13:00 & 14:00 – 18:00

Location: Limassol, Cyprus

Apply now!

Send your CV and academic transcripts to [email protected], quoting the above job reference number by Friday, 10th June 2016.

Download: Internship Opportunities – 6 Weeks IT Professionals – IEEE2016 (PDF) (199 downloads)


WPML: How to add the Languages’s list in a WordPress menu

The code in this article can be used to create a WPML language switcher that will be attached at the bottom of a specific WordPress menu. Each Language will be a sub-menu item of a new menu item named Languages.

Menu Languages Options

You should add this code to your theme’s functions.php file. It is highly recommended to add this change to the functions.php of child theme.

Choosing which languages to show

By default, the following code will be showing ALL available languages, including the currently active one. We did that as we only have two languages in our website and it would weird to: (a) have only one sub-menu item in the Languages menu item or (b) remove the Languages menu item and place the inactive language item directly on the menu.

To show only the inactive languages, change the line that contains $showActive = true; to $showActive = false;.

Choosing which menu to show the languages on

To choose the menu on which you want the languages to appear in, you need to set the value of $themeLocation = 'main'; to the theme location that the menu has in your current theme.

For the default WordPress themes (Twenty Sixteen, Twenty Fifteen, etc.) setting the value to primary as follows $themeLocation = 'primary'; will add the languages to the main menu.

For the customizr theme (this also works for the pro version),  setting the value to main as follows $themeLocation = 'main'; will add the languages to the main menu.

For other themes, you need to find the name of the menu you want to append the languages to. To do so change the line $showMenuName = false; to $showMenuName = true;. This will print at the beginning of your menu the location of the menu inside the theme using a <pre> tag. Copy that value to the $themeLocation variable, change the $showMenuName back to $showMenuName = false; and you are done.

Menu Location

/* Add languages menu to main menu */
add_filter('wp_nav_menu_items', 'new_nav_menu_items', 10, 2);
function new_nav_menu_items($items, $args) {
    
    $showActive = true;
    $themeLocation = 'main';
    $showMenuName = false;

    if ($showMenuName) {
        echo "<pre>"; print_r(get_object_vars($args)['theme_location']); echo "</pre>";
    }

    // get languages
    $languages = apply_filters( 'wpml_active_languages', NULL, 'skip_missing=0' );

    if ( $languages && $args->theme_location == $themeLocation) {

        if(!empty($languages)){

            $items = $items . '<br/><li class="menu-item">Languages';
            $items = $items . '<ul class="dropdown-menu">';
            foreach($languages as $l){

                if(!$l['active'] || $showActive){
                    $items = $items . '<li class="menu-item"><a href="' . $l['url'] . '"><img src="' . $l['country_flag_url'] . '" height="12" alt="' . $l['language_code'] . '" width="18" /> ' . $l['native_name'] . '</a></li>';
                }
            }
            $items = $items . '</li></ul>';
        }
    }

    return $items;
}

The above code was generated using the documentation examples of WPML.


Skype: Disable notifications for a specific group chat without leaving the group nor changing the status of the account

In cases where you are part of a Skype group chat and you do not want to receive notifications for the messages that arrive you can mute most of the notifications without leaving the group and without changing your account status to Do not disturb.

Method A

Use the Skype chat command /alertsoff that will disable message notifications. To use the command, type in the chat window /alertsoff and press Enter.

Method B

Right click on the group chat name or avatar, select View Profile and in the popup window uncheck the option Notify me when something new happens. This method will have the same effect as Method A.

Note

Both methods will disable message alerts, notifications for incoming files and pictures will still be displayed.

To enable the message notifications

Reverse Method A

Use the Skype chat command /alertson that will enable message notifications. To use the command, type in the chat window /alertson and press Enter.

Reverse Method B

Right click on the group chat name or avatar, select View Profile and in the popup window check the option Notify me when something new happens. This method will have the same effect as reversing Method A.

Additional info for Method A reversal

If you do not want to enable all message notifications but you would like to be informed when specific buzz words appear in the chat you could enable notifications only for those words.

For example, if you are interested in getting notified when the word Goal appears in the chat, type in the chat window /alertson Goal. Any other chat message will be ignored while the messages containing the buzz word Goal will create notifications.


Windows: Kill a service that Windows Task Manager does not stop

In some cases, Windows Task Manager cannot stop a certain service. When the user right clicks on the service and selects the Stop Process option, the status of the service becomes Stopping but never actually stops. This happens when the developer of the service did not handle properly the signal instructing the service to shut down properly.

A way to stop it by force is to use the taskkill command with the help of the task manager. Press at the same time the keyboard keys Ctrl+Shift+Esc this will show the task manager. In the new window, switch to the tab Services. Find the service you want to stop and copy from the column PID the process ID number of the service. In the following example, if we wanted to kill the WinVNC4 service we would copy the number 1828.

TaskKill-Manager

Then, press at the same time the keyboard keys Windows+R that will pop up the Run Command screen. In the input line type the following:

taskkill /pid PID

and replace the PID with the PID we copied from the task manager before. In our example the input will become:

taskkill /pid 1828

and then hit the OK button.

Run-TaskKill


Windows: How to start “Remote Desktop Connection” without the icon shortcut

On a Windows machine we were using recently, all shortcuts icons to system applications got corrupt and they were not linking to the applications they were supposed to link to.

We needed to use Microsoft Remote Desktop Connection without the shortcuts icons. To do so we used the shortcut key of the Run Command screen that allows us to start any application that Windows knows where the binary is.

We pressed the keys Windows+R on the keyboard together and that showed the run command screen. In the input line we typed mstsc and hit the Enter button (you can also click on the OK button on the screen).

Run-mstsc

mstsc is the name that the executable file of Microsoft Remote Desktop Connection application holds in C:\Windows\System32. The easiest way to find the name of a binary is to check the icon shortcut on a machine that it is not corrupted or just search the internet for it.

Using all monitors

If you have multiple monitors that are arranged to form a rectangle, you can instruct mstsc by using the /span parameter to match the Remote Desktop width and height with the local virtual desktop and span across multiple monitors if necessary.

Executing mstsc /span will create a bigger window to the remote machine that will cover all your local monitors. Unfortunately, this window will not operate as if it is composed of multiple monitors. If you set a window to full screen, it will fill ALL local monitors as if they are one.


WordPress: Google AdSense Plugin 3

Recently, we were trying to changing the position of the Google Ads that are shown on a website using the Google AdSense plugin for WordPress.

After clicking on the Manage Ads button at the configuration page (http://example.com/wp-admin/options-general.php?page=GooglePublisherPlugin) we would get an error that another theme had set the active ads and that the plugin cannot proceed to modify them unless they are disabled from the other theme (Specifically we would get the following error: Due to a theme change, ads are not being shown. Please reconfigure your ads or restore the previous theme.).

(a) Disabling the plugin, (b) Uninstalling the plugin (c) Reconnecting with another AdSense account did not help at all.

What we did at the end was to remove the GooglePublisherPlugin entry from the wp_option database of our installation.

DELETE FROM `MyDatabase`.`wp_options` WHERE `option_name` = "GooglePublisherPlugin";

Next time we visited the configuration page, we were disconnected from the previous account and we had to reconnect.

GetStarted-AdSense
After reconnecting, we followed the configuration procedure, we pressed the Manage Ads button, the website was analyzed, we got the confirmation message and we clicked on the Save & Activate button to store the changes.

For a few hours, no ads were shown on the site. We did not take any action and on the next day that ads started appearing as expected.


Java: Create a lookup table for an Enum 4

Simple Enum

Assuming the user has a simple enum that does not have any member variable as follows:

public enum Grades {
    A, B, C, D, E, F
}

In this case, we do not need to create a map to get an enum value from an input string. We can use the enum build-in method valueOf which returns the enum constant of the specified enum type with the specified name.

Please note that the input string must match exactly an identifier used to declare an enum constant in this type. Extraneous white-space characters or wrong letter case are not permitted.

An Enum that has a member variable

Switching to a more interesting example, we assume the user has an enum type that each enum constant has a member variable. In the following example each constant has an int value as member.

public enum Weekday {

    MONDAY(1),
    TUESDAY(2),
    WEDNESDAY(3),
    THURSDAY(4),
    FRIDAY(5),
    SATURDAY(6),
    SUNDAY(7);

    public int getCode() {

        return mCode;
    }

    Weekday(final int code) {

        mCode = code;
    }

    private final int mCode;
}

In this example, getting the matching enum constant for an input integer number is not as straight forward as before. We could iterate over all constants check their code and return the one that matches but that would not be efficient, especially for enum types that have many constants.

What we propose is to create a static map of elements that will have on the key part the value of the enum constant and on the value part the actual enum constant.

public enum Weekday {

    MONDAY(1),
    TUESDAY(2),
    WEDNESDAY(3),
    THURSDAY(4),
    FRIDAY(5),
    SATURDAY(6),
    SUNDAY(7);

    public int getCode() {

        return mCode;
    }

    public static Weekday fromCode(final int code) {

        final Weekday element = sMap.get(code);
        if (element != null) {

            return element;
        }
        throw new IllegalArgumentException(String.format("Unknown code: <%d> for %s", code, Weekday.class.toString()));
    }

    Weekday(final int code) {

        mCode = code;
    }

    private static final Map<Integer, Weekday> sMap = new HashMap<>();

    static {
        for (final Weekday d : Weekday.values()) {

            sMap.put(d.getCode(), d);
        }
    }
    private final int mCode;
}

What we did in the above code, was to statically create the map we described. Which is, a map from the value that each enum constant holds to the constant. Then using the fromCode method we defined, we check the map for the input value, if the value is not part of the keys set of the map, it will throw an Illegal Argument Exception.

Doing Even better

An improvement we could do in the above code, would be to extract the part of the code that iterates over the fields and creates the map to reuse it. To do that, we need to use Functional Interface. Meaning we will create a function that takes as input another function that expects some input (if any) and produces as output an element of the type of the key.

Our function that can be used for all enums that their constants hold an int value is the following:

//ToIntFunction represents a function that produces an int-valued result. This is the int-producing primitive specialization for Function. This is a functional interface whose functional method is applyAsInt(Object).
public static <E extends Enum<E>> Map<Integer, E> createMap(final ToIntFunction<E> converter, final Class<E> enumClass) {

    final Map<Integer, E> map = new HashMap<>();
    for (final E s : enumClass.getEnumConstants()) {

        map.put(converter.applyAsInt(s), s);
    }
    return map;
}

The code of the enum could change to the following:

public enum Weekday {

    MONDAY(1),
    TUESDAY(2),
    WEDNESDAY(3),
    THURSDAY(4),
    FRIDAY(5),
    SATURDAY(6),
    SUNDAY(7);

    public int getCode() {

        return mCode;
    }

    public static Weekday fromCode(final int code) {

        final Weekday element = sMap.get(code);
        if (element != null) {

            return element;
        }
        throw new IllegalArgumentException(String.format("Unknown code: <%d> for %s", code, Weekday.class.toString()));
    }

    Weekday(final int code) {

        mCode = code;
    }

    private static final Map<Integer, Weekday> sMap = Collections.unmodifiableMap(createMap(Weekday::getCode, Weekday.class));

    private final int mCode;
}

The function that we used as input to the new function is the getCode we defined in the enum. As you can see, it is a function that produces an int-valued result just as the requirement of the ToIntFunction parameter asks for.


Java: Breakdown a long number to the powers of two it is composed from 1

The following function will accept a long number as input and will produce an LongStream, which is a sequence of primitive long-valued elements that supports sequential and parallel aggregate operations.

public static LongStream splitToPowersOfTwo(final long input) {

    long temp = input;
    final LongStream.Builder builder = LongStream.builder();

    while (temp != 0) {

        //Long.lowestOneBit: Finds the lowest-order ("rightmost") one-bit in the specified long value and returns a long number that has only the bit in the previously matched position set as 1. This value is a power of two that is one of the components of the number. If the number is zero, the function returns zero.
        final long powerOfTwo = Long.lowestOneBit(temp);
        builder.add(powerOfTwo);
        //We update our temp by subtracting the number we got, our goal is to remove the previously matched bit and get the next one on the next iteration.
        temp = temp & ~ powerOfTwo;
    }

    return builder.build();
}

We used Long.lowestOneBit as it makes it easy for us to breakdown the number to its power of two components by matching for us only one bit at time.

Example of usage:

Scenario: We want to breakdown a long number to the powers of two that their sum produces the number and create a String with those values. Using our function, we can do the following:

final String output = splitToPowersOfTwo(flags).mapToObj(Long::toString).collect(Collectors.joining(", "));

Note: an LongStream is not the same as a Stream<Long>, we used .mapToObj(Long::toString) which calls the static method Long.toString(long). This is different to calling .map(Long::toString) on a Stream<Long> as the latter won’t compile because it is ambiguous.


Java: Breakdown an integer to the powers of two it is composed from 1

The following function will accept an integer number as input and will produce an IntStream, which is a sequence of primitive int-valued elements that supports sequential and parallel aggregate operations.

public static IntStream splitToPowersOfTwo(final int input) {

    int temp = input;
    final IntStream.Builder builder = IntStream.builder();

    while (temp != 0) {

        //Integer.lowestOneBit: Finds the lowest-order ("rightmost") one-bit in the specified int value and returns an int number that has only the bit in the previously matched position set as 1. This value is a power of two that is one of the components of the number. If the number is zero, the function returns zero.
        final int powerOfTwo = Integer.lowestOneBit(temp);
        builder.add(powerOfTwo);
        //We update our temp by subtracting the number we got, our goal is to remove the previously matched bit and get the next one on the next iteration.
        temp = temp & ~ powerOfTwo;
    }

    return builder.build();
}

We used Integer.lowestOneBit as it makes it easy for us to breakdown the number to its power of two components by matching for us only one bit at time.

Example of usage:

Scenario: We want to breakdown an integer to the powers of two that their sum produces the number and create a String with those values. Using our function, we can do the following:

final String output = splitToPowersOfTwo(flags).mapToObj(Integer::toString).collect(Collectors.joining(", "));

Note: an IntStream is not the same as a Stream<Integer>, we used .mapToObj(Integer::toString) which calls the static method Integer.toString(int). This is different to calling .map(Integer::toString) on a Stream<Integer> as the latter won’t compile because it is ambiguous.