I have two method in different controller class
#RequestMapping(value="/addHari")
public String menuAddHari(HttpServletRequest request,#RequestParam(value="addchkmerah",required = false)String status, Model model) throws ParseException {
Date hariKerja = new SimpleDateFormat("yyyy-MM-dd").parse(request.getParameter("addharikerja"));
java.sql.Date workDay = new java.sql.Date(hariKerja.getTime());
String keterangan = request.getParameter("addkolomket");
Time jamMasuk = hs.getTime(request.getParameter("addjammasuk"));
Time jamKeluar = hs.getTime(request.getParameter("addjampulang"));
month = request.getParameter("addBulan");
HariKerjaModel hm = new HariKerjaModel();
hm.setKeterangan(keterangan);
hm.setTanggal(workDay);
hm.setMerahBukan(status);
hm.setJamMasuk(jamMasuk);
hm.setJamKeluar(jamKeluar);
hs.saveDate(hm);
model.addAttribute("month", month);
return "redirect:/hariKerja";
}
and
#RequestMapping(value="/hariKerja")
public String menuHariKerja(Model model,HttpServletRequest request) {
List<HariKerjaModel> hk = new ArrayList<>();
String month = request.getParameter("bulansrc");
HariKerjaController h = new HariKerjaController();
String x=h.getMonth();
if(month==null) {
String month2 = (String) model.getAttribute("month");
}
hk = hs.readHariKerja(month);
model.addAttribute("ListHariKerjaModel", hk);
model.addAttribute("valueSelected",month);
return "hariKerja";
}
how to pass value variable "month" in method "menuAddHari" to variable "month2" in method "menuHariKerja" ?
You can achieve your requirement while redirect your request with path or query or body containing entity or fields which are url-form-encoded.
return StringBuffer("redirect:/your/other/controller/method/")
.append(param1).append("/").append(param2);
And you will be able to handle the request in your other controller with :
#GetMapping(value="/your/other/controller/method/{param1}/{param2}")
public String otherMethodInOtherController(#PathVariable("paramName1") String param1, #PathVariable("paramName2") String param2) {
// your 2sd controller code here...
return "your-view-name";
}
Of course, if you have many parameters to send, it will be better to set a ResponseEntity with BodyInserters inside the body of your request before to send it to your 2sd controller.
And don't forget not to use GET methods to forward sensitive user|system datas.
Related
I know that in Java a method can return only one return type... But if there is any possiblity to this, kindly let me know. From the below method I am trying to return a list if condition satisfies else i am trying to return an error message.
Here is my code:
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return "Incorrect loginId..Please enter valid loginId";
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return companyList;
} else {
return "You are not Authorized";
}
} else {
return "Incorrect Password";
}
Yes its possible, create a custom Exception say 'MyAppException' and throw that exception with the error message you want.
Write your logic in a try{}catch block and throw the exception in catch so that the response has the error message
public List<CompanyMaster> getCompanies(#RequestBody UserDetails user) throws MyAppppException
{
try
{
//your logic which throws error
return companyList;
}
catch( final MyAppException we )
{
throw new MyAppException("User not found", HttpStatus.NOT_FOUND);
}
}
Refer this link
https://www.codejava.net/java-core/exception/how-to-create-custom-exceptions-in-java
You can achieve this by creating a new presenter Class which contains List and status of type String and change the return type of getCompanies method to presenter class like
public CompaniesPresenter getCompanies()
And your CompaniesPresenter class should look like
public class CompaniesPresenter {
private List<CompanyMaster> companyMaster;
private string status;
//default constructor
public CompaniesPresenter(){
}
//parameterized constructor to return only string in exception case
public CompaniesPresenter(Stirng status){
this.status = status;
}
//parametirized constructor to return success case
public CompaniesPresenter(List<CompanyMaster> companyMaster, Stirng status){
this.companyMaster = companyMaster;
this.status = status;
}
//getters and setters
}
This is how your updated method lokks like
#RequestMapping(value = "/getcompanies", method = RequestMethod.POST)
public CompaniesPresenter getCompanies(#RequestBody UserDetails user) {
String OrgLoginId = user.getOrgLoginId();
String password = user.getuPassword();
String checkLoginId = null;
String uPassword = null;
String encPassword = null;
String loginId = null;
String checkAuthorized = null;
// String loginId=userService.getLoginId(OrgLoginId);
List<Object[]> CheckIdPassword = userService.checkLoginId(OrgLoginId);
List<Object[]> results = CheckIdPassword;
for (Object[] obj : results) {
checkLoginId = obj[0].toString();
if (null == obj[1]) {
uPassword = "";
} else {
uPassword = obj[1].toString();
}
loginId = obj[2].toString();
}
checkAuthorized = loginId.substring(0, 3);
if (null != password) {
MD5 md5 = new MD5();
encPassword = md5.getPassword(password);
}
if (checkLoginId == null) {
return new CompaniesPresenter("Incorrect loginId..Please enter valid loginId");
} else if (encPassword.equals(uPassword)) {
if (checkAuthorized.equals("STE")) {
List<CompanyMaster> companyList = userService.getCompanyList(OrgLoginId);
return new CompaniesPresenter(companyList,"success");
} else {
return new CompaniesPresenter("You are not Authorized");
}
} else {
return new CompaniesPresenter("Incorrect Password");
}
This is not tested please make sure for any compilation errors
vavr's Either class would be a good choice.
The usage of custom exception is most reasonable solution. However, creating custom exception for just one case is not ideal always.
Another solution is to return empty List from your method, check if the List is empty in your servlet (or wherever you are invoking this method from), and show error message there.
It seems like you want to return multiple error messages for different cases. In this case, custom exception is recommended solution. If you don't like custom exceptions, you can return List<Object> and populate error message as the first element in the list. In the place where this List is obtained, check if the first element is instanceOf String or CompanyMaster. Based on what it is, you can perform your operations. This is a weird but possible solution (only if you don't like custom exceptions).
You need to understand the problem first. You are mixing two things here, first authorization, does the user has correct privileges to get company details, second giving the company details itself. Let's understand the first problem when a user tries to access "/getcompanies" endpoint will you let him in if does not have access, in REST world your security model should take care of it. I would use spring security to achieve this. My recommendation would be to explore on "interceptor" and solve the problem of invalid user. This will make your other problem easy as your "/getcompanies" endpoint can focus only on getting the details and return it (SRP).
Help me, guys!
Please, advise a simple solution to convert type boolean from server to int in Android :)
When I log in, i get respone from server like this :
{"status":{"error":0,"code":200,"message":"OK"},"response":{"profile":{"id":114,"username":"k#gmail.com","full_name":"k","phone":"9999999","verified":1,"admin":0,"allow_dev":false,"company":{"id":9,"name":"ООО \"Фингерз медиа\"","email":"info#fingers.by","sample":null,"logo":"http://storage.guardian-glass.fingersmedia.by/0cb56968b3cec1bba301db8d51d1015e.jpg"}},"access_token":"15629e234e04a54a5a44ef2aa4eccb1d"}}
Then I get undefined exception: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected NUMBER but was BOOLEAN
This happens because of JsonElement "allow_dev" is boolean from server, and in Android I have "allow_dev" like int.
This is login method :
private void login(String email, String pass) {
showProgress();
JsonObject params = new JsonObject();
params.addProperty("username", email);
params.addProperty("password", pass);
UserOperationsTask task = new UserOperationsTask(UserOperationsTask.TaskMode.MODE_LOGIN, params) {
#Override
public void onLoadFinished(Bundle res) {
hideProgress();
String errorMessage = res.getString(UserOperationsTask.RESULT_ERROR_STRING);
if (errorMessage != null) {
showMessage(getString(R.string.login_error), getString(R.string.server_request_error));
} else {
String json = res.getString(UserOperationsTask.RESULT_JSON_STRING);
if (json != null) {
JsonParser parser = new JsonParser();
JsonObject responseData = parser.parse(json).getAsJsonObject();
JsonObject companyObj = responseData.getAsJsonObject("profile").getAsJsonObject("company");
}
setRegisteredMode();
}
}
};
task.execute(this);
}
This method parse response and I tried to convert allow_dev type from boolean to int, but I dont understand whether I'm doing right?
private Bundle parseProfileResponse(Context context, JsonObject responseData) {
Log.d(TAG, "parseProfileResponse");
// I tried convert allow_dev type from boolean to int
String allow_dev_server = String.valueOf(responseData.get("allow_dev"));
boolean b = allow_dev_server.equals("true");
int allow_dev = b ? 1 : 0; // true == 1
Profile profile;
profile = GsonHolder.getGSONInstance().fromJson(responseData.getAsJsonObject("profile"), Profile.class);
profile.allow_dev = allow_dev;
Bundle res = new Bundle();
res.putParcelable(RESULT_OBJ, profile);
res.putString(RESULT_JSON_STRING, responseData.toString());
try {
Cache.saveToCache(context, profile);
} catch (RemoteException e) {
Log.d(TAG, "parseAuthResponse RemoteException: " + e.toString());
res.putString(RESULT_ERROR_STRING, context.getString(R.string.database_error));
} catch (OperationApplicationException e) {
Log.d(TAG, "parseAuthResponse OperationApplicationException: " + e.toString());
res.putString(RESULT_ERROR_STRING, context.getString(R.string.database_error));
}
return res;
}
I have to get "allow_dev" convert it in int and write to database.
If you can switch to mappings, you can use everything static typing can give you, comparing to weakly "typed" JsonElement and its subclasses. It has several advantages: compile-time checking, more robust code, IDE support, etc. The major disadvantage is that you have to write custom mappings, however you there tools (online as well) that can try to generate simple mapping classes based on the given sample JSON (for example, a very popular tool here: http://www.jsonschema2pojo.org/).
Now, let's create some mappings. The mappings like the ones below are used rarely: final fields (used for "server responses" that are no supposed to be modified programmatically; Gson can assign such fields anyway); null for non-primitives and some hacks for primitive type defaults values to cheat the compiler (like Integer.value(0) but not simply 0: otherwise, javac may inline constants, thus Gson cannot affect them); no getters/setters (data-transfer objects are just data bags, but yes, getters can work better). Anyway, you can use your style, and the followings mappings are used for demonstration purposes (the mappings code has even compact formatting: one property per line collapsing the annotations).
final class Response<T> {
final Status status = null;
final T response = null;
}
final class Status {
final int error = Integer.valueOf(0);
final int code = Integer.valueOf(0);
final String message = null;
}
final class ProfileAndAccessToken {
final Profile profile = null;
#SerializedName("access_token") final String accessToken = null;
}
final class Profile {
final int id = Integer.valueOf(0);
final String username = null;
#SerializedName("full_name") final String fullName = null;
final String phone = null;
final int verified = Integer.valueOf(0);
final int admin = Integer.valueOf(0);
#SerializedName("allow_dev") #JsonAdapter(BooleanToIntTypeAdapter.class) final int allowDev = Integer.valueOf(0);
final Company company = null;
}
final class Company {
final int id = Integer.valueOf(0);
final String name = null;
final String email = null;
final String sample = null;
final URL logo = null;
}
Note two annotations above:
#SerializedName -- this annotation can "rename" fields so you can use even special characters (however it's discouraged, and it's typically used to map incoming JSON property names to javaCamelNamingConventions).
#JsonAdapter -- this annotation can "attach" a special type adapter to a certain field, so that it could convert JSON properties to the given field and vice versa.
Now let's implement a type adapter that can convert incoming boolean values to local int values and vice versa. Note that type adapters work in streaming manner, so you have to read JSON tokens stream on fly during read, and, of course, generate JSON tokens stream during write.
final class BooleanToIntTypeAdapter
extends TypeAdapter<Integer> {
// Public constructors may be evil, and let expose as less as possible
// Gson can still instantiate this type adapter itself
private BooleanToIntTypeAdapter() {
}
#Override
#SuppressWarnings("resource")
public void write(final JsonWriter out, final Integer value)
throws IOException {
// If the given value is null, we must write the `null` token to the output JSON tokens stream anyway in order not to break JSON documents
if ( value == null ) {
out.nullValue();
return;
}
// Let's assume that we can accept either 0 or 1 that are mapped to false and true respectively
switch ( value ) {
case 0:
out.value(false);
break;
case 1:
out.value(true);
break;
default:
// Or throw an exception as fast as we can
throw new IllegalArgumentException("Cannot convert " + value + " to a boolean literal");
}
}
#Override
public Integer read(final JsonReader in)
throws IOException {
// Peek the next token type, and if it's null, then return null value too
if ( in.peek() == NULL ) {
return null;
}
// Otherwise parse the next token as boolean and map it either to 1 or 0
return in.nextBoolean() ? 1 : 0;
}
}
That's all you need in Gson. Now, for you entire JSON, since the response mapping class is generic, you have to tell Gson what the T is. Gson accepts java.lang.reflect.Type in the fromJson method, and this type can hold both raw and parameterized types, so Gson could (de)serialize more accurately.
private static final Type profileAndAccessTokenResponse = new TypeToken<Response<ProfileAndAccessToken>>() {
}.getType();
final Response<ProfileAndAccessToken> response = gson.fromJson(JSON, profileAndAccessTokenResponse);
System.out.println(response.response.profile.allowDev);
System.out.println(gson.toJson(response, profileAndAccessTokenResponse));
Output:
0
{"status":{"error":0,"code":200,"message":"OK"},"response":{"profile":{"id":114,"username":"k#gmail.com","full_name":"k","phone":"9999999","verified":1,"admin":0,"allow_dev":false,"company":{"id":9,"name":"ООО \"Фингерз медиа\"","email":"info#fingers.by","logo":"http://storage.guardian-glass.fingersmedia.by/0cb56968b3cec1bba301db8d51d1015e.jpg"}},"access_token":"15629e234e04a54a5a44ef2aa4eccb1d"}}
Note that the first line is 0: this what is generated with BooleanToIntTypeAdapter. Backing to your code:
String allow_dev_server = String.valueOf(responseData.get("allow_dev"));
boolean b = allow_dev_server.equals("true");
int allow_dev = b ? 1 : 0; // true == 1
Profile profile;
profile = GsonHolder.getGSONInstance().fromJson(responseData.getAsJsonObject("profile"), Profile.class);
profile.allow_dev = allow_dev;
Can be replaced with simple:
final Profile profile = GsonHolder.getGSONInstance().fromJson(responseData.getAsJsonObject("profile"), Profile.class)
// now `Profile.allowDev` is 0 or 1 automatically
Note that responseData can be replace with a particular mapping, so you could even not parse at that line: probably you might simply pass the whole response object as a class mapping rather that JsonObject in your parseProfileResponse -- it would be more robust.
Below is a generalized version of my Spring RestController implementation. It's purpose is to respond to HTTP requests at a specific base URI ("/rest/my/uri/to/resource") with a resource based on the URI ID input. It works fine, however due to the structure of the data it returns I have had to add an optional date param. I have the controller calculate today's date and use that in my database query when the user does not supply one in the URI.
My question to you is if I use the todaySqlDate variable for each response where the user does not supply a date as I am below, will it recalculate the date each time it responds to a request? Obviously if the user inputs a date like
/rest/my/uri/to/resource/identifier/666?date=2016-03-15
this will not be an issue. My concern is that when the date is not included, a la
/rest/my/uri/to/resource/identifier/666
it will use the default date. Will the default date stop being current if the service is left running for more than 24 hours?
#RestController
#RequestMapping(value = "/rest/my/uri/to/resource")
public class ResourceController
{
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceController.class);
#Autowired
private ResourceService resourceService;
public String todaySqlDate = getTodaySqlDate();
#RequestMapping(value = "/identifier/{id}", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public Resource getResource(#PathVariable("id") String id,
#RequestParam(value="date", defaultValue="", required=false) String resourceDate)
throws InvalidParameterException, DataNotFoundException, Exception
{
LOGGER.trace("In ResourceController.getResouce() with {}", id);
try
{
if(!isValidIdentifier(id))
{
throw new InvalidParameterException("id is not valid: " + id);
}
if(resourceDate.isEmpty())
{
resourceDate = todaySqlDate;
}
else if(!isValidSqlDateString(resourceDate))
{
throw new InvalidParameterException("resourceDate is present but not valid: " + resourceDate);
}
ResourceList resourceList = resourceService.getResource(id, resourceDate);
if (resourceList == null)
{
LOGGER.trace("No resources found for given input");
throw new DataNotFoundException("ResourceList for " + id + " not found");
}
return resourceList;
}
catch (Exception e)
{
LOGGER.error(e.getMessage(), e);
throw e;
}
}
public String getTodaySqlDate()
{
java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
return sqlDate.toString();
}
}
Yes, every new request will be handled by a new separate instance (thread) and hence it will re-calculate the date every time.
You can have a look at Spring/REST Documentation for more information.
Useful Link:
How are Threads allocated to handle Servlet request?
I have entity for reporting. I wonna know who filled up report. I would like to put id of user from session to form class.
I've tried already methods like: bind, fill; but no working solution found.
Ofcorse I mean form class: play.data.Form.form
How can I achive this?
Please help.
Here is my approach (when I wrote this Post):
static Form<Registry> modelForm = form(Registry.class);
Registry registry = new Registry();
registry.creationUser = User.getCurrentUser();
registry.test="tt";
modelForm.fill(registry);
modelForm.bind(data, allowedFields)
My submit method
#Transactional
public static Result submit() {
modelForm = modelForm.bindFromRequest();
if (modelForm.hasErrors()) {
return badRequest(views.html.Registry.form.render(modelForm));
} else {
modelForm.get();
}
registry.creationUser = User.getCurrentUser();
modelForm.fill(registry);
if (modelForm.hasErrors()) {
Logger.debug(modelForm.toString());
return badRequest(views.html.Registry.form.render(modelForm));
} else {
modelForm.get().toDataBase();
toLog("success", "Succefully added new Report");
flash("success", "Pomyślnie dodano.");
return redirect(routes.Index.index());
}
}
Let's say you have a Report model like:
public Date date;
public User user;
public String message;
You need to create and fill an object first (without saving to DB!) and then fill the form with it, like:
Report report = new Report(); // constructors params are welcome here
report.user = loggedUser;
report.date = new Date();
Form<Report> reportForm = Form.form(Report.class).fill(report);
// OR
Form<Report> reportForm = Form.form(Report.class);
reportForm = reportForm().fill(report);
// NOT
Form<Report> reportForm = Form.form(Report.class);
reportForm().fill(report); // Wrong!
return ok(reportCreatingView.render(reportForm));
Edit: You don't need to fill User data at first step, as actually you can add it after binding, you have too much lines in your submit() action, keep it simple :)
public static Result submit() {
User user = User.getCurrentUser();
if (user == null) return unauthorized("You must log in");
modelForm = modelForm.bindFromRequest();
if (modelForm.hasErrors()) {
return badRequest(views.html.Registry.form.render(modelForm));
}
// At this point you have a logged User obj which is not null,
// you have modelForm without errors (checked previously)
// so you need to only add the user to form and save it.
Registry registry = modelForm.get();
registry.creationUser = user;
registry.save();
flash("success", "Twój log został zapisany w bazie.");
return redirect(routes.Index.index());
}
I have an MVC app that is creating new offices instead of updating them on when using an edit form. Please help me understand why this is happening.
Search method that populates the search results:
#RequestMapping(value = "/searchResults", method = RequestMethod.POST)
public ModelAndView search(#RequestParam String searchCriteria, HttpServletRequest request) {
List<Office> offices = officeServiceImpl.search(searchCriteria);
return new ModelAndView("searchResults", "offices", offices);
}
Here's what the link to the edit form looks like on the search results page:
Edit Office
Here is the Controller's edit GET method that populates the form with the existing Office:
#RequestMapping(value = "/{officeId}/edit", method = RequestMethod.GET)
#Transactional(noRollbackFor=NoResultException.class)
public ModelAndView initUpdateOfficeForm(
#PathVariable("officeId") Long officeId, Model model) {
Office office = officeServiceImpl.find(officeId);
//prepareEditFormModelAndView(office) just converts some objects to strings for typeahead form population
return prepareEditFormModelAndView(office);
}
Here is the edit POST method:
#RequestMapping(value = "/{officeId}/edit", method = RequestMethod.POST)
public ModelAndView processUpdateOfficeForm(#ModelAttribute("office") #Valid Office office,
BindingResult result, SessionStatus status) {
if (! "united states of america".equals(office.getFolderStrings().toLowerCase())) {
//This portion of code converts the typeahead strings to objects
result = tryCountries(office, result);
result = tryDepartments(office, result);
result = tryEmployees(office, result);
}
if (result.hasErrors()) {
return prepareEditFormModelAndView(office);
} else {
officeServiceImpl.save(office);
status.setComplete();
return new ModelAndView("editResult", "office", office);
}
}
officeServiceImpl calls officeRepositoryImpl method save which looks like:
#Override
public Office save(Office office) {
em.merge(office);
em.flush();
return office;
}
Thanks
Edit: Adding prepareEditFormModelAndView(office), This method attempts to build strings from associated objects:
#Transactional(noRollbackFor={NoResultException.class, IndexOutOfBoundsException.class})
private ModelAndView prepareEditFormModelAndView(Office office) {
String departmentStrings = "";
String employeeStrings = "";
List<OOM> officeOOMs = new ArrayList<OOM>();
StringBuilder sb = new StringBuilder();
try {
officeOOMs = oomServiceImpl.getOOMsForCurrentOffice(office.getId());
} catch (NoResultException e) {
officeOOMs = null;
}
for (OOM o : officeOOMs) {
try {
Employee tempEmployee = employeeServiceImpl.find(o
.getEmployeeId());
sb.append(tempEmployee.getDisplayName() + ", ");
} catch (NoResultException e) {
sb.append("Not found in system");
}
}
employeeStrings = sb.toString();
if ((! "".equals(office.getDepartmentStringsOnForm())) && office.getDepartmentStringsOnForm() != null) {
departmentStrings = office.getDepartmentStringsOnForm();
}
String folderStrings = "";
try {
folderStrings = kmlFolderServiceImpl.getInternationalOfficeString(office.getId());
LOGGER.info("Folder Strings: " + folderStrings);
} catch (NoResultException e) {
folderStrings = "";
LOGGER.info("Folder Strings: " + "no result");
}
boolean isInternational = office.isInternational();
ModelAndView result = new ModelAndView("editOfficeForm", "office", office);
result.addObject("departmentStrings", departmentStrings);
result.addObject("isInternational", isInternational);
result.addObject("folderStrings", folderStrings);
result.addObject("employeeStrings", employeeStrings);
return result;
}
I am adding a previous comment here, for better clarification. According to the OP the following fixes the problem:
When the ID is not in the form then when the model is posted back no ID is set to the entity making the persistence provider believe it is new entity.
Therefor the most obvious solution is to post the ID of the entity as well in the save operation (probably using a hidden field).
Another solution would be to try to load the entity in the database based on some business key
to see if the entity is new or not.