I have a simple program that is supposed to get a user from github's API and I want to count the times the method is called.
Here is my REST Controller with a GET method (that's the method to be counted):
#RestController
#RequestMapping("/api/v1")
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping("/user/info/{login}")
public User getUser(#PathVariable String login) throws IOException {
userService.insertRecord(login);
return userService.fetchUser(login);
}
}
My program works (more or less) as it's supposed to, BUT .insertRecord() method does not work at all. It basically does nothing. The method SHOULD check if the database already has a user of the given login. If yes - then it should proceed to update the record and increment the REQUEST_COUNT number by 1. If no - then it should create a new record of a given login and REQUEST_COUNT as 1. The table in my database has only two column - LOGIN and REUQEST_COUNT.
But that method literally does nothing, the table in my database remains untouched.
public void insertRecord(String login) {
//this part checks if there is a login like that in the database already
String sql = "select * from calls where LOGIN = ?";
PreparedStatement preparedStatement;
ResultSet resultSet;
try {
preparedStatement = getConnection().prepareStatement(sql);
preparedStatement.setString(1, login);
resultSet = preparedStatement.executeQuery();
//if there's a result - I am trying to increment the value of REQUEST_COUNT column by 1.
if (resultSet.next()) {
String updateSql = "update calls set REQUEST_COUNT = REQUEST_COUNT + 1 where login = ?";
PreparedStatement updatePreparedStatement;
try {
updatePreparedStatement = getConnection().prepareStatement(updateSql);
updatePreparedStatement.setString(1, login);
} catch (SQLException e) {
logger.error("Could not insert a record into the database.");
e.printStackTrace();
}
//if there is no result - I want to insert a new record.
} else {
String insertSql = "insert into calls (LOGIN, REQUEST_COUNT) values (?, ?)";
try (final PreparedStatement insertPreparedStatement = getConnection().prepareStatement(insertSql)) {
insertPreparedStatement.setString(1, login);
insertPreparedStatement.setInt(2, 1);
} catch (SQLException e) {
logger.error("Could not insert a record into the database.");
e.printStackTrace();
}
}
} catch (SQLException e) {
logger.error("Operation failed.");
e.printStackTrace();
}
}
No Logger's messages are printed either, it's as if the method was simply completely ignored.
Please, help.
Because you are not calling updatePreparedStatement.executeUpdate() !
The sql will only take effetc after you call executeUpdate().
You can put a filter that will execute before/after the endpoint executed. And in that particular filter, you can also track which endpoint is executed and take appropriate action for any specific endpoint.
I am trying to access a variable from one class in another.
In my example below, I have 2 files, one called login.java and usermainpage.java.
I want to access a variable called sessionId that is in login.java from usermainpage class file.
I tried several methods but it is not working at all, in login class file, I declared sessionId as a public string and in the file I define it as equals to a data that I retrieved from my database. (If you see the code I am doing a database connection also).
I thought by returning sessionId at the end of the function I can now access this variable from all other java files but no, in my usermainpage.java file, I tried printing out sessionId and it displays nothing. Let me know of a solution, thank you.
// login.java file
public class login extends javax.swing.JFrame {
public String sessionId;
Connection con;
PreparedStatement pst;
ResultSet rs;
private String LoginButtonActionPerformed(java.awt.event.ActionEvent evt) {
try{
String query = "SELECT * FROM `accounts` WHERE username=? and password=?";
con = DriverManager.getConnection("jdbc:mysql://localhost/restock", "root", "password");
pst = con.prepareStatement(query);
pst.setString(1, txtUsername.getText());
pst.setString(2, txtPassword.getText());
rs = pst.executeQuery();
if(rs.next()){
String userType = rs.getString("usertype");
sessionId = rs.getString("id");
System.out.print("##########" + sessionId + "##########"); //this prints out the id I want
if(userType.equals("Admin")){
JOptionPane.showMessageDialog(this, "Login is successful as admin");
mainpage admin = new mainpage();
admin.setVisible(true);
dispose();
} else{
JOptionPane.showMessageDialog(this, "Login is successful as user");
usermainpage user = new usermainpage();
user.setVisible(true);
dispose();
}
}
else{
JOptionPane.showMessageDialog(this, "Incorrect username or password");
txtUsername.setText("");
txtPassword.setText("");
}
} catch(HeadlessException | SQLException ex){
JOptionPane.showMessageDialog(this, ex.getMessage());
}
return sessionId;
}
}
//usermainpage.java file
public class usermainpage extends javax.swing.JFrame {
private void RequestButtonActionPerformed(java.awt.event.ActionEvent evt) {
String type = txttype.getSelectedItem().toString();
String name = txtname.getText();
String quantity = txtquantity.getText();
String status = "Pending";
String userId;
//Create new class object from login.java
login testing = new login();
userId = testing.sessionId;
System.out.print("########## " + userId + "########## "); //this prints out null value
}
}
EDIT: These are some of the problems I encountered based on the suggestions.
There are so many problems with your code:
You should always follow Java naming conventions e.g. you should name your class as Login instead of login. Similarly, the name of your method should be loginButtonActionPerformed instead of LoginButtonActionPerformed.
I do not see any use of the parameter, java.awt.event.ActionEvent evt in your method, LoginButtonActionPerformed. I would remove it from its definition and the calls.
You should avoid making variables public. You should keep sessionId as private or protected as per the requirement and create the public accessor and mutator for it as shown below:
private String sessionId;
public setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getSessionId() {
return sessionId;
}
You are returning the value of sessionId from the method, LoginButtonActionPerformed and therefore you need to call this method inside your method, RequestButtonActionPerformed as follows:
login testing = new login();
userId = testing.LoginButtonActionPerformed(evt);
However, for this, you need to declare your method, LoginButtonActionPerformed as public.
A better approach would be to declare LoginButtonActionPerformed as public void and then you can do:
login testing = new login();
testing.LoginButtonActionPerformed(evt);
userId = testing.getSessionId();
I'm setting up java project where user enter his details and the data will be saved in the the database bellow is my code:
public String CreateUserDetails() throws SQLException, JsonProcessingException
{
iterationResourse = new IterationResourse();
dbcon = DatabaseConnection.getInstance();
iteratinDetails = IterationDetailsParser.getInstance();
try {
String sqlUser = "INSERT INTO user (User_Id,Username,Active_Indi)VALUES(?,?,?)";
PreparedStatement statement = (PreparedStatement) dbcon.con.prepareStatement(sqlUser);
statement.setString(1, iterationResourse.ConvertObjectToString(iteratinDetails.getUserId()));
statement.setString(2, iterationResourse.ConvertObjectToString(iteratinDetails.getUserObj()));
statement.setBoolean(3, true );
statement.executeUpdate();
statement.close();
System.out.println("user created");
st = "user created";
} catch (SQLException e)
{
System.out.println("user id alredy exits");
userIdExits = false;
ObjectMapper mapperUser = new ObjectMapper();
JsonNode rootNode = mapperUser.createObjectNode();
((ObjectNode) rootNode).put("Response", "User ID alreday exits");
String jsonString = mapperUser.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);
System.out.println(jsonString);
iterationResourse.response = jsonString;
st = "Response\", \"User ID alreday exits";
}
return st;
}
I have to write a test case for the above code i have tried the fallowing code. i am trying to mock all the objects that i am trying to use form the other class , the expected result should be string that returns "User created" . but i am unable the get the expected result based on the current code.
public class UserDatabaseTest {
User user = null;
IterationResourse iterationResourse;
DatabaseConnection db;
IterationDetailsParser iterationDetails ;
#Before
public void setUp()
{
iterationResourse = mock(IterationResourse.class);
db = mock(DatabaseConnection.class);
iterationDetails = mock(IterationDetailsParser.class);
user = new User();
}
#Test
public void test() throws JsonProcessingException, SQLException {
Object Object = "3";
String value = "3";
when(db.getInstance().GetDBConnection()).thenReturn(db.getInstance().GetDBConnection());
when(iterationDetails.getUserId()).thenReturn(Object);
when(iterationResourse.ConvertObjectToString(Object)).thenReturn(value);
assertEquals(user.CreateUserDetails(), "user created");
}
}
There are two cases to be written here.
CreateUserDetails return "user created"
Else return "User ID already exists" (i fixed the two typos)
As stated in the comments you should really abstract your DAO layer. But at a high level, you want to mock the DatabaseConnection and return mocks for anything it may return. Doing this prevents NPE's when calling your code base.
Once your mocks are in place the test should return "user created". For the second test have one of your mock throw an SQLException and you can test that "User ID already exists" is returned. I would probably just pass iteratinDetails as a parameter, seems like a dependency for this method.
Lastly, you should not be testing that your code has created database tables and populated them correctly. As long as the data you are passing in (which is something you can test) you should have faith that SQL is going to execute scripts as intended. If you really wanted to get crazy, you could do some mocking to ensure that the statement was prepared properly. IMO that's overkill.
Goodluck!
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.
I am pretty new to AspectJ and AOP in general. I do know that AspectJ has many Annotations (After, AfterReturning, etc) to execute code before a method is called, after it is called, after it returns, when an exception is thrown, etc.
I would like to use it for logging, a pretty typical use case. I've been looking at this article and I think it's most of what I need. It uses AspectJ as well as "jcambi aspects" to perform logging.
But I would like to do something like the following:
public void login(User user) {
String userType = user.getType();
if (!user.isActive()) {
// point cut 1 -- log inactive user
} else if (!user.isPasswordValid()) {
// point cut 2 -- log wrong password
} else {
// point cut 3 -- log successful login
}
}
We have an established log format. Something like:
<actor>|<action_code>|<error_code>|<extra_info>
All of the Actor types, Actions and Error Codes are contained in enums.
Is there any way to tell AspectJ to :
log within 'ifs', and
log different info, depending on what happened? for example, in point cut 1 log one of the following:
admin|login|001|Admin user inactive
user|login|001|Normal user inactive
... and in point cut 2 log one of the following:
admin|login|002|Invalid Admin password
user|login|002|Invalid normal user password
... and in point cut 3 log one of the following:
admin|login|000|Successful Admin login
user|login|000|Successful Normal user login
Something tells me it is not possible. Or at least not easy. But I'm not sure if it's even worth attempting. So I'm torn. On the one hand I'd like to "sanitize" my code of all logging. On the other hand, I'm not sure if it's going to be too much work to implement this.
Any ideas?
*************************************** EDIT ***************************************
Thank you both for your answers! I realize now two things: 1. I've got a lot of work ahead of me. And 2. I think I put too much emphasis on the "login" example.
Login is just one tiny use case. My task is to add logging everywhere ... in a bunch of methods in many, many classes. Basically everywhere I see a LOG.debug() or LOG.info(), anywhere in the application, to replace it with Aspect logging. This also means that as much as I'd like to, I can't just refactor all of the code to make my life easier. I'd love to make login use Exceptions but it's beyond the scope of my task: add logging.
And of course, in each method the business logic will be different, and as such, so will the logging. So my question becomes: what's the best practice to do this? I mean, each method will have its own logic, its ifs ... and will log different things conditionally. So do I go ahead and create an aspect class for each one of these use cases and basically have the same "ifs" there as well?
An example (that's not login!): A method that imports data.
public void import(String type) {
if (type.equals("people")) {
try {
int result = importPeople();
if (result > 0) {
// do some stuff
LOG.info("ok");
} else {
// do some stuff
LOG.info("problem");
}
} catch (Exception e) {
// do some stuff
LOG.debug("exception ...");
}
} else if (type.equals("places")) {
try {
int result = importPlaces();
if (result > 0) {
// do some stuff
LOG.info("ok");
} else {
// do some stuff
LOG.info("problem");
}
} catch (Exception e) {
// do some stuff
LOG.debug("exception ...");
}
}
}
Mind you it's a crap example, with repeated code, etc. But you get the idea. Should I also create an "import" aspect, for logging this method ... with all the accompanying "ifs" to log "ok", "problem", "exception" ? And do this for every use case?
I'm all for getting rid of intrusive logging code but ... it seems like something of a code smell to have to have the logic, with its "ifs", etc., both in the original method (because the method is "doing more stuff" than logging) as well as in the corresponding Aspect ...
Anyway, you both answered my original question ... but I can only have 1 be the answer, so I'm going to accept kriegaex's because he seems to have put a lot of work into it!
Yes, it is possible. But if I were you, I would model the whole story a bit differently. First of all, I would throw exceptions for failed logins due to unknown or inactive users or wrong passwords. Alternatively, the login method could return a boolean value (true for successful login, false otherwise). But in my opinion this would rather be old-fashioned C style than modern OOP.
Here is a self-consistent example. Sorry for the ugly UserDB class with lots of static members and methods. And in reality, you would not store clear-text passwords but randomised salts and salted hashes. But after all it is just a proof of concept for aspect-based, conditional logging.
User bean used for logins:
package de.scrum_master.app;
public class User {
private String id;
private String password;
public User(String id, String password) {
this.id = id;
this.password = password;
}
public String getId() {
return id;
}
public String getPassword() {
return password;
}
}
User database:
There are hard-coded DB entries, static enums, members and methods as well as static inner classes for simplicity's sake. Sorry! You can easily imagine how to do the same with better design, I hope.
package de.scrum_master.app;
import java.util.HashMap;
import java.util.Map;
public class UserDB {
public static enum Role { admin, user, guest }
public static enum Action { login, logout, read, write }
public static enum Error { successful_login, user_inactive, invalid_password, unknown_user }
private static class UserInfo {
String password;
Role role;
boolean active;
public UserInfo(String password, Role role, boolean active) {
this.password = password;
this.role = role;
this.active = active;
}
}
private static Map<String, UserInfo> knownUsers = new HashMap<>();
static {
knownUsers.put("bruce", new UserInfo("alm1GHTy", Role.admin, true));
knownUsers.put("john", new UserInfo("LetMe_in", Role.user, true));
knownUsers.put("jane", new UserInfo("heLL0123", Role.guest, true));
knownUsers.put("richard", new UserInfo("dicky", Role.user, false));
knownUsers.put("martha", new UserInfo("paZZword", Role.admin, false));
}
public static class UserDBException extends Exception {
private static final long serialVersionUID = 7662809670014934460L;
public final String userId;
public final Role role;
public final Action action;
public final Error error;
public UserDBException(String userId, Role role, Action action, Error error, String message) {
super(message);
this.userId = userId;
this.role = role;
this.action = action;
this.error = error;
}
}
public static boolean isKnown(User user) {
return knownUsers.get(user.getId()) != null;
}
public static boolean isActive(User user) {
return isKnown(user) && knownUsers.get(user.getId()).active;
}
public static boolean isPasswordValid(User user) {
return isKnown(user) && knownUsers.get(user.getId()).password.equals(user.getPassword());
}
public static Role getRole(User user) {
return isKnown(user) ? knownUsers.get(user.getId()).role : null;
}
public static void login(User user) throws UserDBException {
String userId = user.getId();
if (!isKnown(user))
throw new UserDBException(
userId, getRole(user), Action.login,
Error.unknown_user, "Unknown user"
);
if (!isActive(user))
throw new UserDBException(
userId, getRole(user), Action.login,
Error.user_inactive, "Inactive " + getRole(user)
);
if (!isPasswordValid(user))
throw new UserDBException(
userId, getRole(user), Action.login,
Error.invalid_password, "Invalid " + getRole(user) + " password"
);
}
}
Please note how the login(User) method throws exceptions with details helpful for logging.
Driver application simulating logins for several user/password combinations:
package de.scrum_master.app;
import java.util.Arrays;
import java.util.List;
public class Application {
public static void main(String[] args) {
List<User> users = Arrays.asList(
new User("mr_x", "foobar"),
new User("bruce", "foobar"),
new User("bruce", "alm1GHTy"),
new User("john", "foobar"),
new User("john", "LetMe_in"),
new User("jane", "foobar"),
new User("jane", "heLL0123"),
new User("richard", "foobar"),
new User("richard", "dicky"),
new User("martha", "foobar"),
new User("martha", "paZZword")
);
for (User user : users) {
try {
UserDB.login(user);
System.out.printf("%-8s -> %s%n", user.getId(), "Successful " + UserDB.getRole(user) + " login");
} catch (Exception e) {
System.out.printf("%-8s -> %s%n", user.getId(), e.getMessage());
}
}
}
}
Please note that we just catch and log all exceptions so as to avoid the application from exiting after failed login attempts.
Console log:
mr_x -> Unknown user
bruce -> Invalid admin password
bruce -> Successful admin login
john -> Invalid user password
john -> Successful user login
jane -> Invalid guest password
jane -> Successful guest login
richard -> Inactive user
richard -> Inactive user
martha -> Inactive admin
martha -> Inactive admin
Login logger aspect:
I suggest you first comment out the two System.out.printf(..) calls in Application.main(..) so as not to mix them up with aspect logging.
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import de.scrum_master.app.User;
import de.scrum_master.app.UserDB;
import de.scrum_master.app.UserDB.Action;
import de.scrum_master.app.UserDB.Error;
import de.scrum_master.app.UserDB.UserDBException;
#Aspect
public class UserActionLogger {
#Around("execution(void de.scrum_master.app.UserDB.login(*)) && args(user)")
public void captureLogin(ProceedingJoinPoint thisJoinPoint, User user) throws Throwable {
try {
thisJoinPoint.proceed();
System.out.printf("%s|%s|%d03|%s%n",
user.getId(), Action.login, Error.successful_login.ordinal(),
"Successful " + UserDB.getRole(user) + " login"
);
} catch (UserDBException e) {
System.out.printf("%s|%s|%03d|%s%n",
e.userId, e.action, e.error.ordinal(),
e.getMessage()
);
throw e;
}
}
}
Console log for aspect:
mr_x|login|003|Unknown user
bruce|login|002|Invalid admin password
bruce|login|003|Successful admin login
john|login|002|Invalid user password
john|login|003|Successful user login
jane|login|002|Invalid guest password
jane|login|003|Successful guest login
richard|login|001|Inactive user
richard|login|001|Inactive user
martha|login|001|Inactive admin
martha|login|001|Inactive admin
Et voilĂ ! I hope that this is roughly what you want.
Its possible.
Create a point-cut/within around login method and get the user object also in your aspect class and once you get User object you can do your logging conditionally.
To get the user object,please check the below answered question by me and how it got the value of surveyId.Same way you can get the User object.
#Around("updateDate()"
public Object myAspect(final ProceedingJoinPoint pjp) {
//retrieve the runtime method arguments (dynamic)
Object returnVal = null;
for (final Object argument : pjp.getArgs())
{
if (argument instanceof SurveyHelper)
{
SurveyHelper surveyHelper = (SurveyHelper) argument;
surveyId = surveyHelper.getSurveyId();
}
}
try
{
returnVal = pjp.proceed();
}
catch (Throwable e)
{
gtLogger.debug("Unable to use JointPoint :(");
}
return returnVal;
}
Here is the complete link for your reference:
Spring AOP for database operation