The class
public class Details {
private String name;
private String id;
//more attributes
}
I have a method which takes the http request and write an xml file based on the attribute the request wants (basically exporting specific fields that user wants).
Http Request body,
{
"isNameRequired": true,
"isIdRequired": true,
"isAttrRequired": false
// and other params to for filtering
}
Eg -> if the request wants only 'name' and 'id' values, the request will have booleans such as isNameRequired and IsIdRequired.
Based on this booleans my condition would look like
if (isNameRequired) {
// write name on file
}
if (isIdRequired) {
// write id on file
}
This is just an example. The actual attributes I want to write is more than 50 and I'm having that many booleans and multiple if checks to write.
Note : The entire process can happen n number of times.
There is list of Details (size > 100) which I need to loop through and write a xml based on users request.
If the user only needs name and id field, my xml should look like
<info>
<detail1>
<name>somename</name>
<id>someid</id>
</detail1>
<detail2>
<name>somename</name>
<id>someid</id>
</detail2>
</info>
like wise, if the request needs only name,
<info>
<detail1>
<name>somename</name>
</detail1>
<detail2>
<name>somename</name>
</detail2>
</info>
My question is, is this approach correct or is there a better way to handle this situation?
Well, it depends on your code but you could have some form of request handler that operates on a single requested element (e.g. "name"). If that fits your needs you could maintain a map of those handlers, get the ones you need and use them.
I'll add a simple example and assume your requests contain a number of strings for what's needed (if it's different I'll leave the conversion to you :) ):
interface RequestedElementHandler {
String getHandledElementName();
void handleElement(File xmlFile); //add anything else as needed
}
class NameHandler implements RequestedElementHandler {
public String getHandledElementName() { return "name"; }
public void handleElement(File xmlFile) {
//write name to file
}
}
And when handling your request:
Collection<String> requestedElements = ...;
//just an example, maintain a reusable map in a real world example
Map<String, RequestedElementHandler> handlers = new HashMap<>();
//you could use handlers.put("name", new NameHandler()) but doing it that way lets the handler define what elements it will work on
NameHandler nameHandler = new NameHandler();
handlers.put(nameHandler.getHandledElementName(), nameHandler());
//add other handlers as needed
for( String element : requestedElements ) {
RequestedElementHandler handler = handlers.get(element);
if( handler != null ) {
handler.handle(xmlFile);
}
}
Update:
Since your request body actually contains boolean properties you could modifiy the above approach as follows:
class NameHandler implements RequestedElementHandler {
//rename in interface
public String getPredicateName() { return "isNameRequired"; }
}
And when processing the request:
//extract the parameters from the request - how depends on whether you have
// - a ServletRequest: getParameterMap()
// - a Json object: try Jackon's ObjectMapper.readTree() and extract property names and values from the generic json object you get
// - anything else: can't tell without knowing but you should be able to do some research
Map<String, Boolean> parameters = ...
for( Map.Entry<String, Boolean> parameterEntry : parameters.entrySet() ) {
//reject FALSE and null
if( !Boolean.TRUE.equals(parameterEntry.getValue()) {
continue;
}
RequestedElementHandler handler = handlers.get(parameterEntry.getKey());
//use handler
}
Alternative:
Maintain a list of RequestHandler elements which all get the request and act on it if needed.
Example:
interface RequestHandler {
void handleRequest(Request r, File xmlFile);
}
class NameHandler implements RequestHandler {
void handleRequest(Request r, File xmlFile) {
if( !r.isNameRequired() ) {
return; //request doesn't require the name so skip
}
//add name to xml file
}
}
Usage:
Request request = ... //your request object
File xmlFile = ... //the file you want to write to
List<RequestHandler> handlers = ... //get from somewhere
handlers.forEach(handler -> handler.handleRequest(request, xmlFile));
Related
PLEASE READ THE EDIT BELOW
I am trying to create a file archive program that receives a POST request with a pdf file in it's requestBody embedded and then save that pdf file directly into a Database.
But I receive a "unsupported Media Type" and "Content type 'application/pdf;charset=UTF-8' not supported" error message on Postman, as well as this in Eclipse when sending the request:
2020-05-25 11:20:58.551 WARN 3944 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/pdf;charset=UTF-8' not supported]
below is my current code. I've looked up multiple similar questions on here, but to no avail.
I am using Postman to send out my POST requests.
Here is the code of my Controller Class.
private final DocumentRepository docRepository;
private final DocumentService documentService;
/*#Autowired
public Controller(DocumentRepository docRepository, FileStorage fileStorage, DocumentService documentService) {
this.docRepository = docRepository;
this.fileStorage = fileStorage;
this.doc
}*/
// Just for testing purposes
#GetMapping("/files")
public List<Document> files() {
return docRepository.findAll();
}
// POST Method. Add a new entry to the Database.
// WIP. Works, but creates 2 new columns "file_name" and "size_in_bytes" with the values
// instead of inserting them under the actual made "fileName" and "sizeInBytes" columns
#PostMapping(value="/document", consumes=MediaType.APPLICATION_PDF_VALUE)
public Optional<Document> postDocument(#RequestHeader(value="fileName") String fileName, #RequestBody MultipartHttpServletRequest file
) {
Optional<Document> result = documentService.postToDB(fileName, file);
return result;
}
}
And then the code for the Service Class that handles the logic.
#Service
public class DocumentService {
public static String directory = System.getProperty("user.dir")+ "/test_uploads";
private final DocumentRepository docRepository;
public DocumentService(DocumentRepository docRepository) {
this.docRepository = docRepository;
}
public Optional<Document> postToDB(String fileName, MultipartHttpServletRequest file) {
// temporary counter to see if there already is another entry with the same name
// temporary id to compare all the ids and iterate the one with the biggest value.
int counter=0;
int temp_id=0;
Timestamp current_timestamp = new Timestamp(Calendar.getInstance().getTime().getTime());
// This loop is supposed to look if there already is an entry with the same file name, if yes, then the counter will get iterated by 1.
// Bugged. It will still add a new entry that has a identical file name with another entry as of right now.
for(int i = 0; i < docRepository.count(); i++) {
if(docRepository.findAll().get(i).getFileName() == fileName) {
counter = counter + 1;
}
}
// This checks if the counter is less than one, thus asking if there are 1 or more file name duplicates
if (counter < 1) {
// This gets every ID of every entry and saves them on the temporary variable
for(int i = 0; i < docRepository.count(); i++) {
temp_id= docRepository.findAll().get(i).getId();
// It is then checked if the next entry is bigger than the previously saved value, if yes, then the variable gets overwritten
// with the bigger ID until the biggest ID stands.
if(docRepository.findAll().get(i).getId() > temp_id) {
temp_id = docRepository.findAll().get(i).getId();
}
}
// after the for-loop closes, the new File will get added with the biggest ID +1 and the fileName in the Header of the POST request.
Optional<Document> service_result;
try {
service_result = DataAccess.saveEntry(fileName,file, temp_id, docRepository, current_timestamp, 1 );
return service_result;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
else {
return Optional.empty();
}
}
}
and then finally, the DataAcces Class that saves the requests.
public class DataAccess {
public static Optional<Document> saveEntry(String fileName, MultipartHttpServletRequest file, Integer temp_id, DocumentRepository docRepository, Timestamp updated, Integer version) throws IOException {
docRepository.save(new Document(temp_id +1,fileName+ ".pdf",file.getFile(fileName).getBytes(), file.getFile(fileName).getSize(),updated, version));
return docRepository.findById(temp_id +1);
}
}
EDIT 1: I changed consumes to form-data and I send the POST request in Postman with a custom content-type header with application/form-data.
#PostMapping(value="/document", consumes="multipart/form-data")
public Optional<Document> postDocument(#RequestHeader(value="fileName") String fileName, #RequestBody MultipartHttpServletRequest file
) {
Optional<Document> result = documentService.postToDB(fileName, file);
return result;
}
Now it seems to work, but I now get a new exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
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.
Does anyone know how to test for different types of Collection in a route?
// This processor returns a Collection of 2 Sets
// 1. Set<GoodMessage>
// 2. Set<BadMessage>
.process(new MyGoodBadMessageProcessor())
// Split the result List
.split(body()).choice()
// How do you test for a Set<GoodMessage>??
.when(body().isInstanceOf(Set<GoodMessage>)
.to("direct:good")
.otherwise()
.to("direct:bad")
.endChoice()
Background: (In case someone can see a better way of doing this) I have a Processor that currently works as follows:
#Override
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
Set<UnknownMessage> unknownMessages = message.getBody(Set.class);
Set<GoodMessage> goodMessages = new HashSet<GoodMessage>();
for(UnknownMessage msg: unknownMessages) {
// Simplified logic here
if (msg.isGood()) {
goodMessages.add(msg.getGoodMessage());
}
}
message.setBody(goodMessages);
}
I'd like to update this as to now include the BadMessage(s) for reporting:
#Override
public void process(Exchange exchange) throws Exception {
Message message = exchange.getIn();
Set<UnknownMessage> unknownMessages = message.getBody(Set.class);
Set<GoodMessage> goodMessages = new HashSet<GoodMessage>();
Set<BadMessage> badMessages = new HashSet<BadMessage>();
List result = new ArrayList();
for(UnknownMessage msg: unknownMessages) {
// Simplified logic here
if (msg.isGood()) {
goodMessages.add(msg.getGoodMessage());
} else {
badMessages.add(msg.getBadMessage());
}
}
result.add(goodMessages)
result.add(badMessages)
message.setBody(result);
}
You cannot get the type of collection in this way (nothing to do with camel).
The way you've updated your process method does not need creating a different end point for bad messages.
One possible way to send this to a different end point based on message type is add a processor before the choice which inspects the type of the message and adds a header. Your choice statement can then work based on this header.
The following Predicate would work, although might give incorrect results when the Set is empty :/
Public class IsGoodMessage implements Predicate {
#Override
public boolean matches(Exchange exchange) {
Message message = exchange.getIn();
Set unknownSet = message.getBody(Set.class);
for (Object o : unknownSet) {
if (o instanceof GoodMessage) {
return true;
} else {
return false;
}
}
return false;
}
}
This helped:
How do I find out what type each object is in a ArrayList<Object>?
UPDATE: After some further reading, a better way to do this is to use a Header/Property to help distinguish the message type.
STEP 1: Update Processor to produce a Map that identifies different message types.
"GOOD_MSGS" -> List<GoodMessage>
"BAD_MSGS" -> List<BadMessage>
STEP 2: Create a splitter bean that splits this Map and then creates a header using the key of Map from the previous step.
(see "splitMessage" here http://camel.apache.org/splitter.html)
STEP 3: In the route use these headers to route the messages accordingly
I'm looking for some ideas on implementing a basic message factory that reads a header from an input stream and creates the appropriate message type based on the type defined in the message header.
So I have something like (roughly.. and I'm willing to change the design if a better paradigm is presented here)
class MessageHeader {
public String type;
}
class MessageA extends Message {
public static final String MESSAGE_TYPE = "MSGA";
public MessageA (DataInputStream din) {
var1 = din.readInt ();
var2 = din.readInt ()
// etc
}
}
and I essentially want to do something like this:
MessageHeader header = ... read in from stream.
if (header.type == MessageA.MESSAGE_TYPE) {
return new MessageA (din);
} else if (header.type == MessageB.MESSAGE_TYPE) {
return new MessageB (din);
}
Although this scheme works I feel like there could be a better method using a Map and an Interface somehow...
public interface MessageCreator {
public Message create (DataInputStream);
}
Map <String, MessageCreater> factory = new Map <String, MessageCreator> ();
factory.put (MessageTypeA.MESSAGE_TYPE, new MessageCreator () {
public Message create (DataInputStream din) {
return new MessageA (din); }});
...
// Read message header
Message createdMessage = Map.get (header.type).create (din);
But then whenever I want to use the message I have to use instanceof and cast to the correct subclass.
Is there a 3rd (better?) option? Maybe there's a way to accomplish this using templates. Any help is appreciated. Thanks
Edit: I guess it's important to note I want to "dispatch" the message to a function. So essentially I really want to do this:
MessageHeader header = ... read in from stream.
if (header.type == MessageA.MESSAGE_TYPE) {
handleMessageA (new MessageA (din));
} else if (header.type == MessageB.MESSAGE_TYPE) {
handleMessageB (new MessageB (din))
}
So a pattern that incorporates the factory and a dispatch would be perfect
How about letting the guy who creates the messages actually dispatch to a handler.
So you'd add a handler interface like this:
public interface MessageHandler {
void handleTypeA(MessageA message);
void handleTypeB(MessageB message);
}
Then you'd have a dispatcher which is basically the same thing as your MessageCreator, except it calls the correct method on the handler instead of returning the message object.
public interface MessageDispatcher {
void createAndDispatch(DataInputStream input, MessageHandler handler);
}
The implementation is then almost identical to the first code snippet you posted:
public void createAndDispatch(DataInputStream input, MessageHandler handler) {
MessageHeader header = ... read in from stream.
if (header.type == MessageA.MESSAGE_TYPE) {
handler.handleTypeA(new MessageA (din));
} else if (header.type == MessageB.MESSAGE_TYPE) {
handler.handleTypeB(new MessageB (din));
}
}
Now you only have the one spot in the code where you have to do a switch or if/else if and after that everything is specifically typed and there's no more casting.
I need to build a GWT application that will be called by an external application with specific URL parameters.
For example:
http://www.somehost.com/com.app.client.Order.html?orderId=99999.
How do I capture the orderId parameter inside the GWT application?
Try,
String value = com.google.gwt.user.client.Window.Location.getParameter("orderId");
// parse the value to int
P.S. GWT can invoke native javascript which means if javascript can do the stuff, GWT can do it too; e.g. in GWT, you can write
public static native void alert(String msg)
/*-{
$wnd.alert("Hey I am javascript");
}-*/;
In this case, you can even use existing javascript lib to extract param's value in the querystring.
GWT has a facility to get params from the URL:
String value = Window.Location.getParameter("param");
Make sure your URLs are in the form of:
http://app.com/?param=value#place instead of http://app.com/#place¶m=value
In order to get all params in a map, use:
Map<String, List<String>> map = Window.Location.getParameterMap();
I suggest you to use GWT MVP .
Assume that your url as
http://www.myPageName/myproject.html?#orderId:99999
And in your AppController.java --
Try as
......
public final void onValueChange(final ValueChangeEvent<String> event) {
String token = event.getValue();
if (token != null) {
String[] tokens = History.getToken().split(":");
final String token1 = tokens[0];
final String token2 = tokens.length > 1 ? tokens[1] : "";
if (token1.equals("orderId") && tonken2.length > 0) {
Long orderId = Long.parseLong(token2);
// another your operation
}
}
}
...........
Another option , you can also use with Spring MVC. Here is an example ...
// Below is in your view.java or presenter.java
Window.open(GWT.getHostPageBaseURL() + "customer/order/balance.html?&orderId=99999",
"_self", "enable");
// Below code in in your serverside controller.java
#Controller
#RequestMapping("/customer")
public class ServletController {
#RequestMapping(value = "/order/balance.html", method = RequestMethod.GET)
public void downloadAuctionWonExcel(#RequestParam(value = "orderId", required = true) final String orderId,
final HttpServletResponse res) throws Exception {
try {
System.out.println("Order Id is "+orderId);
// more of your service codes
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
You can use the Activities and Places to do that. When you create the Place for your page, you can set the orderId as a member. This member can be used afterwords when you create the Activity associated with the place (in ActivityMapper).
The only restriction is that you can't send the orderId as a normal parameter. You will have to use an url with this form :
127.0.0.1:60206/XUI.html?#TestPlace:orderId=1