Wednesday, August 19, 2015

Useful English learning resources

Please find below the list of resources that I find useful for English learning.

Free Apps to install:
Free online courses:
Free video lessons:
Free English tests:
Website with free learning materials:
Website to improve your writing/spelling skills:
Other websites:
Podcasts:

Wednesday, April 17, 2013

Database and LDAP backup scripts

It is a common task to make backups of database or LDAP on the server. Here are examples of such scripts for Linux.

For MySQL database backup I use this script
#!/bin/sh
export PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin
umask 077

savedays="180"
backupdir="/mnt/backups/db"

now=`date "+%Y%m%d_%H%M"`

if [ ! -d ${backupdir} ] ; then
    echo Creating ${backupdir}
    mkdir -p  ${backupdir}; chmod 700 ${backupdir}; 
fi

mysqldump -hlocalhost -uuser -ppassword --routines database | gzip > ${backupdir}/database_${now}.sql.gz  2>>/var/log/dbbackups.log
find ${backupdir} -name 'database_*' -a -mtime +${savedays} -print -delete
For LDAP backup I use this script:
#!/bin/sh
export PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin
umask 077

savedays="180"
backupdir="/mnt/backups/ldap"

now=`date "+%Y%m%d_%H%M"`

if [ ! -d ${backupdir} ] ; then
    echo Creating ${backupdir}
    mkdir -p  ${backupdir}; chmod 700 ${backupdir}; 
fi
        
slapcat > ${backupdir}/ldap.${now}.ldif  2>>/var/log/ldapbackups.log         
find ${backupdir} -name 'ldap_*' -a -mtime +${savedays} -print -delete
These script will save your database and LDAP backups to /mnt/backups/db and /mnt/backups/ldap accordingly. Backup files will be kept for 180 days and then they will be deleted.

To automate backup you can simply place the scripts to the appropriate cron directory (e.g. /etc/cron.daily) and make sure that cron has rights to execute this file and has rights to write to /mnt/backups/db and /mnt/backups/ldap folders.

Friday, April 12, 2013

Getting Search Volume with Google Adwords API (TargetingIdeaService) - new Java library

Google provides an API to get AdWords data, but there is a little amount of examples of its usage. I'll show you simple example how to get demand (search volume) data for specific words using TargetingIdeaService. In previous post I showed how to do it using the old library, in this post we'll use the new library.

Here is the example:
package loader;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.axis.v201302.cm.Language;
import com.google.api.ads.adwords.axis.v201302.cm.Location;
import com.google.api.ads.adwords.axis.v201302.cm.Money;
import com.google.api.ads.adwords.axis.v201302.cm.Paging;
import com.google.api.ads.adwords.axis.v201302.o.Attribute;
import com.google.api.ads.adwords.axis.v201302.o.AttributeType;
import com.google.api.ads.adwords.axis.v201302.o.IdeaType;
import com.google.api.ads.adwords.axis.v201302.o.LanguageSearchParameter;
import com.google.api.ads.adwords.axis.v201302.o.LocationSearchParameter;
import com.google.api.ads.adwords.axis.v201302.o.LongAttribute;
import com.google.api.ads.adwords.axis.v201302.o.MoneyAttribute;
import com.google.api.ads.adwords.axis.v201302.o.RelatedToQuerySearchParameter;
import com.google.api.ads.adwords.axis.v201302.o.RequestType;
import com.google.api.ads.adwords.axis.v201302.o.SearchParameter;
import com.google.api.ads.adwords.axis.v201302.o.StringAttribute;
import com.google.api.ads.adwords.axis.v201302.o.TargetingIdea;
import com.google.api.ads.adwords.axis.v201302.o.TargetingIdeaPage;
import com.google.api.ads.adwords.axis.v201302.o.TargetingIdeaSelector;
import com.google.api.ads.adwords.axis.v201302.o.TargetingIdeaServiceInterface;
import com.google.api.ads.adwords.axis.v201302.o.Type_AttributeMapEntry;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.common.lib.auth.ClientLoginTokens;
import com.google.api.ads.common.lib.conf.ConfigurationLoadException;
import com.google.api.ads.common.lib.exception.ValidationException;
import com.google.api.client.googleapis.auth.clientlogin.ClientLoginResponseException;

public class Adwords {
    public static void main(String[] args) throws ClientLoginResponseException, IOException, ValidationException,
            ConfigurationLoadException {
        String[] locationNames = new String[] { "Paris", "Quebec", "Spain", "Deutschland" };
        String clientLoginToken = new ClientLoginTokens.Builder().forApi(ClientLoginTokens.Api.ADWORDS)
                .fromFile("adwords.properties").build().requestToken();
        AdWordsSession session = new AdWordsSession.Builder().fromFile("adwords.properties")
                .withClientLoginToken(clientLoginToken).build();
        AdWordsServices adWordsServices = new AdWordsServices();
        String[] keywords = getKeywords();

        TargetingIdeaServiceInterface targetingIdeaService = adWordsServices.get(session,
                TargetingIdeaServiceInterface.class);

        TargetingIdeaSelector selector = new TargetingIdeaSelector();

        selector.setRequestType(RequestType.STATS);
        selector.setIdeaType(IdeaType.KEYWORD);

        selector.setRequestedAttributeTypes(new AttributeType[] { AttributeType.KEYWORD_TEXT, AttributeType.SEARCH_VOLUME, AttributeType.AVERAGE_CPC });

        Language language = new Language();
        language.setId(1000L);

        // Countrycodes
        // http://code.google.com/apis/adwords/docs/appendix/countrycodes.html
        Location location = new Location();
        location.setId(2840L);

        RelatedToQuerySearchParameter relatedToQuerySearchParameter = new RelatedToQuerySearchParameter();
        relatedToQuerySearchParameter.setQueries(keywords);

        LocationSearchParameter locationSearchParameter = new LocationSearchParameter();
        locationSearchParameter.setLocations(new Location[] { location });

        LanguageSearchParameter languageSearchParameter = new LanguageSearchParameter();
        languageSearchParameter.setLanguages(new Language[] { language });

        selector.setSearchParameters(new SearchParameter[] { relatedToQuerySearchParameter, locationSearchParameter,
                languageSearchParameter // if not provided locationSearchParameter, languageSearchParameter then result
                                        // is global
        });

        selector.setLocaleCode("US");

        Paging paging = new Paging();
        paging.setStartIndex(0);
        paging.setNumberResults(keywords.length);
        selector.setPaging(paging);

        TargetingIdeaPage page = targetingIdeaService.get(selector);
        if (page.getEntries() != null && page.getEntries().length > 0) {
            for (TargetingIdea targetingIdea : page.getEntries()) {
                Map<AttributeType, Attribute> data = toMap(targetingIdea.getData());
                String kwd = ((StringAttribute) data.get(AttributeType.KEYWORD_TEXT)).getValue();
                Long monthlySearches = ((LongAttribute) data.get(AttributeType.SEARCH_VOLUME)).getValue();
                Money avgCpc = ((MoneyAttribute) data.get(AttributeType.AVERAGE_CPC)).getValue();
                
                System.out.println(kwd + ", " + monthlySearches + ", " + avgCpc.getMicroAmount() / 1000000.0);
            }
        }
    }
    
    public static String[] getKeywords() {
        //Put your keywords here
        return null;
    }
    
    public static Map<AttributeType, Attribute> toMap(Type_AttributeMapEntry[] data) {
        Map<AttributeType, Attribute> result = new HashMap<AttributeType, Attribute>();
        for (Type_AttributeMapEntry entry: data) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;        
    }
}

Thursday, February 28, 2013

User friendly Git revision numbers

As you know Git keeps the number of the revision in SHA1 and if you need to show the revision number somewhere in the application you'll have to look for some better solutions. In this post I will investigate the possibilities to display pretty revision numbers.

As a first approach I'd like to mention Maven Buildnumber plugin. It is designed to get a unique build number for each time you build your project. Here is the example snippet from pom.xml:
...
<build>
 <plugins>
  <plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>buildnumber-maven-plugin</artifactId>
   <version>1.0</version>
   <executions>
       <execution>
    <phase>validate</phase>
    <goals>
        <goal>create</goal>
    </goals>
       </execution>
   </executions>
   <configuration>
       <doCheck>false</doCheck>
       <doUpdate>false</doUpdate>
   </configuration>
  </plugin>
 </plugins>
...
</build>
You'll need also to configure the connection to your git repository:
<scm>
 <connection>scm:git:https://YOUR_CONNECTION</connection>
 <url>scm:git:https://YOUR_URL</url>
 <developerConnection>scm:git:https://YOUR_DEV_CONNECTION</developerConnection>
</scm>
Now you can acces ${buildNumber} within your pom.xml. E.g.:
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-war-plugin</artifactId>
 <version>2.1</version>
 <configuration>
     <webResources>
  ...
     </webResources>
     <archiveClasses>false</archiveClasses>
     <archive>
  <manifest>
      <addClasspath>true</addClasspath>
      <classpathPrefix/>
  </manifest>
  <manifestEntries>
      <Implementation-Revision>${buildNumber}</Implementation-Revision>
      ...
  </manifestEntries>
     </archive>
 </configuration>
</plugin>
But the ${buildNumber} is too long so it would be better to use additional configuration to use only the beginning N characters. So for example if you will set length of ${buildNumber} to 5 you will get not 6b4017bf51257db678c75732d48121a0997dc19e but 6b401. Here is the configuration for pom.xml:
<configuration>
   <shortRevisionLength>5</shortRevisionLength>
</configuration>
To take ${buildNumber} not only within pom.xml you should use Maven Resources plugin filtering or Maven properties plugin with Spring PropertyPlaceholderConfigurer(For details check here).

The second approach for pretty build number is to use git tag and git describe (for details read here). This is the standard solution for the pretty revision number issue, but if you use CI tools such as Jenkins or Hudson I don't recommend giving to such tools write permissions to your repository. But if CI tool does not have write permissions it cannot create tags and git describe will not give you the result that you expected.

The third approach that you can use in Maven if you have configured resource filtering (the command is for Linux, but I think something similar should work for Windows and Mac):
mvn clean install -DbuildNumberEnv=$(git rev-list HEAD | wc -l)
And now you can use ${buildNumberEnv} to get the numeric value of the revision. But be careful with this approach since your local ${buildNumberEnv} may be the same with another developer's one while you'll have different revision list.
And of course you can use some kind of composition of presented approaches.

Sunday, February 17, 2013

Getting Top Search Queries report with Google Webmaster Tools API

Google has a good and free tool that is very useful for search engine optimization analysis. I'm talking about Google Webmaster Tools. It's possible to download the report from Google WMT using API. It's an example how to do it for Top Queries report.

At first you need to download gdata-webmastertools-2.0.jar and gdata-client-1.0.jar. You can take them from http://gdata-java-client.googlecode.com/files/gdata-src.java-1.47.1.zip (there in the lib folder you'll find the jars).

Then you'll need to place them into you PATH. I will use maven. At first I will install these jars:
mvn install:install-file -Dfile=gdata-webmastertools-2.0.jar -DgroupId=com.google.gdata -DartifactId=gdata-webmastertools -Dversion=2.0 -Dpackaging=jar -DgeneratePom=true
mvn install:install-file -Dfile=gdata-client-1.0.jar -DgroupId=com.google.gdata -DartifactId=gdata-client -Dversion=1.0 -Dpackaging=jar -DgeneratePom=true
After this I will add dependencies to my pom.xml:
<dependency>
    <groupId>com.google.gdata</groupId>
    <artifactId>gdata-client</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>com.google.gdata</groupId>
    <artifactId>gdata-webmastertools</artifactId>
    <version>2.0</version>
</dependency>
Also we'll need to parse JSON. I will use Jackson library:
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.8.5</version>
</dependency>
And finally we are ready to get the report from Google WMT API. (In this example I also used Apache Commons and log4j but it's not necessary, so you can get rid off those dependencies)
package loader;

import com.google.gdata.client.Service.GDataRequest;
import com.google.gdata.client.Service.GDataRequest.RequestType;
import com.google.gdata.client.webmastertools.WebmasterToolsService;
import com.google.gdata.data.OutOfLineContent;
import com.google.gdata.data.webmastertools.SitesEntry;
import com.google.gdata.data.webmastertools.SitesFeed;
import com.google.gdata.util.*;
import org.apache.log4j.Logger;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import org.apache.commons.lang.time.DateUtils;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;

public class GoogleWMTClient {
    private final static Logger LOGGER = Logger.getLogger(GoogleWMTClient.class);

    private final static ArrayList<String> STATISTIC_TYPE = new ArrayList<String>();
    static {
        STATISTIC_TYPE.add("ALL");
        // STATISTIC_TYPE.add("WEB");
        // STATISTIC_TYPE.add("IMAGE");
        // STATISTIC_TYPE.add("VIDEO");
        // STATISTIC_TYPE.add("MOBILE_SMARTPHONE");
        // STATISTIC_TYPE.add("MOBILE_RESTRICT");
    }
    private final static String GOOGLE_HOST = "www.google.com";
    private final static String DOWNLOAD_LIST_URL_PART = "/webmasters/tools/downloads-list?hl=%s&siteUrl=%s";
    private final static String SITES_FEED_URL_PART = "/webmasters/tools/feeds/sites/";    
    private final static String DATE_FORMAT = "yyyyMMdd";
    private final static String APPLICATION_NAME = "JavaDevTips";


    public static void main(String[] args) {
        pullData(Constants.GWMT_URL, Constants.GWMT_LANGUAGE_CODE, new Date());
    }
    
    public static void pullData(String url, String languageCode, Date endDate) {
        LOGGER.info("Download GWMT data for endDate: " + endDate + " and url: " + url);
        try {
            WebmasterToolsService service = initService(Constants.ADWORDS_USER, Constants.ADWORDS_PASSWORD);
            // used for deletion of newly created SitesEntry
            boolean newEntry = false;
            SitesEntry entry = findSitesEntry(service, url);
            if (entry == null) {
                newEntry = true;
                try {
                    entry = insertSiteEntry(service, url);
                } catch (ServiceForbiddenException ex) {
                    LOGGER.error(ex, ex);
                }
            }
            downloadReports(service, entry, endDate, languageCode);

            if (newEntry) {
                deleteSiteEntry(service, url);
            }
        } catch (ServiceException e) {
            LOGGER.error(e, e);
        } catch (IOException e) {
            LOGGER.error(e, e);
        }
    }

    public static WebmasterToolsService initService(String userName, String password) throws AuthenticationException {
        WebmasterToolsService service = new WebmasterToolsService(APPLICATION_NAME);
        service.setUserCredentials(userName, password);
        return service;
    }

    private static SitesEntry findSitesEntry(WebmasterToolsService service, String siteUrl) throws IOException,
            ServiceException {
        siteUrl = correctSiteUrl(siteUrl);
        LOGGER.info("Trying to find SitesEntry for " + siteUrl);
        SitesFeed sitesResultFeed = service.getFeed(getGoogleUrl(SITES_FEED_URL_PART), SitesFeed.class);
        for (SitesEntry entry : sitesResultFeed.getEntries()) {
            if (entry.getTitle().getPlainText().equals(siteUrl)) {
                LOGGER.info("SitesEntry is found");
                return entry;
            }
        }
        LOGGER.info("SitesEntry for " + siteUrl + " not found");
        return null;
    }

    private static URL getGoogleUrl(String path) throws MalformedURLException {
        return new URL("https://" + GOOGLE_HOST + path);
    }

    private static String correctSiteUrl(String siteUrl) {
        siteUrl = siteUrl.trim();
        if (!siteUrl.endsWith("/")) {
            siteUrl += "/";
        }
        if (!siteUrl.startsWith("http")) {
            siteUrl = "http://" + siteUrl;
        }
        return siteUrl;
    }

    private static void downloadReports(WebmasterToolsService service, SitesEntry entry, Date endDate,
            String languageCode) throws IOException, ServiceException {
        LOGGER.info("Downloading reports for " + entry.getTitle().getPlainText());
        Date startDate = DateUtils.addDays(endDate, (-1) * Constants.DATA_PERIOD);
        ObjectMapper mapper = new ObjectMapper();
        InputStream inputStream = getQueryInputStream(service, entry, languageCode);
        if (inputStream == null) {
            LOGGER.error("Empty InputStream");
            return;
        }
        Map<String, Object> map = mapper.readValue(inputStream, new TypeReference<Map<String, Object>>() {
        });
        if (map != null) {
            String fileName = null;
            SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT);            
            for (String prop : STATISTIC_TYPE) {
                StringBuilder sbPath = new StringBuilder((String) map.get("TOP_QUERIES")).append("&prop=" + prop)
                        .append("&db=" + sdf.format(startDate))
                        .append("&de=" + sdf.format(endDate));

                fileName = "gwmt_" + sdf.format(endDate) + prop + ".csv" ;
                OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8");
                boolean hasData = downloadData(service, sbPath.toString(), out);
                if (!hasData) {
                    LOGGER.info("File contains no data. Deleting.");
                    new File(fileName).delete(); // if the file contain no data we delete it
                }
                out.close();
            }
        }
    }

    private static boolean downloadData(WebmasterToolsService service, String path, OutputStreamWriter out)
            throws IOException, ServiceException {
        LOGGER.info("Downloading data for " + path);
        String data;
        URL url = getGoogleUrl(path);
        GDataRequest req = service.createRequest(RequestType.QUERY, url, ContentType.TEXT_PLAIN);
        req.execute();
        BufferedReader in = new BufferedReader(new InputStreamReader(req.getResponseStream()));
        if (in.readLine() != null) {
            while ((data = in.readLine()) != null) {
                out.write(data + "\n");
            }
            return true;
        } else {
            return false;
        }
    }

    private static InputStream getQueryInputStream(WebmasterToolsService service, SitesEntry entry, String lang)
            throws IOException, ServiceException {
        URL url = getGoogleUrl(String.format(DOWNLOAD_LIST_URL_PART, lang, entry.getTitle().getPlainText()));
        GDataRequest req = service.createRequest(RequestType.QUERY, url, ContentType.JSON);
        try {
            req.execute();
            return req.getResponseStream();
        } catch (RedirectRequiredException e) {
            LOGGER.error(e, e);
        }
        return null;
    }

    private static SitesEntry insertSiteEntry(WebmasterToolsService myService, String siteUrl) throws IOException,
            ServiceException {
        siteUrl = correctSiteUrl(siteUrl);
        SitesEntry entry = new SitesEntry();
        OutOfLineContent content = new OutOfLineContent();
        content.setUri(siteUrl);
        entry.setContent(content);
        LOGGER.info("Adding SitesEntry for  " + siteUrl);
        return myService.insert(getGoogleUrl(SITES_FEED_URL_PART), entry);
    }

    private static void deleteSiteEntry(WebmasterToolsService myService, String siteUrl) throws IOException,
            ServiceException {
        siteUrl = correctSiteUrl(siteUrl);
        String siteId = URLEncoder.encode(siteUrl, "UTF-8");
        URL feedUrl = new URL(getGoogleUrl(SITES_FEED_URL_PART) + siteId);
        SitesEntry entry = myService.getEntry(feedUrl, SitesEntry.class);
        LOGGER.info("Deleting SitesEntry for " + siteUrl);
        entry.delete();
    }
}

Getting Search Volume with Google Adwords API (TargetingIdeaService) - old Java library

Google provides an API to get AdWords data, but there is a little amount of examples of its usage. I'll show you simple example how to get demand (search volume) data for specific words using TargetingIdeaService. This is example of old library, see the example of usage the new library.

Here is the example:
package loader;

import java.io.IOException;
import java.util.Map;

import javax.xml.rpc.ServiceException;

import common.Constants;

import com.google.api.adwords.lib.AdWordsService;
import com.google.api.adwords.lib.AdWordsUser;
import com.google.api.adwords.lib.AuthToken;
import com.google.api.adwords.lib.AuthTokenException;
import com.google.api.adwords.lib.utils.MapUtils;
import com.google.api.adwords.v201209.cm.Language;
import com.google.api.adwords.v201209.cm.Location;
import com.google.api.adwords.v201209.cm.Paging;
import com.google.api.adwords.v201209.o.Attribute;
import com.google.api.adwords.v201209.o.AttributeType;
import com.google.api.adwords.v201209.o.IdeaType;
import com.google.api.adwords.v201209.o.LanguageSearchParameter;
import com.google.api.adwords.v201209.o.LocationSearchParameter;
import com.google.api.adwords.v201209.o.LongAttribute;
import com.google.api.adwords.v201209.o.RelatedToQuerySearchParameter;
import com.google.api.adwords.v201209.o.RequestType;
import com.google.api.adwords.v201209.o.SearchParameter;
import com.google.api.adwords.v201209.o.StringAttribute;
import com.google.api.adwords.v201209.o.TargetingIdea;
import com.google.api.adwords.v201209.o.TargetingIdeaPage;
import com.google.api.adwords.v201209.o.TargetingIdeaSelector;
import com.google.api.adwords.v201209.o.TargetingIdeaServiceInterface;

public class GoogleAdwordsClient {


    private static AdWordsUser getAdWordsUser() {
        try {
            AdWordsUser user = new AdWordsUser(Constants.ADWORDS_USER, Constants.ADWORDS_PASSWORD, null, null,
                    LoaderConstants.ADWORDS_DEVELOPER_TOKEN, false);
            if (user.getRegisteredAuthToken() == null) {
                user.setAuthToken(new AuthToken(user.getEmail(), user.getPassword()).getAuthToken());
            }
            return user;
        } catch (AuthTokenException e) {
            throw new RuntimeException(e);
        }
    }

    private static String[] getKeywords() {
        //some logic to return array of keywords
    }


    public static void main(String[] args) throws ServiceException, IOException {
        String[] keywords = getKeywords();
        AdWordsUser user = getAdWordsUser();

        TargetingIdeaServiceInterface targetingIdeaService = user
                .getService(AdWordsService.V201209.TARGETING_IDEA_SERVICE);

        TargetingIdeaSelector selector = new TargetingIdeaSelector();
       
       
        selector.setRequestType(RequestType.STATS);
        selector.setIdeaType(IdeaType.KEYWORD);

        selector.setRequestedAttributeTypes(new AttributeType[] {
                AttributeType.KEYWORD_TEXT,
                AttributeType.SEARCH_VOLUME,
        });

        Language language = new Language();
        language.setId(1000L);

        // Countrycodes
        // http://code.google.com/apis/adwords/docs/appendix/countrycodes.html
        Location location = new Location();
        location.setId(2840L);

        RelatedToQuerySearchParameter relatedToQuerySearchParameter = new RelatedToQuerySearchParameter();
        relatedToQuerySearchParameter.setQueries(keywords);
       

        LocationSearchParameter locationSearchParameter = new LocationSearchParameter();
        locationSearchParameter.setLocations(new Location[]{location});
       
        LanguageSearchParameter languageSearchParameter = new LanguageSearchParameter();
        languageSearchParameter.setLanguages(new Language[]{language});
       
        selector.setSearchParameters(new SearchParameter[] { relatedToQuerySearchParameter
                , locationSearchParameter, languageSearchParameter //if not provided locationSearchParameter, languageSearchParameter then result is global
                });

        selector.setLocaleCode("US");

        Paging paging = new Paging();
        paging.setStartIndex(0);
        paging.setNumberResults(keywords.length);
        selector.setPaging(paging);

        TargetingIdeaPage page = targetingIdeaService.get(selector);
        if (page.getEntries() != null && page.getEntries().length > 0) {
            for (TargetingIdea targetingIdea : page.getEntries()) {
                Map<AttributeType, Attribute> data = MapUtils.toMap(targetingIdea.getData());
                String kwd = ((StringAttribute) data.get(AttributeType.KEYWORD_TEXT)).getValue();
                Long monthlySearches = ((LongAttribute) data.get(AttributeType.SEARCH_VOLUME)).getValue();
                
                System.out.println(kwd + ": " + monthlySearches);
            }
        }
    }
}

Saturday, January 12, 2013

Using popular share links

Nowadays almost every application needs connection with some kind of social network. The most common task is to publish something on the user's wall. So today I will show how easy you can share your content with Facebook, Twitter, Google+, Tumblr and VK users.

First let's discuss Twitter and Tumblr. To share something on Twitter from your site all you need is just to add link in the following format:
<a href="http://twitter.com/?status=javadevtips.blogspot.com - Ideas, solutions and tips for Java developer http://javadevtips.blogspot.com" target="_blank">Twitter</a>
For Tumblr:
<a href="http://www.tumblr.com/share/photo?source=http%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2Fthumb%2F3%2F31%2FBlogger.svg%2F256px-Blogger.svg.png&caption=javadevtips.blogspot.com - ideas, solutions and tips for Java developer" target="_blank">Tumblr</a>     
Now let's make it for Facebook. But it won't be that straightforward as with previous ones. Facebook uses Open Graph and we need to provide the link with our page that has right ol meta tags. Here are the meta tags that you'll need:
<meta property="og:url" content="http://javadevtips.blogspot.com"/>
<meta property="og:title" content="Java Dev Tips - ideas, solutions and tips"/>
<meta property="og:image" content="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvXlXbR9AGzfqKWM01TWPNuvGEhTXDOxNlSkb_u_5c1vYhYUJ92dERMkXmLQXQXecsqR-3lgK0udiaB8npunewoV6d3LJMWF2Jvk4T37XIfpZoPEKZDfLtCbIkmer_r0GtICn4JMUBJeUA/s220/243d5e0.jpg"/>
<meta property="og:site_name" content="Java Dev Tips"/>
<meta property="og:description" content="Java Dev Tips - ideas, solutions and tips for Java developer"/>
And we are ready to put the link:
<a href="http://www.facebook.com/sharer.php?u=http://javadevtips.blogspot.com" target="_blank">FB</a>
The same OG meta tags will work for Google+, so here is it's link:
<a href="https://plus.google.com/share?url=http://javadevtips.blogspot.com" target="_blank">Google+</a>
As for VK, it will work with OG meta tags if usual meta tags will not be provided:
<meta name="description" content="Java Dev Tips - ideas, solutions and tips for Java developer" />
And VK link:
<a href="http://vkontakte.ru/share.php?url=http://javadevtips.blogspot.com" target="_blank">VK</a>
Example:

FB | Google+ | VK | Twitter | Tumblr

If you cannot share on Facebook or your shared content is outdated try this solution.

Facebook caches your page and so your updates are not refreshed on FB. You should use Open Graph Debugger to fix it:
  1. Go to http://developers.facebook.com/tools/debug
  2. Enter your URL that has the issue
  3. Press Debug button

OG Debugger also provides information how your URL will be parsed by Open Graph engine. 

Read how to share on different social networks

Friday, January 11, 2013

Whitespaces collapse in Richfaces

It's well known that by default sequence of whitespaces in HTML will collapse to a single whitespace. To fix it it's sufficient to set CSS white-space Property:
white-space:pre-wrap;
But what to do when you use Richafaces (Ajax4jsf) and have set this CSS property correctly but nevertheless whitespaces keep collapsing?

According to RichFaces Developer Guide:
RichFaces uses a filter for a correction of code received on an Ajax request. In case of a "regular" JSF request a browser makes correction independently. In case of Ajax request in order to prevent layout destruction it's needed to use a filter, because a received code could differ from a code validated by a browser and a browser doesn't make any corrections.
Thus Richfaces parser do correct our HTML code, but the parser does not know anything about our CSS and thus here is the cause of the issue.

In Richfaces there are 2 types of parsers:
  1. Tidy is the default parser. It is similar to usual browser parsing. It's the slowest one from Richfaces parsers. On my opinion it should be used only with complicated markup
  2. Neko is less stricted and so it works faster
So to fix our issue it would be sufficient to change our parser from default (Tidy) to Neko. We should add according filter in web.xml:
<context-param>
   <param-name>org.ajax4jsf.xmlparser.ORDER</param-name>
   <param-value>NEKO</param-value>
</context-param>
<context-param>
   <param-name>org.ajax4jsf.xmlparser.NEKO</param-name>
    <param-value>.*\..*</param-value>
</context-param>
If necessary you can configure filters flexibly for different pages (example)

Sunday, December 2, 2012

Cannot run Spring application: BeanDefinitionParsingException

When you try to run Spring based application you can see the exception
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Unable to locate Spring NamespaceHandler for XML schema namespace
The problem may be caused by not having all neccessary Spring jars or by jar conflicts.

First of all check that you have all jars you need. Here is the list of possible jars with explanations for Spring 3.0.5 (the original of this list of dependencies is Spring Blog):
<!-- Shared version number properties -->
<properties>
    <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
</properties>
 
<!--
    Core utilities used by other modules.
    Define this if you use Spring Utility APIs (org.springframework.core.*/org.springframework.util.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Expression Language (depends on spring-core)
    Define this if you use Spring Expression APIs (org.springframework.expression.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Bean Factory and JavaBeans utilities (depends on spring-core)
    Define this if you use Spring Bean APIs (org.springframework.beans.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Aspect Oriented Programming (AOP) Framework (depends on spring-core, spring-beans)
    Define this if you use Spring AOP APIs (org.springframework.aop.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Application Context (depends on spring-core, spring-expression, spring-aop, spring-beans)
    This is the central artifact for Spring's Dependency Injection Container and is generally always defined
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Various Application Context utilities, including EhCache, JavaMail, Quartz, and Freemarker integration
    Define this if you need any of these integrations
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Transaction Management Abstraction (depends on spring-core, spring-beans, spring-aop, spring-context)
    Define this if you use Spring Transactions or DAO Exception Hierarchy
    (org.springframework.transaction.*/org.springframework.dao.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-tx</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, spring-tx)
    Define this if you use Spring's JdbcTemplate API (org.springframework.jdbc.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Object-to-Relation-Mapping (ORM) integration with Hibernate, JPA, and iBatis.
    (depends on spring-core, spring-beans, spring-context, spring-tx)
    Define this if you need ORM (org.springframework.orm.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-orm</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Object-to-XML Mapping (OXM) abstraction and integration with JAXB, JiBX, Castor, XStream, and XML Beans.
    (depends on spring-core, spring-beans, spring-context)
    Define this if you need OXM (org.springframework.oxm.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-oxm</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Web application development utilities applicable to both Servlet and Portlet Environments
    (depends on spring-core, spring-beans, spring-context)
    Define this if you use Spring MVC, or wish to use Struts, JSF, or another web framework with Spring (org.springframework.web.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Spring MVC for Servlet Environments (depends on spring-core, spring-beans, spring-context, spring-web)
    Define this if you use Spring MVC with a Servlet Container such as Apache Tomcat (org.springframework.web.servlet.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Spring MVC for Portlet Environments (depends on spring-core, spring-beans, spring-context, spring-web)
    Define this if you use Spring MVC with a Portlet Container (org.springframework.web.portlet.*)
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc-portlet</artifactId>
  <version>${org.springframework.version}</version>
</dependency>
 
<!--
    Support for testing Spring applications with tools such as JUnit and TestNG
    This artifact is generally always defined with a 'test' scope for the integration testing framework and unit testing stubs
-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
  <version>${org.springframework.version}</version>
  <scope>test</scope>
</dependency>
If this does not help then try Maven Shade plugin to solve the most probable conflicts:
<plugin>
 <groupId>org.apache.maven.plugins</groupId>
 <artifactId>maven-shade-plugin</artifactId>
 <version>1.4</version>
 <executions>
  <execution>
   <phase>package</phase>
   <goals>
    <goal>shade</goal>
   </goals>
   <configuration>
    <transformers>
     <transformer
      implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
      <resource>META-INF/spring.handlers</resource>
     </transformer>
     <transformer
      implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
      <resource>META-INF/spring.schemas</resource>
     </transformer>
    </transformers>
   </configuration>
  </execution>
 </executions>
</plugin>

Work with annotations in Spring Framework

Annotations are a very useful feature in Java programming language and if you use Spring Framework than it can be also very easy to create annotation processor.

I expect you to be familiar with basics of annotations (read here). In this tutorial we'll create annotation for SLF4J. Here is the annotation code:
package uay.log;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {

}
Now let's create annotation processor using Spring Framework capabilities:
package uay.log;

import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;

@Component
public class LogProcessor implements BeanPostProcessor {
 public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    public Object postProcessBeforeInitialization(final Object bean, String beanName)
            throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                if (field.getAnnotation(Log.class) != null) {
                    Logger log = LoggerFactory.getLogger(bean.getClass());
                    field.setAccessible(true);
                    field.set(bean, log);
                    field.setAccessible(false);
                }
            }
        });
        return bean;
    }
}
And it's that easy. Now we can use this annotation in our code:
@Log
Logger log;

Saturday, December 1, 2012

Quickstart with SLF4J

In this tutorial we will configure SLF4J logging with log4j implementation.

SLF4J is a facade of abstractions for various logging frameworks(log4j, java.util.logging, commons logging, logback). It is really easy to swap between logging frameworks when you use SLF4J. Here is what you have to add to your Maven pom.xml to use SLF4J with log4j implementaion:
<properties>
 <slf4j.version>1.6.6</slf4j.version>
</properties>
<dependencies>    
 <!-- Logging -->
 <dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
 </dependency>
 <dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>${slf4j.version}</version>
 </dependency>
 <dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-log4j12</artifactId>
  <version>${slf4j.version}</version>
 </dependency>
</dependencies>
Run
mvn install
Now we need to configure log4j as usual. There are two ways to do it: via xml or via properties file. Let's use properties file. We need to create log4j.properties in the classpath. Here is the code of the file:
# Root logger option
log4j.rootLogger=INFO, file, stdout
 
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern = '.'yyyy-MM-dd
log4j.appender.file.Append = true
log4j.appender.file.File=logs/main.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
 
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{dd-MM-yy HH:mm:ss,SSS} %5p [%c{1}:%M:%L] - %m%n
According to this configuration we defined the lowest log priority to INFO and set the output to Console and to the logs/main.log file(logs/main.log file will be rolled by days automatically).

Now we can test how the logging works:
package uay.test;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class App 
{
    private static Logger log = LoggerFactory.getLogger(App.class);
    public static void main( String[] args )
    {
     log.info("test");
    }
}
If we run the code the Console output would be:
01-12-12 23:12:24,840  INFO [App:main:11] - test
The file output would be:
23:12:24,840  INFO App:11 - test
Thus, we have configured the code to use log4j via SLF4J. As a further improvement of code you should think about using annotations to make the code look like this:
@Log
private Logger log;
This tutorial will definitely help you with it. Later I will show how to implement this annotation even easier using Spring Framework.

Friday, November 30, 2012

Simple analysis and improvement of MySQL schema

Here is some basic analysis and advises on improvement of MySQL database performance. From time to time you should check you MySQL schema status, I use the following request for it:
SELECT                                                                                                                                                                                                                                                                         
    table_name AS table_name,                                                                                                                                                                                                                                                  
    engine,                                                                                                                                                                                                                                                                    
    ROUND(data_length/1024/1024,2) AS total_data_size_mb,                                                                                                                                                                                                                      
    ROUND(index_length/1024/1024,2) AS total_index_size_mb,                                                                                                                                                                                                                    
    ROUND((data_length+index_length)/1024/1024,2) AS total_size_mb,                                                                                                                                                                                                            
    ROUND(data_free/1024/1024,2) AS total_free_size_mb,                                                                                                                                                                                                                        
    ROUND(data_free/(data_length/100)) AS defragment_percent,                                                                                                                                                                                                                  
    table_rows                                                                                                                                                                                                                                                                 
FROM                                                                                                                                                                                                                                                                           
    information_schema.tables                                                                                                                                                                                                                                                  
WHERE                                                                                                                                                                                                                                                                          
    table_schema=DATABASE();
Field defragment_percent value should be 0, but it may be even higher than 100(e.g. you loaded 3MB of data to the table and then deleted 2MB, the table will still be 3MB with real data of 1MB). The most prone to fragmentation are VARCHAR fields so pay attention to the tables where you use VARCHAR a lot. To optimize tables you can use MySQL command:
OPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL] TABLE mi_tabla1 [, mi_tabla2] ...;
To optimize the whole schema you can use:
mysqlcheck -o <databasename>


Field total_index_size_mb should be less than total total_data_size_mb. If it is not so then review your indexes.

Also I'd recommend to use MySQLTuner. MySQLTuner is a script written in Perl that allows you to review a MySQL installation quickly and make adjustments to increase performance and stability. To get it you can use:
wget http://mysqltuner.com/mysqltuner.pl
Then run it with
perl mysqltuner.pl
This tool will provide you with analysis of your MySQL server and some advises on how to improve its performance.

Tuesday, November 27, 2012

Execute scripts in multiple databases

Have you ever needed to execute the same script on several databases? I have met such need and developed a simple algorithm for it. This algorithm I'd like to share.

At first I tried to develop it using only JDBC but unfortunately it has the really big limitation: it cannot execute at once several calls, which is pretty common for SQL scripts. So I decided to run scripts merely from a command line.

Here is the code:
package uay.loader;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;

public class BulkScriptLoader {

    private static final String   DB_PREFIX     = "ua";
    private static final String[] DB_SKIP = { "ua_1", "ua_2" };
    private static final String   FOLDER_NAME   = "scripts";
    private static final String   FILE_EXT      = ".sql";
    private static final String   URL           = HOST + ":" + PORT;
    private static final String   USER_NAME     = "xxx";
    private static final String   PASSWORD      = "xxx";

    private final static Logger   LOGGER        = Logger.getLogger(BulkScriptLoader.class);

    public static void main(String[] args) {
        Connection connection;
        try {
            connection = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
        } catch (SQLException e1) {
            LOGGER.error(e1);
            throw new RuntimeException(e1);
        }

        List<String> dbNames = getDbNames(connection);
        File[] scriptFiles = getScriptFiles();
        for (File file : scriptFiles) {
            LOGGER.info("Work with " + file.getName());
            BufferedReader bf;
            StringBuilder statement = new StringBuilder();
            try {
                bf = new BufferedReader(new FileReader(file));
                String line = "";
                while ((line = bf.readLine()) != null) {
                    statement.append(line).append("\n");
                }
            } catch (IOException e) {
                LOGGER.error(e, e);
            }
            if (statement.toString().length() > 0) {
                execute(connection, dbNames, statement.toString(), URL, USER_NAME, PASSWORD);
            }
        }
    }

    public static File[] getScriptFiles() {
        File folder = new File(FOLDER_NAME);
        if (folder.exists() && folder.isDirectory()) {
            return folder.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.getName().toLowerCase().endsWith(FILE_EXT.toLowerCase());
                }
            });
        }
        return null;
    }

    public static void execute(Connection connection, List<String> dbNames,
            String generalStatement, String url, String userName, String password) {
        for (String dbName : dbNames) {
            String cmdCommand = String.format("mysql -u %s -p%s -h %s -P %s -D %s",userName, password, 
              parseHost(url), parsePort(url), dbName);
            LOGGER.info(cmdCommand);
            try {
                Process process = Runtime.getRuntime().exec(cmdCommand);
                Writer w = new OutputStreamWriter(process.getOutputStream());
                // System.out.println(statement);
                w.write(generalStatement);
                w.flush();
                w.close();
            } catch (IOException e) {
                LOGGER.error(e, e);
            }
        }
    }

    public static String parseHost(String url) {
        return url.substring(0, (url.indexOf(":") > 0 ? url.indexOf(":"): url.length()))

    }

    public static String parsePort(String url) {
        Pattern p = Pattern.compile("(\\d\\d\\d\\d)");
        Matcher m = p.matcher(url);
        if (m.find()) {
            return m.group(0);
        }
        return "3306";
    }

    public static List<String> getDbNames(Connection connection) {
        List<String> dbNames = new ArrayList<String>();
        try {
            PreparedStatement preparedStatement = connection.
               prepareStatement("select schema_name from information_schema.schemata");
            if (preparedStatement.execute()) {
                ResultSet rs = preparedStatement.getResultSet();
                while (rs.next()) {
                    String dbName = rs.getString(1);
                    if (dbName.startsWith(DB_PREFIX) && !Arrays.asList(DB_SKIP).contains(dbName)) {
                        dbNames.add(dbName);
                    }
                }
            }
        } catch (SQLException e) {
            LOGGER.error(e, e);
        }
        return dbNames;
    }
}
As you can see it's really simple. All scripts that I'd like to execute I place in 'scripts' folder with '.sql' extension. I configure database connection, prefix of databases that I want to update and specify the list of databases that I want to skip with this update.

Search and replace text in multiple files

It's very common task to find some text in some files and replace it. Here is my recipe to deal with it

In this example I will find all 'varchar' text in my '.sql' files and replace it with 'char'.
At first let's search for the files that contain the text:
grep -i 'varchar' *.sql
In the output you can see the list of files with corresponding match within it.
Now let's take this list of files and replace the searched text within with the new one:
grep -il 'varchar' *.txt | xargs sed -i 's/varchar/char/gi'
This was a case-insensitive case. But if you need to be case-sensitive then use:
grep -l 'varchar' *.txt | xargs sed -i 's/varchar/char/g'

Monday, November 19, 2012

Exclude SVN directories from Eclipse search

If you perform search in Eclipse IDE and use SVN in your project then probably you have encountered the situation that the search tool looks also in .svn.

To fix this situation you can:
  1. Open Project settings
  2. Select Resoursec / Resource Filter
  3. Click Add... button
  4. Select exclude all, applies to folders, all children
  5. Type .svn
And that is all you need. The next search will no include svn directories.

Monday, September 3, 2012

Never use JBoss Seam version below 2.2.2.Final

Today it will be the small post about very significant security hole in JBoss Seam versions below 2.2.2.Final.

It is possible to execute malware code on your server through Seam application using only browser's address bar. To check the issue add this to your GET parameters(works for Linux):
actionOutcome=/pwn.xhtml?pwned%3d%23{expressions.getClass().forName('java.lang.Runtime').getDeclaredMethods()[6].invoke(expressions.getClass().forName('java.lang.Runtime')).exec('mkdir%20/tmp/pwned')}

This code will create 'pwned' directory in /tmp/ directory.

To close this vulnerability just update your JBoss Seam to 2.2.2.Final.

In this post I used the material of this article.To read more check these links:
  1. JBoss Seam Framework remote code execution
  2. JBoss Seam2 privilege escalation caused by EL interpolation in FacesMessages
  3. Abusing JBOSS
  4. Good Bye Critical Jboss 0day

Friday, August 31, 2012

Debug remote application with Eclipse

When you have remote application with enabled debug it is easy to connect Eclipse IDE and debug the application. Here is the list of steps for it:
  1. Open menu Run, select item Debug Configurations.
  2. In the left area of the appeared window double click on Remote Java Application
  3. Enter all required information about your server. In the screenshot below you can see configurations for the server configured here
  4. Click Apply or Debug

JBoss remote debugging

As all you well know debugging is very important part of the development process. In this post I will show you how to configure your JBoss AS to allow debugging deployed applications.

We need to configure JBoss's bin/run.conf file. Add the following line at the end:
JAVA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n $JAVA_OPTS"

This options mean:
  • -Xdebug asks JBoss to work with debugging support
  • -Xrunjdwp loads JDWP (Java Debug Wire Protocol). This option has its own options:
    1. transport=dt_socket means that sockets will be used for transport
    2. address=8787 means the address(in this case it's the local machine's port 8787) where the socket will be opened
    3. server=y if it is set to 'y' then it means that JBoss will listen for debugger to attach; if it is set to 'n' then it means that JBoss will attach to the debugger at the specified address
    4. suspend=n if it is set to 'y' then it means that JBoss will be launched in the suspended mode and will stay suspended until the debugger is connected

You may also want to check how to debug remote application with Eclipse

Wednesday, August 29, 2012

LDAP Authentication and Search

LDAP is a very widespread way for authentication. In this post I would like to show you how to connect to LDAP server, authenticate user and perform search.

We will use only standard Java classes. So we don't need any dependency. In the example you can see my configurations. Yours may differ (pay attention to Context.SECURITY_PRINCIPAL). Here is the code:
import java.util.Properties;

import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;

public class LdapTest {
    
    public static void processRequest(InitialLdapContext ctx, String userContext, String filter, String attribute) {
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        NamingEnumeration<SearchResult> searchResults;
        try {
            searchResults = ctx.search(userContext, filter, searchControls);
            
            while (searchResults.hasMoreElements()) {
                SearchResult sr = searchResults.next();
                Attributes attributes = sr.getAttributes();
                Attribute a = attributes.get(attribute);
                if (a != null) {
                    String attrValue = (a.get().toString());
                    System.out.println(attrValue);
                } else {
                    System.out.println("Cannot get data");
                }
            }
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
        
    private static InitialLdapContext initialiaseLdapContext(String server, int port, String username, String password, String contextDN) {
        boolean initialised = false; 
        Properties properties = new Properties();
        properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        properties.put(Context.PROVIDER_URL, "ldap://" + server + ":"+ port);
        properties.put(Context.SECURITY_AUTHENTICATION, "simple");
        properties.put(Context.SECURITY_PRINCIPAL, "uid=" + username + "," + contextDN); //YOU MAY NEED TO CHANGE CODE HERE
        properties.put(Context.SECURITY_CREDENTIALS, password);

        InitialLdapContext ctx = null;
        try {
            // Create initial context
            ctx = new InitialLdapContext(properties, null);
            initialised = true;
        } catch (NamingException e) {
            initialised = false;
        } finally {
            if (initialised) {
                System.out.println("Initialization success");
            } else {
                System.out.println("Initialization fail");
            }
        }
        return ctx;
    }

    public static void main(String[] args) {
        String contextDN = "dc=test,dc=com";
        InitialLdapContext ctx = initialiaseLdapContext("localhost", 389, "test@test.com", "test" , contextDN);
        try {
            if (ctx != null) {
                processRequest(ctx, contextDN, "(uid=qwertTest@test.com)", "street");
                ctx.close();
            }
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
}

Fast bulk loading to the database

Whenever you have to insert a big amount of data consider using not usual 'Insert Into', but the load command. For MySQL and Oracle it's 'Load Data Infile'. Here is the small example how to import usual CSV file with the header to MySQL:

load data local infile 'load.csv'
into table someTable
character set utf8 
fields terminated by ',' 
optionally enclosed by '"'
lines terminated by '\n'
ignore 1 lines

For more information view official documentation