How to get Logback in a custom layout when parameterized logging? - logback

I have created a custom layout.
Here is my code:
private static final Logger log = LoggerFactory.getLogger(AccountLoginHelper.class);
...
log.info("Successful Login: {}", userName);
I can see in the debugger that userName is filled with "myid" before the call.
But the output in the message looks like this:
Successful Login: {}
I need it to look like this:
Successful Login: myid
This is the code in my customLayout:
#Override
public synchronized String doLayout(ILoggingEvent event) {
String message = event.getMessage();

Just figured it out. Need to change
String message = event.getMessage()
to
String message = event.getFormattedMessage()

Related

Converting CSV file to JSON and send it to ActiveMQ queue

My aim is to read a CSV file, convert it to JSON and send the generated JSON one by one to ActiveMQ queue. My Code below:
final BindyCsvDataFormat bindy=new BindyCsvDataFormat(camelproject.EquityFeeds.class);
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
CamelContext _ctx = new DefaultCamelContext();
_ctx.addComponent("jms", JmsComponent.jmsComponentAutoAcknowledge(connectionFactory));
_ctx.addRoutes(new RouteBuilder() {
public void configure() throws Exception {
from("file:src/main/resources?fileName=data-sample.csv")
.unmarshal(bindy)
.marshal()
.json(JsonLibrary.Jackson).log("${body}")
.to("file:src/main/resources/?fileName=emp.json");
}
});
EquityFeeds is my POJO class in the above code.
Issues:
No Output is produced. "emp.json" file does not get generated at the given location.
Also how do I split the generated JSON into individual JSON's and send it to ActiveMQ queue like what I did for XML as below:
.split(body().tokenizeXML("equityFeeds", null)).streaming().to("jms:queue:xml.upstream.queue");
EquityFeeds (POJO):
#CsvRecord(separator = ",",skipFirstLine = true)
public class EquityFeeds {
#DataField(pos = 1)
private String externalTransactionId;
#DataField(pos = 2)
private String clientId;
#DataField(pos = 3)
private String securityId;
#DataField(pos = 4)
private String transactionType;
#DataField(pos = 5)
private Date transactionDate;
#DataField(pos = 6)
private float marketValue;
#DataField(pos = 7)
private String priorityFlag;
// getters and setters...
}
Please kindly help. Please tell me where I am going wrong. Need help desperately. Stuck in this issue and not able to move forward. Any help would be highly appreciated. I have really tried hard, searched Google and tried various options but nothing is working.
Please Note: I commented the .marshal() and .json() to check if the .unmarshal() is working but the unmarshal is also not working as "emp.json" is not getting created.
If nothing happens at all when starting the route then it is most likely due to the relative path you passed to the file component. Probably the execution directory of your Java process is not where you think it is and the file is not found. To simplify things I suggest you start with an absolute path. Once everything else is working figure out the correct relative path (your base should be the value of the user.dir system property).
Re your question about splitting the contents: This is answered in the documentation.
This works for me (Camel 3.1):
public class CsvRouteBuilder extends EndpointRouteBuilder {
#Override
public void configure() {
DataFormat bindy = new BindyCsvDataFormat(BindyModel.class);
from(file("/tmp?fileName=simpsons.csv"))
.unmarshal(bindy)
.split(body())
.log("Unmarshalled model: ${body}")
.marshal().json()
.log("Marshalled to JSON: ${body}")
// Unique file name for the JSON output
.setHeader(Exchange.FILE_NAME, () -> UUID.randomUUID().toString() + ".json")
.to(file("/tmp"));
}
}
// Use lombok to generate all the boilerplate stuff
#ToString
#Getter
#Setter
#NoArgsConstructor
// Bindy record definition
#CsvRecord(separator = ";", skipFirstLine = true, crlf = "UNIX")
public static class BindyModel {
#DataField(pos = 1)
private String firstName;
#DataField(pos = 2)
private String middleName;
#DataField(pos = 3)
private String lastName;
}
Given this input in /tmp/simpsons.csv
firstname;middlename;lastname
Homer;Jay;Simpson
Marge;Jacqueline;Simpson
the log output looks like this
Unmarshalled model: RestRouteBuilder.BindyModel(firstName=Homer, middleName=Jay, lastName=Simpson)
Marshalled to JSON: {"firstName":"Homer","middleName":"Jay","lastName":"Simpson"}
Unmarshalled model: RestRouteBuilder.BindyModel(firstName=Marge, middleName=Jacqueline, lastName=Simpson)
Marshalled to JSON: {"firstName":"Marge","middleName":"Jacqueline","lastName":"Simpson"}
and two json files are written in /tmp.

JSON Patch Request validation in Java

In my spring boot service, I'm using https://github.com/java-json-tools/json-patch for handling PATCH requests.
Everything seems to be ok except a way to avoid modifying immutable fields like object id's, creation_time etc. I have found a similar question on Github https://github.com/java-json-tools/json-patch/issues/21 for which I could not find the right example.
This blog seems to give some interesting solutions about validating JSON patch requests with a solution in node.js. Would be good to know if something similar in JAVA is already there.
Under many circumstances you can just patch an intermediate object which only has fields that the user can write to. After that you could quite easily map the intermediate object to your entity, using some object mapper or just manually.
The downside of this is that if you have a requirement that fields must be explicitly nullable, you won’t know if the patch object set a field to null explicitly or if it was never present in the patch.
What you can do too is abuse Optionals for this, e.g.
public class ProjectPatchDTO {
private Optional<#NotBlank String> name;
private Optional<String> description;
}
Although Optionals were not intended to be used like this, it's the most straightforward way to implement patch operations while maintaining a typed input. When the optional field is null, it was never passed from the client. When the optional is not present, that means the client has set the value to null.
Instead of receiving a JsonPatch directly from the client, define a DTO to handle the validation and then you will later convert the DTO instance to a JsonPatch.
Say you want to update a user of instance User.class, you can define a DTO such as:
public class UserDTO {
#Email(message = "The provided email is invalid")
private String username;
#Size(min = 2, max = 10, message = "firstname should have at least 2 and a maximum of 10 characters")
private String firstName;
#Size(min = 2, max = 10, message = "firstname should have at least 2 and a maximum of 10 characters")
private String lastName;
#Override
public String toString() {
return new Gson().toJson(this);
}
//getters and setters
}
The custom toString method ensures that fields that are not included in the update request are not prefilled with null values.
Your PATCH request can be as follows(For simplicity, I didn't cater for Exceptions)
#PatchMapping("/{id}")
ResponseEntity<Object> updateUser(#RequestBody #Valid UserDTO request,
#PathVariable String id) throws ParseException, IOException, JsonPatchException {
User oldUser = userRepository.findById(id);
String detailsToUpdate = request.toString();
User newUser = applyPatchToUser(detailsToUpdate, oldUser);
userRepository.save(newUser);
return userService.updateUser(request, id);
}
The following method returns the patched User which is updated above in the controller.
private User applyPatchToUser(String detailsToUpdate, User oldUser) throws IOException, JsonPatchException {
ObjectMapper objectMapper = new ObjectMapper();
// Parse the patch to JsonNode
JsonNode patchNode = objectMapper.readTree(detailsToUpdate);
// Create the patch
JsonMergePatch patch = JsonMergePatch.fromJson(patchNode);
// Convert the original object to JsonNode
JsonNode originalObjNode = objectMapper.valueToTree(oldUser);
// Apply the patch
TreeNode patchedObjNode = patch.apply(originalObjNode);
// Convert the patched node to an updated obj
return objectMapper.treeToValue(patchedObjNode, User.class);
}
Another solution would be to imperatively deserialize and validate the request body.
So your example DTO might look like this:
public class CatDto {
#NotBlank
private String name;
#Min(0)
#Max(100)
private int laziness;
#Max(3)
private int purringVolume;
}
And your controller can be something like this:
#RestController
#RequestMapping("/api/cats")
#io.swagger.v3.oas.annotations.parameters.RequestBody(
content = #Content(schema = #Schema(implementation = CatDto.class)))
// ^^ this passes your CatDto model to swagger (you must use springdoc to get it to work!)
public class CatController {
#Autowired
SmartValidator validator; // we'll use this to validate our request
#PatchMapping(path = "/{id}", consumes = "application/json")
public ResponseEntity<String> updateCat(
#PathVariable String id,
#RequestBody Map<String, Object> body
// ^^ no Valid annotation, no declarative DTO binding here!
) throws MethodArgumentNotValidException {
CatDto catDto = new CatDto();
WebDataBinder binder = new WebDataBinder(catDto);
BindingResult bindingResult = binder.getBindingResult();
binder.bind(new MutablePropertyValues(body));
// ^^ imperatively bind to DTO
body.forEach((k, v) -> validator.validateValue(CatDto.class, k, v, bindingResult));
// ^^ imperatively validate user input
if (bindingResult.hasErrors()) {
throw new MethodArgumentNotValidException(null, bindingResult);
// ^^ this can be handled by your regular exception handler
}
// Here you can do normal stuff with your cat DTO.
// Map it to cat model, send to cat service, whatever.
return ResponseEntity.ok("cat updated");
}
}
No need for Optional's, no extra dependencies, your normal validation just works, your swagger looks good. The only problem is, you don't get proper merge patch on nested objects, but in many use cases that's not even required.

IHttpActionResult return Json object

I have created one method in mvc api which returns string. But instead of returning string, I want to return Json Object. Here is my code.
[AllowAnonymous]
[HttpPost]
[Route("resetpassword")]
public IHttpActionResult ResetPassword(string email)
{
CreateUserAppService();
string newPassword =_userAppService.ResetPassword(email);
string subject = "Reset password";
string body = #"We have processed your request for password reset.<br/><br/>";
string from = ConfigurationManager.AppSettings[Common.Constants.FromEmailDisplayNameKey];
body = string.Format(body, newPassword, from);
SendEmail(email, subject, body, string.Empty);
return Ok<string>(newPassword);
}
Here it returns Ok<string>(newPassword); Now I want to return Json object. How can I return Json object?
Try that:
[AllowAnonymous]
[HttpPost]
[Route("resetpassword")]
public IHttpActionResult ResetPassword(string email)
{
//...
return Json(newPassword);
}
You are actually already using the key thing...
[HttpGet]
public IHttpActionResult Test()
{
return Ok(new {Password = "1234"});
}
You need to return it as CLR object so Web API serialize it to JSON, you can create your own POCO class or do it like this:
var passResponse = new
{
newPassword= yourNewPassword
};
But from security standpoint what you are doing is not correct, you should NEVER send plain passwords by email, you should reset user password by providing them a reset email link to your portal with some token and they should enter the new password. What you are doing here is not secure.
Create a return object.
public class PasswordResponse{
public string Password {get;set;}
//...other stuff to pass...
}
Then return an instance of the type in your response.
return OK(new PasswordResponse(){Password = newPassword});

Can the initial step of Box API use be automated?

I am the admin of an enterprise account at Box, and I'm working on an automated integration to update our users' email addresses and set their quotas, based on our enterprise' internal catalog.
Although the Box API documentation seems targeted at other usage scenarios, I can gather that once I get an access_token/refresh_token pair, that refresh_token is valid for 60 days, and I can get a new one at any time during that period.
Being of the conviction that "something always goes wrong", I'm just wondering if there is any way of automating the initial step of getting an access_token/refresh_token pair, that doesn't require a browser and manual interaction. I'm afraid that IF the refresh_token is lost or becomes invalid due to an update at Box or similar, no one here will remember how you went about getting that initial token pair by hand.
If there isn't a way to do it automatically, I'll just live with it, but I don't want to give up without having asked explicitly to know that I didn't just miss something. :-)
[Is there] any way of automating the initial step of getting an access_token/refresh_token pair, that doesn't require a browser and manual interaction
No, there are no authZ/authN shortcuts. That goes double for accounts that can manage an entire enterprise, given their power and reach.
I'm afraid ... no one here will remember how you went about getting that initial token pair by hand.
One way to resolve this might be to implement something like this:
Create a Box app with the 'manage an enterprise' scope.
Create a web app in your domain that simply implements the OAuth2 workflow.
Store the resulting access/refresh token pair in your persistence layer of choice
If/when something goes wrong due to authZ/authN issues, have your script notify a group email account that someone needs to go to the web app and request a new token.
There are sample web apps available to help get you started. (Python, Asp.NET MVC)
... The Box API documentation seems targeted at other usage scenarios...
A lot of the enterprise-specific stuff is found in the Users and Events parts of the API, and the As-User feature makes the entire API enterprise-ready. It's pretty neat.
You can build a workarround with an webclient like this:
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput;
import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
public class BoxAuth {
private String key;
private String email;
private String password;
private String redirectUrl;
private final String AUTH_URL;
public BoxAuth(String key, String email, String password, String redirectUrl) {
super();
this.key = key;
this.email = email;
this.password = password;
this.redirectUrl = redirectUrl;
this.AUTH_URL = "https://www.box.com/api/oauth2/authorize?response_type=code&client_id=" + key + "&redirect_uri=" + this.redirectUrl;
}
public String authorize() throws IOException, InterruptedException, ExecutionException {
System.out.println("AUTHORIZING: " + AUTH_URL);
final WebClient webClient = new WebClient(BrowserVersion.FIREFOX_17);
HtmlPage loginPage = webClient.getPage(AUTH_URL);
final HtmlPage grantAccessePage = this.authorizeLogin(loginPage);
return this.authorizeGrantAccess(grantAccessePage);
}
private HtmlPage authorizeLogin(HtmlPage page) throws IOException {
final HtmlForm loginForm = page.getFormByName("login_form");
loginForm.getInputByName("password");
final HtmlTextInput emailField = (HtmlTextInput) loginForm.getInputByName("login");
emailField.setValueAttribute(this.email);
final HtmlPasswordInput passwordField = (HtmlPasswordInput) loginForm.getInputByName("password");
passwordField.setValueAttribute(this.password);
final HtmlSubmitInput loginButton = loginForm.getInputByName("login_submit");
final HtmlPage result = loginButton.click();
try {
final HtmlForm test = result.getFormByName("login_form");
throw new Exception("BoxAPI: Wrong login data!!!");
} catch (ElementNotFoundException e) {
}
return result;
}
private String authorizeGrantAccess(HtmlPage grantAccessePage) throws IOException, InterruptedException, ExecutionException {
final HtmlForm grantAccessForm = grantAccessePage.getHtmlElementById("consent_form");
final HtmlButton grantAccess = grantAccessForm.getButtonByName("consent_accept");
final HtmlPage codePage = grantAccess.click();
URL url = codePage.getUrl();
String result = "";
if (url.toString().contains("&code=")) {
result = url.toString().substring(url.toString().indexOf("&code="));
result = result.replace("&code=", "");
}
return result;
}
}
as redirect_url u can use something like "https://app.box.com/services/yourservice"

Jersey 2.2: output xml OK, but fails on json

I've run into a weird problem.
I use Jersey 2.2 to do my restful web services (with jersey-media-moxy).
If I produce my output as application/xml, it runs fine.
But if produce my output as application/json, I get "Internal Server Error 500".
My dependency settings in ivy.xml are:
<dependency org="org.glassfish.jersey.core" name="jersey-server" rev="2.2"/>
<dependency org="org.glassfish.jersey.containers" name="jersey-container-servlet-core" rev="2.2"/>
<dependency org="org.glassfish.jersey.media" name="jersey-media-moxy" rev="2.2"/>
My service class is:
#Path("/projects/{companykey: [0-9]*}")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class ProjectResource {
private static Logger logger = Logger.getLogger(ProjectResource.class);
private final Application app = Application.getInstance();
#GET
public List<ProjectBase> getProjectBases(
#PathParam("companykey") String companyKeyStr) {
...
}
#GET
#Path("/{projectkey: [0-9]*}")
public ProjectBase getProjectBase(
#PathParam("companykey") String companyKeyStr,
#PathParam("projectkey") String projectKeyStr) {
int companyKey = Integer.valueOf(companyKeyStr);
int projObjKey = Integer.valueOf(projectKeyStr);
logger.debug(MessageFormat.format("get project {1} of company {0}",
companyKey, projObjKey));
ProjectBase project = null;
try {
project = app.getProjectIF().getProjectBase(companyKey, projObjKey);
if (project == null) throw new WebApplicationException(404);
return project;
} catch (ServerException se) {
logger.warn("get project fails ! " + se);
throw new WebApplicationException(500);
}
}
...
}
//class end
If I ask for the xml output (visit http://biz.loc.net:8080/tm/rest/projects/100/104), I get:
<projectBase>
<_checkTopicAccess>false</_checkTopicAccess>
<_checkTaskAccess>false</_checkTaskAccess>
....
If I ask for the json output, I get:
HTTP Status 500 - Internal Server Error
type Status report
message Internal Server Error
description The server encountered an internal error (Internal Server Error) that prevented it from fulfilling this request.
I do not find any error messages in my app's log file or Tomcat's log file, so I have no
idea what is going on.
Does anyone know any possible reason for this problem? Really appreciate ...
Can you show the entity code? Are you missing an empty constructor?
Thanks for your help, the following code snippet is my entity clas:
#XmlRootElement
public class ProjectBase implements UdaEnabled, SdaEnabled, FormBean {
private int projObjKey;
private String projName;
//...
private Timestamp createdAt;
//...
//...
#XmlElement(name = "createdAt")
#XmlJavaTypeAdapter(TimestampAdapter.class)
public Timestamp getCreatedAt() {
return createdAt;
}
// non-args Constructor
public ProjectBase() {
init();
}
}
It does has an empty constructor, although these's a init() inside.
As I said, I think it is weird because producing xml is OK.