Design - how map Request parameters data to domain model - java

This is a design question is the same problem in any language.
Guys as you do to map the controller to the domain model?
We have situations in general larger than ... consider the example objects .
situation.1 - We have a request that has all the parameters of the account ;
{ " id" : " 1 " , "name " : "test " , "some " : " xxx " } ............. and other fields .
situation.2 - can request that has to have a certain account parameters , for example in the case of an update;
{" id" , " 1" , "name " , " testUpdated "}
situation.3 - We have a request that has some parameters of the account , others have more like id as user together;
{ " id" : " 1 " , "user " : " xxx " , "service " : " yyy " } in which case each piece of the request will turn an object .
public class Account {
private Long id;
private String name ;
private String some ;
}
I see a few options ;
1 - Can I get AccountForm in the controller and set the properties for the Account object and others in CONTROLLER ;
+ ok for situation.1 situations 2, and situation.3
+ Separates the requisition of the object domain
- Pollutes the controller with code conversion
- Controller is full of setters .. if a higher class as a large object as a request is very confusing .
controller ( AccountForm from ) {
Account account = new Account ( )
account.setNome form.getNome = ();
account.setSome form.getSome = ();
Other outher = new Other ( ) ;
other.setSome ( form.getSome ( ) ) ;
}
2 - Can I get AccountRequest in the controller and have a method in itself as AccountRequest.getAccount ( ) to return a mapped model , in this case the mapping is at own Request object .
+ Separates the requisition of the object domain
+ Encapsulates the conversion in a place with easy access .
+ Meets situation.1 situation.2 and situation3 ;
- Request object has two responsibilities represent the request and map to a valid model .
controller ( AccountForm accountRequest ) {
Account account = accountRequest.getAccount ( ) ;
Outher outher accountRequest.getOther = ( )
}
3 - Can I get the controller Direct Account which had been filled with nulls .
+ Eliminate object request
- Serves only situation.1 situation.2 .
controller (Account account ) {
account.someMethod ();
}
4 - Outsource this mapping request parameters to another object mapper for request ..
+ Isolates logic mapping
- Until complexity for simpler cases are used as standard for all such a find by id .
- One more class for each request ;
In the case of API gets worse response has two further classes. speaking in terms of request for response .... AccountRequest, AccountRequestMapper, Account, AccountResponseMapper, AccountResponse ........
I'm doing more testing the Hybrid option 3 for simple cases (find ID or updates) .... with option 2 for example for more complex cases ...
What would be ideal / What is expressive and easy to maintain? Thank you.

Each of your ways listed in your question is technically "correct" :). Whether you choose one of them or use a different approach depends on the complexity of your use case.
Do read through the DDD bible to understand if you even need a Domain model.
Now let us assume that you do and also further assume that Account is an aggregate root, then you certainly do not want to be exposing your Domain Model to the external word.
You will probably use a DTO or such to collect enough information about the Account model and then delegate to a factory/builder who will build out the Account AR for you.
Again i would encourage you to read through the book and ascertain if you need the complexity.

Related

acquire field name of bean validation failure

So the class I'm doing JSR-303 bean validation on has two fields with the same pattern constraint applied to each:
#Column(name="test_suite_revision")
#XmlElement(name="test_suite_revision")
#NotNull
#Pattern(regexp = "\\d\\d-\\d\\d-\\d\\d\\d\\d", message = "value must be of the form xx-xx-xxxx")
private String revisionTestSuite;
#Column(name="test_revision")
#XmlElement(name="test_revision")
#NotNull
#Pattern(regexp = "\\d\\d-\\d\\d-\\d\\d\\d\\d", message = "value must be of the form xx-xx-xxxx")
private String revisionTest;
IMPORTANT - this class is NOT a form-backing class in a classic Spring MVC webapp but an entity class that lives at the base of a web service. So the validation is occuring in the service.
Now the web client that consumes the web service is a Spring MVC and does have a form-backing bean which ties to a jsp with places to put error messages.
So suppose a user enters an incorrectly-formatted string into one of the two fields. I can trap for it with this pretty standard code snippet
Set<ConstraintViolation<TestCase>> violations = validator.validate( permit);
if( !violations.isEmpty()) {
logger.debug( "basic validation FAILED with " + violations.size() + " errors");
Iterator<ConstraintViolation<TestCase>> iter = violations.iterator();
while( iter.hasNext()) {
ConstraintViolation<TestCase> cv = iter.next();
logger.debug( "invalidValue:" + cv.getInvalidValue());
logger.debug( "message:" + cv.getMessage());
ConstraintDescriptor<?> cd = cv.getConstraintDescriptor();
Map<String, Object> mapp = cd.getAttributes();
for( String keey : mapp.keySet()) {
logger.debug("mapp key:" + keey + ":" + mapp.get(keey));
}
which writes out
basic validation FAILED with 1 errors
invalidValue:050607
message:value must be of the form xx-xx-xxxx
mapp key:message:value must be of the form xx-xx-xxxx
mapp key:payload:[Ljava.lang.Class;#1367702
mapp key:flags:[Ljavax.validation.constraints.Pattern$Flag;#bf5210
mapp key:groups:[Ljava.lang.Class;#a49be5
mapp key:regexp:\d\d-\d\d-\d\d\d\d
Here's the rub: How does one figure out WHICH field failed validation? I can't seem to find a way to extract the field name , "revisionTest" or "revisionTestSuite"
from the ConstraintViolation object nor the ConstraintDescritpor object.
the getValidationAppliesTo() method newly available in version 1.1.0.Final of javax.validation-api seems promising but so far this method throws an AbstractMethodError during runtime. Ugh.
TIA,
Still-learning Steve
See ConstraintViolation#getPropertyPath method:
/**
* #return the property path to the value from {#code rootBean}
*/
Path getPropertyPath();
Path.Node#getName will give you the property name. For field names in nested beans, you have walk the path.

"merge" two readers into one writer

I need to implement the method
void filter(Reader mails, Reader groups, Writer users) throws IOException
in such a way that it would combine two pieces of data frow readers into one writer.
The file for mails would look like this:
Login;Email
login1;mail1#mail.com
login2;mail2#mail.com
login3;mail3#mail.com
login4;mail4#mail.com
and the file for groups would look like this:
Login;Group;
login1;Group1
login2;Group2
login3;Group3
login4;Group2
And the result of merging should look like this:
Login;Email;Group
login1;mail1#mail.com;Group1
login2;mail2#mail.com;Group2
login3;mail3#mail.com;Group3
login4;mail4#mail.com;Group2
So, what I came up with is: get a string from the first reader, then get another string from the second reader, manipulate them as needed and then write the result with writer.
But is there a way to make it differently or just more elegant?
PS: I'm obliged to use only Reader and Writer classes.
BTW: when I write something to a file with Writer and if I look into the file, I'll see something unreadable. But if I read the same file with Reader and then print it on the console, it looks ok. Is it normal? Or how can I write to the file to make it readable?
How about using a Map and a POJO container.
Pojo is
String email;
String group;
Then you have a hashmap
Map<String,EmailGroup> emailGroup = new HashMap<String,EmailGroup>();
Then your reading code will read the email list then populate the group after.
readEmail(emailGroup);
readGroup(emailGroup);
readEmail(Map<String,EmailGroup> map)
{
EmailGroup tempgroup;
if(map.contains(login))
{
tempGroup = map.get();
}
else
{
EmailGroup tempGroup = new EmailGroup();
}
tempGroup.setEmail(readEmailAddress);
map.put(login,tempGroup);
}
The readGroup does the same but calls setGroup();
This is not a full solution but should provide a suggestion on another possible way to resolve this issue.
If you want to implement a method with this signature, you could do this:
public static void main(String[] args) throws Exception {
String mails = "Login;Email\n"
+ "login1;mail1#mail.com\n"
+ "login2;mail2#mail.com\n"
+ "login3;mail3#mail.com\n"
+ "login4;mail4#mail.com";
String groups = "Login;Group;\n"
+ "login1;Group1\n"
+ "login2;Group2\n"
+ "login3;Group3\n"
+ "login4;Group2";
Reader mailsReader = new StringReader(mails);
Reader groupsReader = new StringReader(groups);
Writer mergedWriter = new StringWriter();
filter(mailsReader, groupsReader, mergedWriter);
System.out.println(mergedWriter.toString());
}
static void filter(Reader mails, Reader groups, Writer users) throws IOException {
BufferedReader mbr = new BufferedReader(mails);
BufferedReader gbr = new BufferedReader(groups);
BufferedWriter ubw = new BufferedWriter(users);
String ml = mbr.readLine();
String gl = gbr.readLine();
while (ml != null && gl != null) {
ubw.write(ml + ";" + gl.split(";")[1] + "\n");
ml = mbr.readLine();
gl = gbr.readLine();
}
ubw.flush();
}
Output:
Login;Email;Group
login1;mail1#mail.com;Group1
login2;mail2#mail.com;Group2
login3;mail3#mail.com;Group3
login4;mail4#mail.com;Group2
Looks like a join on the common key Login.
This should be easy to solve with some maps and some POJOs.
In the following some additional information that should help clarifying my thoughts.
Let's consider following data table. It contains people's first and last names, their email addresses and logins.
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ Login + Email + First name + Last name +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ smith + smith#mail.com + John + Smith +
+ miller + miller#mail.com + John + Miller +
+ jackson + mail#jackson.com + Scott + Jackson +
+ scott + me#scott.com + Scott + Jackson +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
This information is enough for simple data inquiries. If we want to know what John Smith's email address is, we have to perform a search in the first table. If we want to know Scott Jackson's address, we have a problem, since there are two people with the exact same name in our database.
So a mean to differentiate people is necessary, that is the column Login. It is unique in this table and thus can be used to avoid ambiguities.
Because Login has this attribute, it is called key.
This next table contains the group affiliation per login.
++++++++++++++++++++++++++++++++++++++++
+ Login + Group +
++++++++++++++++++++++++++++++++++++++++
+ smith + user +
+ miller + user +
+ jackson + admin +
+ scott + admin +
++++++++++++++++++++++++++++++++++++++++
Above table has Login as key, too. Using that property allows us to make another type of inquiry. It is possible to ask for group affiliation of persons, in order to do so, we have to retrieve the login of the user in question. Therefore the first table is used. Since Login is a key in the second table, it can be used to get the group affiliation.
This process is called joining, we combined the group affiliation from the second table, with the information on first and second names from the first table and used the login info as key.
A natural join performs this operation on all rows. At this point, I hope it is clear why, I proposed a join. The 2 files correspond to 2 tables (that are invariant to changes in the order of the data). Joining them results in a third table that contains all the information. Printing that table is an answer to OPs question.
To utilize the power of the underlying relational algebra a simple java.util.Map with Login as key can be used.

java neo4j rest how to get the url of a created node

I am creating some nodes within a transaction in neo4j using the rest api. After all nodes have been created (typically between 3 and 5 in one transaction), I have to create some relationships between them. To do this I need, of course the location of the nodes, and this is the source of my problem. I can't figure out how to get this location.
According to documentation, I should be able to get the location of a node from the response-object, after creating the node, like so:
nodeLocation = response.getLocation();
But in a transaction, this of course returns the url of the transaction:
http://localhost:7474/db/data/transaction/108
Then I thought, if I query for the just created node, maybe in that response I can find the location. Again, according to documentation, the node location should be presented in the json-structure extensions in the field self.
"self" : "http://localhost:7474/db/data/node/357",
But my response to the query does not seem to contain an extension structure.
This is the query I'm using:
{"statements": [ {"statement": "MATCH (p:POST {sn_id: 'TW', id: '536982477664190465'} ) RETURN p"} ] }
I send it to the open transaction, and I get this back:
GET to http://localhost:7474/db/data/transaction/108 returned status code 200, returned data: {"commit":"http://localhost:7474/db/data/transaction/108/commit","results":[{"columns":["p"],"data":[]}],"transaction":{"expires":"Mon, 24 Nov 2014 20:40:34 +0000"},"errors":[]}
Just for completeness, this is the code for my query:
String payload = "{\"statements\": "
+ "[ "
+ "{\"statement\": "
+ "\"MATCH (p:POST {sn_id: 'TW', id: '536982477664190465'} ) RETURN p\""
+ "} "
+ "] "
+ "}";
logger.trace("sending cypher {} to endpoint {}", payload, endpointLoc);
WebResource resource = Client.create().resource( endpointLoc );
ClientResponse response = resource
.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON )
.entity( payload )
.get(ClientResponse.class);
//.post( ClientResponse.class );
String responseEntity = response.getEntity(String.class).toString();
int responseStatus = response.getStatus();
logger.trace("GET to {} returned status code {}, returned data: {}",
endpointLoc, responseStatus,
responseEntity);
JSONParser reponseParser = new JSONParser();
Object responseObj = reponseParser.parse(responseEntity);
JSONObject jsonResponseObj = responseObj instanceof JSONObject ?(JSONObject) responseObj : null;
if(jsonResponseObj == null)
throw new ParseException(0, "returned json object is null");
String result = (String) jsonResponseObj.get("results").toString();
logger.trace("result is {} ", result);
String error = (String) jsonResponseObj.get("errors").toString();
Am I missing something? Do I need to use a special call?
Can someone help me with this? Thanks in advance,
Christian
What do you need the node-URL for?
That's the old RESTful representation. You can either get it by using the old HTTP endpoint /db/data/cypher or better by specifying the (very verbose) resultDataContents type REST
You can also specify other types like "row" and "graph" in parallel.
{"statements": [
{"statement": "MATCH (p:POST {sn_id: 'TW', id: '536982477664190465'} ) RETURN p",
"resultDataContents":["REST"]}
] }

JSF Flash arguments not carried over to next view (and no ?faces-redirect=true in browser URL)

We have a page that contains a command link:
<h:commandLink value="Go to Result Manager"
action="#{resultManagerDashboardHelper.navigateToTargetAll()}" />
This is supposed to redirect to the Result Manager view, secretly passing a list of two status flags representing "TODO" and "DONE", so that the filter will be set to "all statuses".
ResultManagerDashboardHelper.java:
#Named
#ViewScoped
public class ResultManagerDashboardHelper
{
...
public static final String SECRET_ARGS_KEY = "secretArgs";
...
private String navigateToPage( String outcome, String... args )
{
List<String> argsList = Arrays.asList( args );
FacesContext.getCurrentInstance().getExternalContext().getFlash().put( SECRET_ARGS_KEY, argsList );
String fullOutcome = outcome + "?faces-redirect=true";
System.out.println( "Setting flash secret args to " + argsList );
System.out.println( "Redirecting from '" + this.outcomeMapper.getCurrentOutcome() + "' to '" + fullOutcome + "'" );
return fullOutcome;
}
private String navigateToTargetPage( String... args )
{
return this.navigateToPage( "/view/resultmgmt/resultManager", args );
}
public String navigateToTargetAll()
{
return this.navigateToTargetPage( "TODO", "DONE" );
}
}
ResultManagerFilterHandler.java:
#Named
#ViewScoped
public class ResultManagerFilterHandler
{
...
// status flags secretly coming in via Flash from dashboard
private List<String> secretStatusFlags;
#Override
#PostConstruct
public void init()
{
...
#SuppressWarnings( "unchecked" )
List<String> secretStatusFlags = ( List<String> ) FacesContext.getCurrentInstance().getExternalContext().getFlash().get( ResultManagerDashboardHelper.SECRET_ARGS_KEY );
if ( secretStatusFlags != null )
{
System.out.println( "ResultManagerFilterHandler secret arguments have arrived via JSF Flash! Size = " + secretStatusFlags.size() );
}
else
{
System.out.println( "ResultManagerFilterHandler: NO secret arguments have arrived via JSF Flash!" );
}
// sits in view scope waiting to be picked up by search routine in ResultManager (also view-scoped)
this.secretStatusFlags = secretStatusFlags;
}
}
ResultManager.java:
#Named
#ViewScoped
public class ResultManager
{
...
#Inject
private ResultManagerFilterHandler filterHandler;
#Override
public void searchInitially()
{
// get Flash object from filter handler
List<String> statusFlags = this.getFilterHandler().getSecretStatusFlags();
System.out.println( "ResultManager statusFlags = " + statusFlags );
// if flash args have been passed, prefer that view over the standard one
if ( statusFlags != null && !statusFlags.isEmpty() )
{
System.out.println( "Flash search!" );
// set to today and decrypt the passed status flags (this is supposed to override the default search filter!)
this.filterHandler.setSelectedPeriod( EPeriod.TODAY );
this.filterHandler.calculateBeginEndDates( this.filterHandler.getSelectedPeriod() );
this.filterHandler.setTodoOnly( statusFlags.size() == 1 && statusFlags.get( 0 ).equals( "TODO" ) );
this.search();
}
else
{
System.out.println( "Non-flash search!" );
super.searchInitially();
}
}
}
When clicking the aforementioned link, the browser is redirected to the correct view. Then an event on the resultManager.xhtml page
<f:event type="preRenderView"
listener="#{resultManager.searchInitially}" />
calls the searchInitially method, which is supposed to pick up the stored status flags from the ResultManagerFilterHandler bean. However the Flash arguments aren't there:
18:58:42,840 INFO [] (134) Setting flash secret args to [TODO, DONE]
18:58:42,840 INFO [] (134) Redirecting from '/view/dashboard' to '/view/resultmgmt/resultManager?faces-redirect=true'
18:58:43,039 INFO [] (135) ResultManagerFilterHandler: NO secret arguments have arrived via JSF Flash!
18:58:44,350 INFO [] (135) ResultManager statusFlags = []
18:58:44,350 INFO [] (135) Non-flash search!
Q:
What am I doing wrong? How do I get it to work?
Note that ?faces-redirect=true URL parameter isn't appended to the URL... I wonder why?? Might this be the reason for the empty flash? Or is it normal behavior?
We're using Mojarra 2.1.22, which is known to have some issues keeping Flash instances longer, but this is something I'd like to solve later.
PS: sorry for the complex example, in reality it is even more complex... :-/
PPS: oh and never mind the combination of #Named and #ViewScoped, we're using Seam 3, which replaces the JSF #ViewScoped with a CDI-compatible one.
First of all, I encourage you not to use two #ViewScoped beans for the same view. You can achieve this functionality either carrying the functionallity of ResultManagerFilterHandler to your ResultManager. If you however want to reuse ResultManagerFilterHandler methods in other beans, just use a plain class (not managed by JSF) and make your managed beans extend from it.
On the other hand, there's no point for using 2.1.22 anymore. Just go with latest 2.1.x branch version, it'll be fully compatible with the code you have and brings lots of bug fixes, specially the ones related with flash scope.
Related with faces-redirect="true", keep in mind the Faces Servlet processes your redirection url before sending it to the browser. This parameter only tells that you want to perform a redirection, the servlet will remove it after evaluating.
See also:
Understand Flash Scope in JSF2
Exception about flash in Mojarra JSF

Neo4j : Retrieving All Nodes and Relationship connected to a Node in Neo4j Rest OR through Cypher

I want to Retrieve all nodes and relationship connected to a node.
I Tried to do this in two ways:
1st Through Neo4j REST API i Tried this
URI traverserUri = new URI( startNode.toString() + "/traverse/node" );
WebResource resource = Client.create()
.resource( traverserUri );
String jsonTraverserPayload = t.toJson();
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON )
.type( MediaType.APPLICATION_JSON )
.entity( jsonTraverserPayload )
.post( ClientResponse.class );
System.out.println( String.format(
"POST [%s] to [%s], status code [%d], returned data: "
+ System.getProperty( "line.separator" ) + "%s",
jsonTraverserPayload, traverserUri, response.getStatus(),
response.getEntity( String.class ) ) );
response.close();
And get Following Response :
[ {
"outgoing_relationships" : "http://localhost:7474/db/data/node/82/relationships/out",
"data" : {
"band" : "The Clash",
"name" : "Joe Strummer"
},
"traverse" : "http://localhost:7474/db/data/node/82/traverse/{returnType}",
"all_typed_relationships" : "http://localhost:7474/db/data/node/82/relationships/all/{-list|&|types}",
"property" : "http://localhost:7474/db/data/node/82/properties/{key}",
"all_relationships" : "http://localhost:7474/db/data/node/82/relationships/all",
"self" : "http://localhost:7474/db/data/node/82",
"properties" : "http://localhost:7474/db/data/node/82/properties",
"outgoing_typed_relationships" : "http://localhost:7474/db/data/node/82/relationships/out/{-list|&|types}",
"incoming_relationships" : "http://localhost:7474/db/data/node/82/relationships/in",
"incoming_typed_relationships" : "http://localhost:7474/db/data/node/82/relationships/in/{-list|&|types}",
"create_relationship" : "http://localhost:7474/db/data/node/82/relationships"
}, {
"outgoing_relationships" : "http://localhost:7474/db/data/node/83/relationships/out",
"data" : {
}]
But the problem is if i want to see the relationship of this node again i will have to hit the link "http://localhost:7474/db/data/node/82/relationships/all"
Cant we get Data in which Node and its relationship are shown directly instead of link to relationship without hitting the link again????
2nd thing I have tried to do is to get this from cypher query :
START a=node(3)
MATCH (a)-[:KNOWS]->(b)-[:KNOWS]->(c)-[:KNOWS]->(d)
RETURN a,b,c,d
But this also didn't work because at (b) and (c) there will be multiple values as a result for which i will have to iterate and write another query
Cant we get this done in single query because i have so many connected relationship that it is getting hard to iterate again and again. Any Help would be Appreaciated.
It's easy to get all nodes connected to a given node with Cypher
START a=node(3)
MATCH (a)-[:KNOWS*]->(d)
RETURN distinct d
But if you have large number of connected nodes and deep connections, you might not get a good performance.
If you know the bounds of the connections, specify it explicitly in the query would be helpful for performance,
START a=node(3)
MATCH (a)-[:KNOWS*1..3]->(d)
RETURN Distinct d
Regarding the question about multiple nodes, or duplicate nodes. I understand what you mean. Here is something I did with such a query to weed out duplicates. More about if a KNOWS b which KNOWS c, but c is really a. Kind of like that. We can use something like WHERE NOT
start player=node({0})
match player-[:player.active*2..2]-friendsOfFriends,
where not(player-[:player.active]-friendsOfFriends) and player <> friendsOfFriends
return distinct friendsOfFriends
order by friendsOfFriends.username asc
If you make your query
MATCH (a)-[r1:KNOWS]->(b)-[r2:KNOWS]->(c)-[r3:KNOWS]->(d) RETURN a,r1,b,r2,c,r3,d;
The r(x) will return the respective details regarding the relationship. There will be a "row" for each path that matches the query.
If you define your deserializer so it recognizes the r(x) and constructs a relationship rather than an entity then you should be able to do it all in one query.

Categories

Resources