Architecture: I have a web application from where I'm interacting with the Datastore and a client (raspberry pi) which is calling methods from the web application using Google Cloud Endpoints.
I have to add that I'm not very familiar with web applications and I assume that something's wrong with the setConsumed() method because I can see the call of /create in the app engine dashboard but there's no entry for /setConsumed.
I'm able to add entities to the Datastore using objectify:
//client method
private static void sendSensorData(long index, String serialNumber) throws IOException {
SensorData data = new SensorData();
data.setId(index+1);
data.setSerialNumber(serialNumber);
sensor.create(data).execute();
}
//api method in the web application
#ApiMethod(name = "create", httpMethod = "post")
public SensorData create(SensorData data, User user) {
// check if user is authenticated and authorized
if (user == null) {
log.warning("User is not authenticated");
System.out.println("Trying to authenticate user...");
createUser(user);
// throw new RuntimeException("Authentication required!");
} else if (!Constants.EMAIL_ADDRESS.equals(user.getEmail())) {
log.warning("User is not authorised, email: " + user.getEmail());
throw new RuntimeException("Not authorised!");
}
data.save();
return data;
}
//method in entity class SensorData
public Key<SensorData> save() {
return ofy().save().entity(this).now();
}
However, I'm not able to delete an entity from the datastore using the following code.
EDIT: There are many logs of the create-request in Stackdriver Logging, but none of setConsumed(). So it seems like the calls don't even reach the API although both methods are in the same class.
EDIT 2: The entity gets removed when I invoke the method from the Powershell so the problem is most likely on client side.
//client method
private static void removeSensorData(long index) throws IOException {
sensor.setConsumed(index+1);
}
//api method in the web application
#ApiMethod(name = "setConsumed", httpMethod = "put")
public void setConsumed(#Named("id") Long id, User user) {
// check if user is authenticated and authorized
if (user == null) {
log.warning("User is not authenticated");
System.out.println("Trying to authenticate user...");
createUser(user);
// throw new RuntimeException("Authentication required!");
} else if (!Constants.EMAIL_ADDRESS.equals(user.getEmail())) {
log.warning("User is not authorised, email: " + user.getEmail());
throw new RuntimeException("Not authorised!");
}
Key serialKey = KeyFactory.createKey("SensorData", id);
datastore.delete(serialKey);
}
This is what I follow to delete an entity from datastore.
public boolean deleteEntity(String propertyValue) {
String entityName = "YOUR_ENTITY_NAME";
String gql = "SELECT * FROM "+entityName +" WHERE property= "+propertyValue+"";
Query<Entity> query = Query.newGqlQueryBuilder(Query.ResultType.ENTITY, gql)
.setAllowLiteral(true).build();
try{
QueryResults<Entity> results = ds.run(query);
if (results.hasNext()) {
Entity rs = results.next();
ds.delete(rs.getKey());
return true;
}
return false;
}catch(Exception e){
logger.error(e.getMessage());
return false;
}
}
If you don't want to use literals, you can also use binding as follows:
String gql = "SELECT * FROM "+entityName+" WHERE property1= #prop1 AND property2= #prop2";
Query<Entity> query = Query.newGqlQueryBuilder(Query.ResultType.ENTITY, gql)
.setBinding("prop1", propertyValue1)
.setBinding("prop2", propertyValue2)
.build();
Hope this helps.
I was able to solve it by myself finally!
The problem was just related to the data type of the index used for removeSensorData(long index) which came out of a for-loop and therefore was an Integer instead of a long.
Related
I have taken the web-api-service-example and is trying to adapt it to a Mongo based backend. The code looks almost identical except I have added (duplicate/rename) the Transaction.class to a User.class as well as the Service implementation and web handler.
I am trying to get the user json from the URL /users/username. Mongo correctly retrieves it.
My question is this: I have two pieces of code, one working (returning the user json) and one not working throwing a NPE.
Not working code (it compiles and I cannot understand why is not working):
#Override
public void getUser(
String username,
OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) {
resultHandler.handle(
persistence.getUser(username)
.compose(user -> {
if (user.isPresent()) {
System.err.println("D: " + user.get());
return Future.succeededFuture(OperationResponse.completedWithJson(user.get()));
} else {
return Future.succeededFuture(new OperationResponse().setStatusCode(404).setStatusMessage("Not Found"));
}
})
);
}
It thows the following exception:
SEVERE: Failed to handleMessage. address: __vertx.reply.1
java.lang.NullPointerException
at io.vertx.ext.web.api.OperationResponseConverter.fromJson(OperationResponseConverter.java:15)
at io.vertx.ext.web.api.OperationResponse.<init>(OperationResponse.java:30)
at io.vertx.ext.web.api.contract.impl.RouteToEBServiceHandler.lambda$handle$1(RouteToEBServiceHandler.java:35)
at io.vertx.core.eventbus.impl.EventBusImpl.lambda$convertHandler$2(EventBusImpl.java:342)
at io.vertx.core.eventbus.impl.HandlerRegistration.deliver(HandlerRegistration.java:278)
...
...
D: {"_id":"5fbebeb97d3c4c0e48f9c7e0","email":"some#user.dk","firstName":"firstname","id":0,"lastName":"lastname","password":"password","phone":"+4512345678","username":"username"}
The NPE is thrown first, then the System.err.println("D: " + user.get()); gets executed and prints the user. The browser then waits forever for a reply.
When this piece of code working fine:
#Override
public void getUser(
String username,
OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) {
persistence.getUser(username).compose(user -> {
if (user.isPresent()) {
resultHandler.handle(Future.succeededFuture(OperationResponse.completedWithJson(user.get())));
return Future.succeededFuture();
} else {
resultHandler.handle(Future.succeededFuture(new OperationResponse().setStatusCode(404).setStatusMessage("Not Found")));
return Future.failedFuture("Error");
}
});
}
One would presume that returing the Future.succeededFuture() to the resultHandler.handle() would be the correct way to do it? But it throws the NPE!
I got it working using this getUser() persistance method:
#Override
public Future<JsonObject> getUser(String username) {
Promise<JsonObject> promise = Promise.promise();
MongoClient mongoClient = MongoClient.createShared(vertx, mongoconfig);
JsonObject query = new JsonObject().put("username", username);
mongoClient.findOne(collectionName, query, null, promise::handle);
return promise.future();
}
and this service handler:
#Override
public void getUser(
String username,
OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) {
persistence.getUser(username)
.onSuccess(jsonObject -> {
resultHandler.handle(Future.succeededFuture(OperationResponse.completedWithJson(jsonObject)));
});
}
Great code comes with great responsability... One has to love Futures (once understood) :)
I'm trying to functionality to login with credentials from the database. So far I've just been getting an EJBTransactionRolledbackException. The stack trace is huge and so far I have not been able to find anything online related to my specific issue.
So the MySQL database I have set up has tables divided up into logical data. I have a user_info table with a memberID, addressID, loginID, firstName, lastName, email, phoneNumber, and isModerator. My user_login table has loginID, username, and password. The user_address table is not necessary at this point in the program. The loginID from the user_login table is a foreign key in the user_info table. So I essentially do an inner join to get all the info from bot tables and then try to create a new user object and return it. I've tried just pulling data from one table but the same issue persists. The query being used in the Java code works just fine in MySQL workbench.
Here is the method in question, it's the findEntry method:
#Stateless
#Local(DataAccessInterface.class)
#LocalBean
public class UserDataService implements DataAccessInterface<User> {
public UserDataService() {
}
#Override
public List<User> findAll() {
return null;
}
#Override
public User findEntry(String condition) {
String query = "SELECT * FROM user_info INNER JOIN user_login WHERE username='" + condition + "';";
Connection databaseConnection = null;
Statement statement = null;
ResultSet resultSet = null;
User currentUser = null;
try {
databaseConnection = DriverManager.getConnection(url, username, password);
statement = databaseConnection.createStatement();
resultSet = statement.executeQuery(query);
currentUser = new User(resultSet.getInt("memberID"), resultSet.getInt("addressID"), resultSet.getInt("loginID"), resultSet.getString("firstName"), resultSet.getString("lastName"), resultSet.getString("email"), resultSet.getString("phoneNumber"), resultSet.getString("username"), resultSet.getString("password"), resultSet.getInt("isModerator"));
}
catch(SQLException e) {
throw new DatabaseException(e);
}
finally {
try {
if(databaseConnection != null) {
databaseConnection.close();
statement.close();
resultSet.close();
}
}
catch(SQLException e) {
throw new DatabaseException(e);
}
}
return currentUser;
}
Here is where the findEntry is being called:
#Stateless
#Local(AccountBusinessInterface.class)
#LocalBean
public class AccountBusiness implements AccountBusinessInterface {
#EJB
private DataAccessInterface<User> userDataService;
public AccountBusiness() {
}
/**
* Validates that the use who entered in their username and password entered the correct information.
*/
#Override
public int validateUser(User user) {
//Sets the login boolean to true.
//user.setLoggedIn(true);
//Sets the login text to logout.
//user.setLoginText("Logout");
User currentUser = userDataService.findEntry(user.getUsername());
if(currentUser != null) {
return 0;
}
return 1;
}
This is the onLogin method in the login controller:
#ManagedBean
#ViewScoped
public class LoginController {
/**
* This is the BusinessAccountInferface.
*/
#Inject
private AccountBusinessInterface accountBusinessInterface;
/**
* The default constructor.
*/
public LoginController() {
}
/**
* Takes in a user object and returns the product page that can only be seen by a logged in user, assuming the correct
* username and password was entered.
* #param user
* #return String
*/
public String onLogin(User user) {
//Gets the user object from the appropriate form.
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("user", user);
//If authentication fails, returns the error page.
if(accountBusinessInterface.validateUser(user) == 0) {
//Return the products page.
return "ProductsPage.xhtml";
}
//Returns the login page by default.
return "Login.xhtml";
}
Here is my custom exception:
public class DatabaseException extends RuntimeException {
/**
* This is the default serial version id.
*/
private static final long serialVersionUID = 1L;
public DatabaseException() {
printStackTrace();
}
public DatabaseException(SQLException e) {
printMessage(e.getMessage());
}
public DatabaseException(String message) {
printMessage(message);
}
public DatabaseException(SQLException e, String message) {
printMessage(e.getMessage());
printMessage(message);
}
private void printMessage(String message) {
System.err.println(message);
}
}
The stack trace is too long, but here are the first two lines:
19:11:22,668 ERROR [stderr] (default task-18) Before start of result set
19:11:22,671 ERROR [org.jboss.as.ejb3.invocation] (default task-18) WFLYEJB0034: EJB Invocation failed on component UserDataService for method public beans.User data.UserDataService.findEntry(java.lang.String): javax.ejb.EJBTransactionRolledbackException
The rest of the stack trace is in a file here since I couldn't paste it here:
https://www.dropbox.com/s/r4ampjxr7clfzjz/log.txt?dl=0
The expected result is that a user will be returned from the findEntry method, which is checked in the validateUser method on the business logic, and if it does not return null then 0 is returned which is checked in the login controller which should log the user in. Obviously something is wring with the database being rolled back. I'm just not sure what that means or what is causing it to happen or how to fix it. Did I leave any important code or xml files out?
You have to move the curser in the result set first, this is what the error message "Before start of result set" is telling you.
So move the curser first before reading from it. ResultSet#next() will return true if it is not already at the end.
if (resultSet.next()){
currentUser = new User(resultSet.getInt("memberID")...
}
I am trying to get data by multiple data from database on the basis of multiple Ids using Spring boot.
Basically it is a GET call which takes request parameters as a list of IDs and return response accordingly. IDs are unique in database
Url : api/details/1a,2a,3b
I am getting response as:
Get(value = "api/details/{Ids})
{
[id="1a",name="Raj", interest="Football"],
[id="2a",name="Tom", interest="Cricket"]
[id="3b",name="Kane", interest="Baseball"]
}
It is fine. But when i am giving a wrong Id, I am getting response as:
Url : api/details/xyz,abc,3b
{
null,
null,
[id="3b",name="Kane", interest="Baseball"]
}
I am expecting that instead of null it show say that the ID is not present along with Status code. Something like
{
2-Not found,3-Not Found,
id="3b",name="Kane", hobby="Baseball,
}
My controller class is like:
#RequestMapping(value = "api/details{Ids}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Game>>
getMultipleDetails(#PathVariable("Idss") String Idss) {
HttpHeaders headers = new HttpHeaders();
List<String> ids = Arrays.asList(Idss.split(","));
List<Game> list = new ArrayList<>();
Game details= null;
for (String id : ids) {
details= da.getMultipleDetails(id);
list.add(devices);
}
if (details== null) {
throw new RuntimeException(HttpStatus.NOT_FOUND.toString());
}
return new ResponseEntity<List<Game>>(list, headers, HttpStatus.OK);
}
}
My repository class is like:
public Device getMultipleDetails(String id) {
Game details= null;
try {
details= jdbcTemplate.queryForObject("SELECT * FROM table_name WHERE Id = ?",new DeviceRowMapper(), id);
} catch (Exception e) {
// Log the system generated Id
String systemRefId = String.valueOf(System.currentTimeMillis());
LOGGER.error(systemRefId, e);
//throw new DatabaseException(systemRefId, e);
}
return details;
}
Game is my model class that conatins id, name, hobby
As you're setting the ResponseEntity<List<Game>> you should only return a List with Game objects inside.
Not sure why you want to return the failed ones in the same List but as a workaround I will set id of the not found and, in the fields name and Game I will set 'Not found' instead of returning null objects. For example:
public Device getMultipleDetails(String id) {
Game details = new Game();
try {
details= jdbcTemplate.queryForObject("SELECT * FROM table_name WHERE Id = ?",new DeviceRowMapper(), id);
//If details is not null but it's empty
if (StringUtils.IsEmpty(details.getId())) {
details.setId(id);
details.setName("Not Found");
details.setGame("Not Found");
}
} catch (Exception e) {
// Log the system generated Id
String systemRefId = String.valueOf(System.currentTimeMillis());
LOGGER.error(systemRefId, e);
//If details is null it will trow null pointer exception
details = new Game();
details.setId(id);
details.setName("Not Found");
details.setGame("Not Found");
}
return details;
}
I strongly recommend you to rename the field Game in you Game class. A field should not duplicate the name of its containing class.
It's confusing to have a class member with the same name (case differences aside) as its enclosing class. This is particularly so when you consider the common practice of naming a class instance for the class itself.
Best practice dictates that any field or member with the same name as the enclosing class be renamed to be more descriptive of the particular aspect of the class it represents or holds.
I would recommend to rename it to something like typeOfGame for example.
You should manage the empty objects, and manage the message also, the code should be like this, because if not, only the last detail is the one evaluated, thats why the exception is not raised.
for (String id : ids) {
details= da.getMultipleDetails(id);
list.add(devices);
if (details== null) {
throw new RuntimeException(HttpStatus.NOT_FOUND.toString());
}
}
I looked a lot of stuff on on internet but I don't found any solution for my needs.
Here is a sample code which doesn't work but show my requirements for better understanding.
#Service
public class FooCachedService {
#Autowired
private MyDataRepository dataRepository;
private static ConcurrentHashMap<Long, Object> cache = new ConcurrentHashMap<>();
public void save(Data data) {
Data savedData = dataRepository.save(data);
if (savedData.getId() != null) {
cache.put(data.getRecipient(), null);
}
}
public Data load(Long recipient) {
Data result = null;
if (!cache.containsKey(recipient)) {
result = dataRepository.findDataByRecipient(recipient);
if (result != null) {
cache.remove(recipient);
return result;
}
}
while (true) {
try {
if (cache.containsKey(recipient)) {
result = dataRepository.findDataByRecipient(recipient);
break;
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return result;
}
}
and data object:
public class Data {
private Long id;
private Long recipient;
private String payload;
// getters and setters
}
As you can see in code above I need implement service which will be stored new data into database and into cache as well.
Whole algorithm should looks something like that:
Some userA create POST request to my controller to store data and it fire save method of my service.
Another userB logged in system send request GET to my controller which fire method load of my service. In this method is compared logged user's id which sent request with recipients' ids in map. If map contains data for this user they are fetched with repository else algorithm check every second if there are some new data for that user (this checking will be some timeout, for example 30s, and after 30s request return empty data, and user create new GET request and so on...)
Can you tell me if it possible do it with some elegant way and how? How to use cache for that or what is the best practice for that? I am new in this area so I will be grateful for any advice.
I'm building an LDAP interface for my database. When a client request bind(), it will search in the database and check if it is valid or not.
public class Main {
LDAPListener listener ;
Main() {}
public static void main(String[] args) {
Main main = new Main();
int port = main.StartServer();
try {
LDAPConnection cn = new LDAPConnection("localhost",port);
System.out.println("."+cn.isConnected()+" "+cn.getConnectedPort());
cn.bind("uid=user,ou=People,dc=example,dc=com", "pass");
cn.close();
main.StopServer();
} catch (Exception e){e.printStackTrace();
main.StopServer();}
}
public int StartServer() {
int listenPort = 0;
RequestHandler requestHandler = new RequestHandler();
LDAPListenerConfig config = new LDAPListenerConfig(listenPort, requestHandler);
listener = new LDAPListener(config);
try {
listener.startListening();
System.out.println(">port "+listener.getListenPort());
} catch (Exception e){System.out.println("e1> "+e.getMessage());}
return listener.getListenPort();
}
public void StopServer(){
System.out.println(">shutdown");
listener.shutDown(true);
}
}
Then, i modify LDAPListenerRequestHandler to communicate with the database, get the record as return value:
class RequestHandler extends LDAPListenerRequestHandler {
#Override
public LDAPMessage processBindRequest(int arg0, BindRequestProtocolOp arg1,
List<Control> arg2) {
String uid = arg1.getBindDN();
String pass = arg1.getSimplePassword();
System.out.println(">bind: "+ uid);
// Database query: SELECT * FROM user WHERE username='uid' AND password='pass'
// Get the record as return value
return null;
}
}
When i run it, i got error message from the bind line:
LDAPException(resultCode=80 (other), errorMessage='An unexpected exception was thrown while attempting to process the requested operation: NullPointerException(trace='run(LDAPListenerClientConnection.java:461)', revision=15579)', diagnosticMessage='An unexpected exception was thrown while attempting to process the requested operation: NullPointerException(trace='run(LDAPListenerClientConnection.java:461)', revision=15579)')
at com.unboundid.ldap.sdk.LDAPConnection.bind(LDAPConnection.java:1881)
at com.unboundid.ldap.sdk.LDAPConnection.bind(LDAPConnection.java:1799)
I think, it is caused by processBindRequest() that return null. How to encapsulate my database record as LDAPMessage in that process?
You are correct that the processBindRequest method must return a non-null response.
If the bind is successful (the user exists, is allowed to authenticate, and has provided the correct credentials) then you can create a successful response with code like:
#Override()
public LDAPMessage processBindRequest(final int messageID,
final BindRequestProtocolOp request,
final List<Control> controls)
{
return new LDAPMessage(messageID,
new BindResponseProtocolOp(ResultCode.SUCCESS_INT_VALUE,
null, // No matched DN is needed
null, // No diagnostic message is needed
null, // No referral URLs are needed
null), // No server SASL credentials are needed
Collections.<Control>emptyList()); // Add empty list to return
}
If the authentication is not successful, then you should probably return a response with a result code of INVALID_CREDENTIALS rather than SUCCESS, and if you want to provide a message to the client with information about why the bind failed, you can put that in the diagnostic message element.