Tuesday 29 December 2015

Update Index in Elasticsearch

Whenever there is a change to be done in an indexed document, it is not mandatory to replace the entire document, instead elasticsearch allows the partial update of required field in it.
The create index functionality will completely replace the existing details, whereas the update functionality will allow us to partially change the document.

REST Service to update the indexed document:
POST http://localhost:9200/index_name/type_name/document_Id/_update
{
    "doc":{
        "field_name":"new_value"
    }
}

Java API to update the indexed document:

UpdateRequest updateRequest = new UpdateRequest();
updateRequest.index(“index_Name”);
updateRequest.type(“type_Name”);
updateRequest.id(“document_id”);
Map<String, Object> json = new HashMap<String, Object>();
json.put(“field_name”,”field_Value”);
updateRequest.doc(json);
try {
            client.update(updateRequest).get();
} catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
}

Note :  More than one field can be updated using a single method call.


Thanks !! Meet you soon with next post !!

Sunday 27 December 2015

Delete Index in Elasticsearch

An existing index/type/document in Elasticsearch can be deleted either by using REST service or by using the Java API invocation.
Below are the scripts to delete the index.

Delete complete index :

REST  Service:
$ curl -XDELETE 'http://localhost:9200/<indexname>'

Java API :
client.admin().indices().delete(new DeleteIndexRequest("<indexname>")).actionGet();

Delete a type in index   :

REST Service :
$ curl -XDELETE 'http://localhost:9200/<indexname>/<typename>'

Java API :
client.prepareDelete().setIndex("<indexname>").setType("<typename>").setId("*").execute().actionGet();

Delete a particular document :

REST Service :
$ curl -XDELETE 'http://localhost:9200/<indexname>/<typename>/<documentId>

Java API:
client.prepareDelete().setIndex("<indexname>").setType("<typename>").setId("<documentId>").execute().actionGet();

Note:

The delete operation cannot be rolled back at any point. So make sure to double check the delete command before execution.

Tuesday 22 December 2015

Manage analyzer settings in Elasticsearch index


There are different types of settings that can be done in an Elasticsearch index. One among them is setting up our analyzers for a particular index.
Let us discuss on how to add the list of analyzers to be used by fields in a type.

Add analyzer to index using RESTFul service

PUT  http://localhost:9200/simplyjava/_settings {
      "analysis": {
                 "filter": {
                          "my_snowball_filter": {
                              "type" : "snowball",
                              "language" : "English"
                          },
                          "my_synonym_filter": {
                            "type": "synonym",
                        "synonyms_path": "my_synonym_file.txt"
                           }
                        },
                           "tokenizer" : {
                            "comma" : {
                                   "type" : "pattern",
                                   "pattern" : ","
                           }
                        },
                        "analyzer": { "a1" : {
                              "type":"custom",
                              "tokenizer": "comma",
                              "filter": ["lowercase", "my_synonym_filter"," my_snowball_filter "]
                        }
                }                                               
          }

       }


Add settings using Java API

The name-value pairs that are used in above rest call can be built as a json object using json builder. This json object can be passed in the java API call.

XContentBuilder settingsBuilder;
try {
                                         settingsBuilder = XContentFactory.jsonBuilder()
                                                                       .startObject()
                                                                           .startObject("analysis")
                                                                                .startObject("filter")
                                                                                  .startObject("my_synonym_filter")
                                                                                  .field("type","synonym")
                                                                                  .field("synonyms_path","my_synonym_file.txt")
                                                                             .endObject()
                                                                       .endObject()
                                                                       .startObject("tokenizer")
                                                                            .startObject("comma")
                                                                               .field("type","pattern")
                                                                               .field("pattern",",")
                                                                            .endObject()
                                                                       .endObject()
                                                                .startObject("analyzer")
                                                                    .startObject("a1")
                                                                       .field("type","custom")
                                                                       .field("tokenizer","comma")
                  .array("filter","lowercase","my_snowball_filter","my_synonym_filter")
                                          .endObject()
                                       .endObject()
                             .endObject()
                         .endObject();                                                            
            client.admin().indices().prepareCreate("simplyjava1").setSettings(settingsBuilder).get();
} catch (IOException e) {
e.printStackTrace();
}

The above piece of code will create index as well as map the settings to the created index.
There is a variety of analyzers available in Elasticsearch. We will be discussing that in the upcoming topics.

Use the following script to view the settings that have been created from executing the snippet.

GET  http://localhost:9200/simplyjava/_settings

Note:
  •        Settings can be updated only when the index is closed, so make sure you close the index before updating it using REST service. Reopen the index once the settings are updated.
  •       Make sure all the files defined in the analyzers are present in config folder of elastic search server, before executing the settings.

 Thanks !! Meet you soon with next post !!

Creating users in keycloak using java API


Keycloak allows us to create users through admin console. But in few scenarios, we might not want our users to know about the underlying authentication mechanisms. In those scenarios, we may need to create users from our application, rather than opening up the admin console of keycloak.

In this post, let us discuss one such way to create users in keycloak by using java API’s.
Add the dependency below, to Maven pom.xml, in order to access the admin functions of Keycloak from the client.

<dependency>
  <groupId>org.keycloak</groupId>
  <artifactId>keycloak-admin-client</artifactId>
  <version>1.4.0.Final</version>
</dependency>

In this post, we will see how to add users to the master realm. 
For this, we have to log into the realm as a user with admin privileges. We have a security-admin-console client, already defined which we are using for this example (But new client can be created and used based on the requirement). 

Keycloak kc = Keycloak.getInstance( "http://localhost:8080/auth", "master", "admin", “password",  "security-admin-console");

http ://localhost:8080/auth  Keycloak admin console url
master                  Realm to log in
admin                  user who has admin privilege in master realm
password                  password of admin user
security-admin-console  client to log in

Creating an existing user

After logging in using the above code, we can create a new user in the master realm

CredentialRepresentation credential = new CredentialRepresentation();
credential.setType(CredentialRepresentation.PASSWORD);
credential.setValue("test123");
UserRepresentation user = new UserRepresentation();
user.setUsername("testuser");
user.setFirstName("Test");
user.setLastName("User");
user.setCredentials(Arrays.asList(credential));
 user.setEnabled(true) ;
kc.realm("master").users().create(user);

User roles can be set using the following lines of code

user.setRealmRoles() ;
user.setClientRoles();

Updating an existing user

A specific user detail can be updated by fetching the user using user id from keycloak.

UserResource userResource = kc.realm("master").users().get("f20f524f-02f6-4465-bc7c-947f1ee9c3df");
UserRepresentation user = userResource.toRepresentation();
user.setFirstName("new First Name");
user.setLastName("new Last Name");
userResource.update(user);

Note : We could use the Admin REST API directly, instead of using java api, we will discuss the same in the next posts.
Thanks !! Meet you soon with next post !!

Sunday 20 December 2015

Enabling CORS (Cross Origin Resource Sharing) headers in java rest services

CORS (Cross Origin Resource Sharing) is a mechanism supported by W3C to enable cross origin requests in web-browsers. CORS requires support from both browser and server to work. 

CORS implementation is required to access rest services hosted in one domain (ip address) from another domain (ip address). The cross domain access will be disabled by default. This will not be an issue until the services are accessed within the same domain. The services can be accessed from different domains using the below mechanism.

Java rest service response should return Access-Control-Allow-Origin: * in the header in order to allow angular js and other applications to access rest services.
This can be done manually, by adding the below line of code, to each and every rest service.
          
 return Response.status(200).header("Access-Control-Allow-Origin", "*").build()

But doing this will be time consuming, if the number of rest services is many.
Instead of doing it manually, we can add this header to the rest services programmatically. This can be done by using the following class in the web project that contains the rest service application.

import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
 @Provider
 public class CORSFilter implements ContainerResponseFilter {
   @Override
    public void filter(final ContainerRequestContext request,  final ContainerResponseContext response) throws IOException {
       response.getHeaders().add("Access-Control-Allow-Origin", "*");
       response.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
       response.getHeaders().add("Access-Control-Allow-Credentials", "true");
       response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
       response.getHeaders().add("Access-Control-Max-Age", "1209600");
    }
}
Note :
This class need not be invoked manually.  @Provider annotation, will automatically call this class, every time the rest service returns a response.
 To enable security, instead of adding * to Access-Control-Allow-Origin, we can give the specific address, that hosts the angular js program.

Create Elasticsearch index using RESTFul Service and Java API


Elasticsearch is used for faster retrieval of data from a stored index. An index can be created either by using the RESTFul service or Java APIs exposed by Elasticsearch. In this blog, let us see what an index is and how to create it.

Basically, an Elasticsearch index is more like a database with multiple types (tables) in it. In relational model, an index can be related to database and type can be related to a table in the database. All the documents that are indexed are similar to the rows in a database table.
An elastic search cluster can have more than one index.

The below code will create an index with name ‘simplyjava’ in the Elasticsearch cluster. We can add our own settings in the created index. By settings, I mean that the type of analyzers, number of shards and number of replicas to be used by index. This will be covered in upcoming posts.

Creating an index using Elasticsearch RESTFul service:

$ curl -XPUT 'http://localhost:9200/simplyjava/'

Creating an index using Elasticsearch Java API:

Settings settings = ImmutableSettings.settingsBuilder().put("cluster.name", "elasticsearch").build();
TransportClient transportClient = new TransportClient(settings);
transportClient = transportClient.addTransportAddress(new InetSocketTransportAddress("localhost", 9300));
Client  client = (Client) transportClient;
CreateIndexRequestBuilder createIndex = client.admin().indices().prepareCreate("simplyjava");
CreateIndexResponse response = createIndex.execute().actionGet();


Note : Even though both the codes do the same functionality you can see that the port value used is different. That is because, 9300 is the port where the TransportClient resides and 9200 is the http port configured in Elasticsearch.