How to shorten the URL for a Restful service - rest

I have a maven java web application developed using Netbeans. I have figured out to run a parameter based Restful service successfully.
The URL contains three names in separated by slashes before providing the parameter.
http://localhost:8080/chims/api/data/list?name=district_list
Can I have a URL with less slashes like
http://localhost:8080/chims/data?name=district_list
This is the applicatoin config file.
package org.netbeans.rest.application.config;
import javax.ws.rs.core.Application;
#javax.ws.rs.ApplicationPath("api")
public class ApplicationConfig extends Application {
}
This is the service file.
package lk.gov.health.phsp.ws;
import java.util.List;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import lk.gov.health.phsp.bean.AreaApplicationController;
import lk.gov.health.phsp.entity.Area;
import lk.gov.health.phsp.enums.AreaType;
import org.json.JSONArray;
import org.json.JSONObject;
#Path("data")
#RequestScoped
public class ApiResource {
#Context
private UriInfo context;
#Inject
AreaApplicationController areaApplicationController;
/**
* Creates a new instance of GenericResource
*/
public ApiResource() {
}
#GET
#Path("list")
#Produces(MediaType.APPLICATION_JSON)
public String getJson(#QueryParam("name") String name) {
JSONObject jSONObjectOut;
if (name == null || name.trim().equals("")) {
jSONObjectOut = errorMessageInstruction();
} else {
switch (name) {
case "district_list":
jSONObjectOut = districtList();
break;
default:
jSONObjectOut = errorMessage();
}
}
String json = jSONObjectOut.toString();
return json;
}
private JSONObject districtList() {
JSONObject jSONObjectOut = new JSONObject();
JSONArray array = new JSONArray();
List<Area> ds = areaApplicationController.getAllAreas(AreaType.District);
for (Area a : ds) {
JSONObject ja = new JSONObject();
ja.put("district_id", a.getCode());
ja.put("district_name", a.getName());
array.put(ja);
}
jSONObjectOut.put("data", array);
jSONObjectOut.put("status", successMessage());
return jSONObjectOut;
}
private JSONObject successMessage() {
JSONObject jSONObjectOut = new JSONObject();
jSONObjectOut.put("code", 200);
jSONObjectOut.put("type", "success");
return jSONObjectOut;
}
private JSONObject errorMessage() {
JSONObject jSONObjectOut = new JSONObject();
jSONObjectOut.put("code", 400);
jSONObjectOut.put("type", "error");
jSONObjectOut.put("message", "Parameter name is not recognized.");
return jSONObjectOut;
}
private JSONObject errorMessageInstruction() {
JSONObject jSONObjectOut = new JSONObject();
jSONObjectOut.put("code", 400);
jSONObjectOut.put("type", "error");
jSONObjectOut.put("message", "You must provide a value for the parameter name.");
return jSONObjectOut;
}
}
I have not done any changes to the web.xml file. All the tutorials I went through did not give me a clear picture as to how and why I have to change it. Even without changing it, the web service works as expected.
How can I reduce the slashes in the URL?

The first thing you can do is remove the #Path("list"). A GET to /data will automatically go to the getJson method.
The next thing you can do is remove the api. You can do this by changing the "api" to "", "/", or "/*". All three will result in the same "/*". What happens when you do this is that Jersey will now take all requests that come to the server (and the same context). Any other servlets or static content will not be reachable.
To get around this, you can configure Jersey to run as a servlet filter instead of a servlet. Then configure it to forward unknown requests. See this post for how to configure this.

Related

Adding file to GitHub using java client - org.eclipse.egit.github.core

I am trying to add a file to a repository using the below code but I am getting below error. I just want to add a file for now.
org.eclipse.egit.github.core.client.RequestException: Invalid request.
For 'properties/email', nil is not a string. For 'properties/name',
nil is not a string. For 'properties/email', nil is not a string. For
'properties/name', nil is not a string. (422) at
org.eclipse.egit.github.core.client.GitHubClient.createException(GitHubClient.java:552)
at
org.eclipse.egit.github.core.client.GitHubClient.sendJson(GitHubClient.java:643)
at
org.eclipse.egit.github.core.client.GitHubClient.post(GitHubClient.java:757)
at
org.eclipse.egit.github.core.service.DataService.createCommit(DataService.java:397)
I sense that it is expecting some properties but how to supply this is not clear. What is that i am missing?
Referring to https://gist.github.com/Detelca/2337731
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.eclipse.egit.github.core.Blob;
import org.eclipse.egit.github.core.Commit;
import org.eclipse.egit.github.core.CommitUser;
import org.eclipse.egit.github.core.Reference;
import org.eclipse.egit.github.core.Repository;
import org.eclipse.egit.github.core.RepositoryCommit;
import org.eclipse.egit.github.core.Tree;
import org.eclipse.egit.github.core.TreeEntry;
import org.eclipse.egit.github.core.TypedResource;
import org.eclipse.egit.github.core.User;
import org.eclipse.egit.github.core.client.GitHubClient;
import org.eclipse.egit.github.core.service.CommitService;
import org.eclipse.egit.github.core.service.DataService;
import org.eclipse.egit.github.core.service.RepositoryService;
import org.eclipse.egit.github.core.service.UserService;
public class GHWriter {
public static void main(String[] args) {
try {
new GHWriter().writeFile("test_two.txt", "test content");
} catch (IOException e) {
e.printStackTrace();
}
}
//https://gist.github.com/Detelca/2337731
public boolean writeFile(String fileName, String fileContent) throws IOException{
// initialize github client
GitHubClient client = new GitHubClient();
//TextView password = (TextView)findViewById(R.id.textViewPassword);
client.setCredentials("username", "password");
// create needed services
RepositoryService repositoryService = new RepositoryService();
CommitService commitService = new CommitService(client);
DataService dataService = new DataService(client);
// get some sha's from current state in git
Repository repository = repositoryService.getRepository("username", "repositoryName");
String baseCommitSha = repositoryService.getBranches(repository).get(0).getCommit().getSha();
RepositoryCommit baseCommit = commitService.getCommit(repository, baseCommitSha);
String treeSha = baseCommit.getSha();
// create new blob with data
Blob blob = new Blob();
blob.setContent("[\"" + System.currentTimeMillis() + "\"]").setEncoding(Blob.ENCODING_UTF8);
String blob_sha = dataService.createBlob(repository, blob);
Tree baseTree = dataService.getTree(repository, treeSha);
// create new tree entry
TreeEntry treeEntry = new TreeEntry();
treeEntry.setPath("testfile.txt");
treeEntry.setMode(TreeEntry.MODE_BLOB);
treeEntry.setType(TreeEntry.TYPE_BLOB);
treeEntry.setSha(blob_sha);
treeEntry.setSize(blob.getContent().length());
Collection<TreeEntry> entries = new ArrayList<TreeEntry>();
entries.add(treeEntry);
Tree newTree = dataService.createTree(repository, entries, baseTree.getSha());
// create commit
Commit commit = new Commit();
commit.setMessage("first commit at " + new Date(System.currentTimeMillis()).toLocaleString());
commit.setTree(newTree);
UserService userService = new UserService( client );
User user = userService.getUser();
CommitUser author = new CommitUser();
author.setName( user.getName() );
Calendar now = Calendar.getInstance();
author.setDate(now.getTime());
commit.setAuthor(author);
commit.setCommitter(author);
List<Commit> listOfCommits = new ArrayList<Commit>();
listOfCommits.add(new Commit().setSha(baseCommitSha));
// listOfCommits.containsAll(base_commit.getParents());
commit.setParents(listOfCommits);
// commit.setSha(base_commit.getSha());
Commit newCommit = dataService.createCommit(repository, commit);
// create resource
TypedResource commitResource = new TypedResource();
commitResource.setSha(newCommit.getSha());
commitResource.setType(TypedResource.TYPE_COMMIT);
commitResource.setUrl(newCommit.getUrl());
// get master reference and update it
Reference reference = dataService.getReference(repository, "heads/master");
reference.setObject(commitResource);
dataService.editReference(repository, reference, true);
System.out.println("Committed URL: "+ newCommit.getUrl());
return false;
}
}
Thanks
After some debugging, found that email and name values are coming as null which is the source of the issue.
Adding below two lines will solve the issue:
author.setName( userName );
author.setEmail(email);

SRVE0777E: Exception thrown by application class

I have a JEE rest app for consume and build web service. When I use url of app in ibm bluemix I have an error in local server webSphere and also in Bluemix:
SRVE0777E: Exception thrown by application class
'tn.hunterViews.services.OfferService.afficherOffer:31'
java.lang.NullPointerException:
at tn.hunterViews.services.OfferService.afficherOffer(OfferService.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.ibm.ws.jaxrs20.server.LibertyJaxRsServerFactoryBean.performInvocation(LibertyJaxRsServerFactoryBean.java:674)
at [internal classes]
OffersService.java
package tn.hunterViews.services;
import java.util.List;
import javax.ejb.EJB;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import tn.hunterViews.business.OfferBusiness;
import tn.hunterViews.business.OfferBusinessRemote;
import tn.hunterViews.domain.Offer;
#Path("/Offers")
public class OfferService {
#EJB
OfferBusinessRemote ofR;
#GET
#Path("")
#Produces(MediaType.APPLICATION_JSON)
public Response afficherOffer(){
return Response.status(Status.OK).entity(ofR.getOffer()).build();
}
}
offersBuissness.java
package tn.hunterViews.business;
import java.util.List;
import javax.ejb.Stateless;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import tn.hunterViews.domain.Offer;
/**
* Session Bean implementation class OfferBusiness
*/
#Stateless
public class OfferBusiness implements OfferBusinessRemote {
#Override
public List<Offer> getOffer() {
Client cl = ClientBuilder.newClient();
WebTarget target = cl.target("https://pihunterviewsdotnet.mybluemix.net/api");
WebTarget off = target.path("offerApi");
List<Offer> offers = off.request(MediaType.APPLICATION_JSON).get(new GenericType<List<Offer>>(){}) ;
cl.close();
return offers;
}
#Override
public boolean createOffer(Offer offer) {
Client cl = ClientBuilder.newClient();
WebTarget target = cl.target("https://pihunterviewsdotnet.mybluemix.net/api");
WebTarget off = target.path("offerApi");
Response resp=target.request().post(Entity.entity(offer, MediaType.APPLICATION_JSON));
if(resp.getStatus()!=201)
return false;
return true;
}
#Override
public boolean updateOffer(int id, Offer offer) {
if (id+""!=null && id!=0 && offer.getId()!=0)
{
Client cl = ClientBuilder.newClient();
WebTarget target = cl.target("https://pihunterviewsdotnet.mybluemix.net/api/offerApi"+ id);
target.request().build("PUT", Entity.entity(offer, MediaType.APPLICATION_JSON))
.invoke();
return true;
}
return false;
}
#Override
public boolean deleteOffer(int id) {
if (id+""!=null && id!=0)
{
Client cl = ClientBuilder.newClient();
WebTarget target = cl.target("https://pihunterviewsdotnet.mybluemix.net/api/offerApi"+ id);
target.request().delete();
return true;}
return false;
}
#Override
public Offer findOfferById(int id) {
Client cl = ClientBuilder.newClient();
WebTarget baseUrl = cl.target("https://pihunterviewsdotnet.mybluemix.net/api/offerApi");
WebTarget getPostURL=baseUrl.path(""+id);
Response response = getPostURL.request(MediaType.APPLICATION_JSON).get();
Offer offer=response.readEntity(Offer.class);
response.close();
cl.close();
return offer;
}
}
Please help me with this problem. Thanks.
If the #EJB annotation cannot be resolved to an EJB, then you would
have received a failure when the server created an instance of the
class containing the #EJB annotation. Since that does not appear to be
happening for you, and instead the instance is created fine, just
without that field being set, then the server is not scanning your
class for annotations or you have the javax.ejb.EJB annotation class
packaged as part of your application.
I would recommend checking the following:
Make sure the javax.ejb.EJB class is not being packaged as part of your annotation
Check that the web.xml for your WAR module has a version > 2.4. WAR modules with a version of 2.4 (or earlier) will not be scanned for annotations.
Check that the web.xml does not contain the setting metadata-complete="true". This setting turns off annotation scanning.
Source: http://www.developersite.org/102-95523-websphere-liberty

Californium Framework CoAP and PUT request

I am trying to do a request to coap server (er-rest-example) using Californium.
I succesfully do a POST request.
But with PUT I am getting a BAD REQUEST, I try using this URLs in url:
coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds
coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds?
coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds?color=r
But with no one get success.
What I am doing wrong?.
This is my simple script:
package coap_client;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
public class cliente {
public static void main(String[] args) throws Exception {
Timer timer;
timer = new Timer();
TimerTask task = new TimerTask(){
#Override
public void run(){
String url="coap://[aaaa::c30c:0000:0000:0002]:5683/actuators/leds";
URI uri= null;
try {
uri = new URI(url);
} catch (URISyntaxException e) {
e.printStackTrace();
}
CoapClient client = new CoapClient(uri);
CoapResponse response = client.put("color=r",MediaTypeRegistry.TEXT_PLAIN);
System.out.println(response.isSuccess());
if (response!=null) {
byte[] myreponse=response.getPayload();
String respuesta2 = new String(myreponse);
System.out.println(respuesta2);
}
}
};
timer.schedule(task, 10,10*1000);
}
}
In Contiki er-rest-example, see the POST/PUT handler(1) for the LED CoAP resource. It expects a mode param without which you will get a BAD_REQUEST as response. I assume that has to go in the request body.

HTTP Basic Authentication for Play framework 2.4

I am looking some way to make some authentication for my play framework app: I want allow/disallow the whole access to non authenticated users
Is there exists some working module/solution for it? I don't need any forms for auth, just 401 HTTP response for non authenticated users (like Apache .htacccess "AuthType Basic" mode).
I've updated Jonck van der Kogel's answer to be more strict in parsing the authorization header, to not fail with ugly exceptions if the auth header is invalid, to allow passwords with ':', and to work with Play 2.6:
So, BasicAuthAction class:
import java.io.UnsupportedEncodingException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import org.apache.commons.codec.binary.Base64;
import play.Logger;
import play.Logger.ALogger;
import play.mvc.Action;
import play.mvc.Http;
import play.mvc.Http.Context;
import play.mvc.Result;
public class BasicAuthAction extends Action<Result> {
private static ALogger log = Logger.of(BasicAuthAction.class);
private static final String AUTHORIZATION = "Authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String REALM = "Basic realm=\"Realm\"";
#Override
public CompletionStage<Result> call(Context context) {
String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null) {
context.response().setHeader(WWW_AUTHENTICATE, REALM);
return CompletableFuture.completedFuture(status(Http.Status.UNAUTHORIZED, "Needs authorization"));
}
String[] credentials;
try {
credentials = parseAuthHeader(authHeader);
} catch (Exception e) {
log.warn("Cannot parse basic auth info", e);
return CompletableFuture.completedFuture(status(Http.Status.FORBIDDEN, "Invalid auth header"));
}
String username = credentials[0];
String password = credentials[1];
boolean loginCorrect = checkLogin(username, password);
if (!loginCorrect) {
log.warn("Incorrect basic auth login, username=" + username);
return CompletableFuture.completedFuture(status(Http.Status.FORBIDDEN, "Forbidden"));
} else {
context.request().setUsername(username);
log.info("Successful basic auth login, username=" + username);
return delegate.call(context);
}
}
private String[] parseAuthHeader(String authHeader) throws UnsupportedEncodingException {
if (!authHeader.startsWith("Basic ")) {
throw new IllegalArgumentException("Invalid Authorization header");
}
String[] credString;
String auth = authHeader.substring(6);
byte[] decodedAuth = new Base64().decode(auth);
credString = new String(decodedAuth, "UTF-8").split(":", 2);
if (credString.length != 2) {
throw new IllegalArgumentException("Invalid Authorization header");
}
return credString;
}
private boolean checkLogin(String username, String password) {
/// change this
return username.equals("vlad");
}
}
And then, in controller classes:
#With(BasicAuthAction.class)
public Result authPage() {
String username = request().username();
return Result.ok("Successful login as user: " + username + "! Here's your data: ...");
}
You can try this filter:
https://github.com/Kaliber/play-basic-authentication-filter
It looks pretty simple to use and configure.
You could also solve this with a play.mvc.Action, like this.
First your Action:
import org.apache.commons.codec.binary.Base64;
import play.libs.F;
import play.libs.F.Promise;
import play.mvc.Action;
import play.mvc.Http.Context;
import play.mvc.Result;
import util.ADUtil;
public class BasicAuthAction extends Action<Result> {
private static final String AUTHORIZATION = "authorization";
private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
private static final String REALM = "Basic realm=\"yourRealm\"";
#Override
public Promise<Result> call(Context context) throws Throwable {
String authHeader = context.request().getHeader(AUTHORIZATION);
if (authHeader == null) {
context.response().setHeader(WWW_AUTHENTICATE, REALM);
return F.Promise.promise(new F.Function0<Result>() {
#Override
public Result apply() throws Throwable {
return unauthorized("Not authorised to perform action");
}
});
}
String auth = authHeader.substring(6);
byte[] decodedAuth = new Base64().decode(auth);
String[] credString = new String(decodedAuth, "UTF-8").split(":");
String username = credString[0];
String password = credString[1];
// here I authenticate against AD, replace by your own authentication mechanism
boolean loginCorrect = ADUtil.loginCorrect(username, password);
if (!loginCorrect) {
return F.Promise.promise(new F.Function0<Result>() {
#Override
public Result apply() throws Throwable {
return unauthorized("Not authorised to perform action");
}
});
} else {
return delegate.call(context);
}
}
}
Next your annotation:
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import play.mvc.With;
#With(BasicAuthAction.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
#Inherited
#Documented
public #interface BasicAuth {
}
You can now annotate your controller functions as follows:
#BasicAuth
public Promise<Result> yourControllerFunction() {
...
I'm afraid there's no such solution, reason is simple: usually when devs need to add authorization/authentication stack they build full solution.
The easiest and fastest way is using HTTP front-end server as a reverse-proxy for your application (I'd choose nginx for that task, but if you have running Apache on the machine it can be used as well). It will allow you to filter/authenticate the traffic with common server's rules
Additionally it gives you other benefits, i.e.: you can create CDN-like path, so you won't waste your apps' resources for serving public, static assets. You can use load-balancer for redeploying your app without stopping it totally for x minutes, etc.

NoClassDefFoundError when calling function from RESTful web service

I'm trying to create a RESTful web service in java that will allow authentication with a Yubikey.
I'm modifying an existing tutorial that I completed while trying to learn about REST.
I'm trying to call the validation function from within the javax.ws.rs.core.Response function but keep getting an error with a package from the imported yubikey jars.
I imported these jars from build path -> libraries --> Add external Jars
Error as follows when I post my form to the RESTful url:
java.lang.NoClassDefFoundError: com/yubico/client/v2/exceptions/YubicoValidationException
package de.vogella.jersey.todo.resources;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import com.yubico.client.v2.exceptions.YubicoValidationException;
import com.yubico.client.v2.exceptions.YubicoValidationFailure;
import de.vogella.jersey.todo.resources.Validate;
#Path("/test")
public class Test {
#POST
public Response testCred(#FormParam("username") String username,
#FormParam("password") String password,
#FormParam("otp") String otp) throws YubicoValidationException, YubicoValidationFailure {
int client_id = 11095;
boolean status;
status = Validate.validate(otp, client_id);
return Response.status(200)
.entity("validation status: : " + status + ", for client " + otp)
.build();
}
}
the validate class is as follows:
package de.vogella.jersey.todo.resources;
import com.yubico.client.v2.YubicoClient;
import com.yubico.client.v2.YubicoResponse;
import com.yubico.client.v2.YubicoResponseStatus;
import com.yubico.client.v2.exceptions.YubicoValidationException;
import com.yubico.client.v2.exceptions.YubicoValidationFailure;
public class Validate {
public static boolean validate(String otp, int client_id) throws YubicoValidationException, YubicoValidationFailure {
YubicoClient client = YubicoClient.getClient(client_id);
YubicoResponse response = client.verify(otp);
if(response != null && response.getStatus() == YubicoResponseStatus.OK) {
return true;
}
return false;
}
}