Long dynamic routes in play framework 2 - java

I am developing an app for showing different aspects of cars. The app has a tree-like structure like this:
Country>Manufacturer>Model>Car_subsystem>Part
I want this structure to be reflected in browser's address bar:
http://localhost:9000/Germany/BMW/X6/Body/doors
Currently, I do it with play framework's dynamic routing as below:
GET /:Country controllers.Application.controllerFunction(Country : String)
GET /:Country/:Manufacturer controllers.Application.controllerFunction(Country : String, Manufacturer : String)
etc.
This works, but I don't like passing around 5 or 6 parameters to all my controller functions just for the paths to be shown beautifully! Is there any other way?

Just use Dynamic parts spanning several / as described in routing doc
route:
GET /Cars/*path controllers.Application.carResolver(path)
Action (simplest approach)
public static Result carResolver(String path) {
Car car = Car.findByPath(path);
return ok(carView.render(car));
}
so each car should have its field path filled with unique string ID, ie: Germany/BMW/X6, Germany/Mercedes/ASL` etc.
Of course it will be much better if you split the path arg by the slash first so you can use each part for displaying different views, 'translate' strings to real object ID etc etc.
public static Result carResolver(String path) {
String[] parts = path.split("/");
int partsLength = parts.length;
String country = (partsLength > 0) ? parts[0] : null;
String manufacturer = (partsLength > 1) ? parts[1] : null;
String carModel = (partsLength > 2) ? parts[2] : null;
// etc
switch (partsLength){
case 0: return countrySelect();
case 1: return allManufacturersIn(country);
case 2: return allModelsOf(manufacturer);
case 3: return singleViewFor(carModel);
// etc
}
return notFound("Didn't find anything for required path...");
}
TIP: "translating" strings to objects will require from you searching in DB by some field, so there's some advices:
Try to make sure that each model has a unique field for searching ie. Country should have unique name as probably you don't want to have Germany 1, Germany 2 etc.
This approach requires probably more time than searching by numeric ID's so try to cache somehow (mem-cache OR at least dedicated DB table) the result of resolving ie.:
Germany/BMW/X6 = Country: 1, Manufacturer: 2, CarModel: 3, action: "singleViewFor"

Related

Prevent coding multiple if/else statements with two changing expressions

I'm a beginner APEX developer (language based off of Java) and I was wondering if there is an efficient way to write conditional statements where two conditions change while the rest remains static.
For instance, in my code below, Countryy__c will change to say UK, US and Canada (besides France) and for each of these countries the Industry will change from Accounting to Medicine to Legal. Meanwhile, lead type and status will always remain as outbound and open respectively. Moreover each Country and Industry combination has a unique 'Owner ID'.
So in other words, there will be a total of 12 if/else statements with 12 different OwnerIds. Given that the code will be messy to maintain in the future if the number of countries and industries grow, is there a better way of coding this?
public static void changeOwnerToQueue(List<String> DeactivatedUserIds){
List<Lead> leadList = new List<Lead>();
List<lead> updatedQueue = new List<Lead>();
leadList = [SELECT Id, OwnerId, Countryy__c, Industry__c, Lead_Type__c, Status from lead
where OwnerId IN :DeactivatedUserIds];
for(Lead l : leadList){
if(l.Countryy__c == 'France' && l.Industry__c == 'Accounting' && l.Lead_Type__c == 'Outbound' && l.Status == 'Open'){
l.OwnerId = '00G5J000000pX41';
updatedQueue.add(l);
}
}
The most maintainable pattern for this kind of mapping in Apex is to use Custom Metadata. You'd create some Custom Metadata Type (MyOwnerMap__mdt), with fields for Country__c, Industry__c, and Owner__c. You'd create Custom Metadata records to represent all of your mappings. Then, in your code, you'd pull that data to create a Map, using a custom class as a key to represent the unique mapping of Country + Industry -> Owner:
class OwnerMapKey {
public String industry;
public String country;
public OwnerMapKey(String ind, String ctry) {
this.industry = ind;
this.country = ctry;
}
public Boolean equals(Object other) {
if (other instanceof OwnerMapKey) {
OwnerMapKey o = (OwnerMapKey)other;
return this.industry == o.industry && this.country == o.country;
}
return false;
}
public Integer hashCode() {
return (this.industry + this.country).hashCode();
}
}
List<MyOwnerMap__mdt> ownerMapValues = MyOwnerMap__mdt.getAll().values();
Map<OwnerMapKey, Id> ownerMap = new Map<OwnerMapKey, Id>();
for (MyOwnerMap__mdt eachOwnerMap: ownerMapValues) {
ownerMap.put(new OwnerMapKey(eachOwnerMap.Industry__c, eachOwnerMap.Country__c), eachOwnerMap.Owner__c);
}
Then, you can easily access the desired Owner value for any combination of Industry and Country. Note that you'll probably want to have a fallback if that entry is missing from your Custom Metadata.
someRecord.OwnerId = ownerMap.get(new OwnerMapKey(SOME_INDUSTRY, SOME_COUNTRY)) || defaultOwner;
(Disclaimer: above code written directly in Stack Overflow and untested).
The reason this pattern is valuable is that your solution then becomes admin-maintainable: you can change the mapping with no code changes and no deployment, just by altering the Custom Metadata records.

I want to filter a list of object in groovy

This is my groovy class
Asset {
ObjectState objectState = ObjectState.CURRENT
String description
#NotEmpty(message = "*Please provide a asset name")
#Length(min = 2, max = 50, message = "*Asset name must have characters between 2 and 50")
String assetName
#DBRef
Company company
}
I want to find those assets of a particular company which contains "test" in assetName and description
Now i implemented the business logic like this
#Override
Page<Asset> fetchAssetsBySearchStringAndObjectStateAndCompany(Company company, Pageable pageable, String searchQuery) {
ObjectState objectState = ObjectState.CURRENT
if (!pageable) {
pageable = PageRequest.of(0, 10, Sort.Direction.DESC, "lastUpdated")
}
if (searchQuery) {
Page<Asset> assets = assetRepository.findAllByCompanyAndObjectState(company, pageable, objectState)
List<Asset> filteredAssets = []
assets.each {
if (it.assetName.contains(searchQuery) || it.description.contains(searchQuery)) {
filteredAssets.add(it)
}
}
return filteredAssets // i want this list in pagination object
} else {
return assetRepository.findAllByCompanyAndObjectState(company, pageable, objectState)
}
}
I find all the assets of a company -
Filter out the "test" string using groovy closure - assets.each { }
Now my filteredAssets contains required result but i want this in pagination object
Now my question is
1- Is this approach is efficient
2- How to convert filteredAssets in Page
I also tried to use mongo native query but i am unable to convert it to spring boot
#Query('''{
'company': ?0,
$or :
[
{'assetName' : { $regex: ?1, $options:'i' }},
{'description' : { $regex: ?1, $options:'i' }},
]
}
''')
Page<Asset> findAllByCompanyAndAssetNameOrDescription(Company company, String assetName, Pageable pageable)
I don't have a specific answer but my suggestion is that your first approach is not going to work at a higher level because you are filtering the results after the pagination has been performed by the initial query. So you will potentially end up with less than the desired page size (or even an empty result) even though there are valid results that could have been returned by the query.
In other words, to achieve this you really do need to use the second approach of constructing a native query that incorporates the filtering. To resolve why that is not working, you would need to post more information about the kind of errors you are seeing (or possibly put it as a separate question and close this one out).
EDIT: to answer the question more specifically - if you choose to persist with the approach, it looks to me like you can construct your own Page object by harnessing the Spring data PageImpl object which has a usable constructor from a list of elements. You can simply construct this object from your filtered list of elements - ie: instead of this:
...
return filteredAssets
Do this:
return new PageImpl(filteredAssets)
If you want to be more idiomatic with your groovy code I would also suggest to change the filtering operation to use findAll. In that case the code gets more compact:
return new PageImpl(assets.findAll { it.assetName.contains(searchQuery) })
Once again though I would caution that from looking at your problem I don't think it's going to have the result you actually want.

I am only receiving partial output from an object Groovy

I have an object in Groovy and in this object there appears to be either a map. I am trying to get the "value" rather than the key from this objects map. How do I go about doing something like this?
This is the output I am receiving in my console the first is the object and the 2nd is the output for the property fromValues...
<com.atlassian.jira.issue.changehistory.ChangeHistoryItem#b791639b id=10130 changeGroupId=10113 userKey=Charley field=status projectId=10000 issueId=10217 issueKey=ICB-128 created=2019-03-12 14:19:22.0 nextChangeCreated=292278994-08-17 02:12:55.807 fromValues=[10003:To Do] toValues=[3:In Progress]>
[runner.ScriptRunnerImpl]: 10003
Here is my code below that I am working with so far in attempt to get this information: (Just so you understand the context... I am pulling an issue from Jira software and extracting the history of that particular issue.)
IssueManager issueManager = ComponentAccessor.getIssueManager();
def issue = issueManager.getIssueObject("ICB-128");
def changeHistoryManager = ComponentAccessor.getChangeHistoryManager()
def changeItems = changeHistoryManager.getAllChangeItems(issue)
//def changeItems = changeHistoryManager.getChangeHistoriesSince(issue, since)
changeItems.eachWithIndex { item, index ->
//log.warn(changeItems.dump())
def last_change = changeItems[index]
log.warn(last_change.dump())
def text = "LAST MODIFIED FIELD:"+ last_change["field"] + "; FROM VALUE:"+ last_change["fromValue"] + "; TO VALUE:"+ last_change["toValue"]
if (last_change["fromValue"])
{
log.warn(last_change["fromValue"])
}
}
Edit 1: I have corrected for what #vahid suggested and now have both values returned how I want it. I have tried numerous ways to call and retrieve specifically the value for that map and nothing has worked (.get or .value or even .key)... Any suggestions?
Edit 2: Turns out this extracted field is now considered as a type "hashmap"

How to implement this property file?

I have this piece of data (this is just one part of one line of the whole file):
000000055555444444******4444 YY
I implemented this CSV config file to be able to read each part of the data and parse it:
128-12,140-22,YY
The first pair (128-12) represent at what position in the line to start reading and then the amount of characters to read, that first pair is for the account number.
The second pair if for the card number.
And the thir parameter is for the registry name.
Anyways, what I do is String.split(","), and then assign the array[0] as the account number and so on.
But I want to change that CSV config file to a Property file, but I'm not sure of how to implement that solution, if I use a Properties file I'd have to add a bunch of if/then in order to properly map my values, here's what I'm thinking of doing:
Property cfg = new Property();
cfg.put("fieldName", "accountNumber");
cfg.put("startPosition", "128");
cfg.put("length", "12");
But I'd have to say if("fieldName".equals("accountNumber")) then assign accountNumber; is there a way to implement this in such a way that I could avoid implementing all this decisions? right now with my solution I don't have to use ifs, I only say accountNumber = array[0]; and that's it, but I don't think that's a good solution and I think that using Property would be more elegant or efficient
EDIT:
This probably needs some more clarification, this data I'm showing is part of a parsing program that I'm currently doing for a client; the data holds information for many many of their customers and I have to parse a huge mess of data that I receive from them, into something more readable in order to convert it to a PDF file, so far the program is under production but I'm trying to refactor it a little bit. All the customer's information is saved into different Registry classes, each class having it's own set of fields with unique information, lets say that this is what RegistryYY would look like:
class RegistryYY extends Registry{
String name;
String lastName;
PhysicalAddress address;
public RegistryYY(String dataFromFile) {
}
}
I want to implement the Property solution, because in that way, I could make the Property for parsing the file, or interpreting the data correctly to be owned by each Registry class, I mean, a Registry should know what data it needs from the data received from the file right?, I think that if I do it that way, I could make each Registry an Observer and they would decide if the current line read from the file belongs to them by checking the registry name stored in the current line and then they'd return an initialized Registry to the calling object which only cares about receiving and storing a Registry class.
EDIT 2:
I created this function to return the value stored in each line's position:
public static String getField(String fieldParams, String rawData){
// splits the field
String[] fields = fieldParams.split("-");
int fieldStart = Integer.parseInt(fields[0]); // get initial position of the field
int fieldLen = Integer.parseInt(fields[1]); // get length of field
// gets field value
String fieldValue = FieldParser.getStringValue(rawData, fieldStart, fieldLen);
return fieldValue;
}
Which works with the CSV file, I'd like to change the implementation to work with the Property file instead.
Is there any reason why you need to have the record layout exposed to the outside world ? does it need to be configurable ?
I think your proposed approached of using the Property file is better than your current approach of using the CSV file since it is more descriptive and meaningful. I would just add a "type" attribute to your Property definition as well to enforce your conversion i.e. for Numeric/String/Date/Boolean.
I wouldnt use an "if" statement to process your property file. You can load all the properties into an Array at the beginning and then iterate around the array for each line of your data file and process that section accordingly something like pseudo code below,
for each loop of data-file{
SomeClass myClass = myClassBuilder(data-file-line)
}
myClassBuilder SomeClass (String data-file-line){
Map<column, value> result = new HashMap<>
for each attribute of property-file-list{
switch attribute_type {
Integer:
result.put(fieldname, makeInteger(data-file-line, property_attribute)
Date:
result.put(fieldname, makeDate(data-file-line, property_attribute)
Boolean :
result.put(fieldname, makeBoolean(data-file-line, property_attribute)
String :
result.put(fieldname, makeBoolean(data-file-line, property_attribute)
------- etc
}
}
return new SomeClass(result)
}
}
If your record layout doesnt need to be configurable then you could do all the conversion inside your Java application only and not even use a Property file.
If you could get your data in XML format then you could use the JAXB framework and simply have your data definition in an XML file.
First of all, thanks to the guys who helped me, #robbie70, #RC. and #VinceEmigh.
I used YAML to parse a file called "test.yml" with the following information in it:
statement:
- fieldName: accountNumber
startPosition: 128
length: 12
- fieldName: cardNumber
startPosition: 140
length: 22
- fieldName: registryName
startPosition: 162
length: 2
This is what I made:
// Start of main
String fileValue = "0222000000002222F 00000000000111110001000000099999444444******4444 YY";
YamlReader reader = new YamlReader(new FileReader("test.yml"));
Object object = reader.read();
System.out.println(object);
Map map = (Map) object;
List list = (List) map.get("statement");
for(int i = 0; i < list.size(); i++) {
Map map2 = (Map) list.get(i);
System.out.println("Value: " + foo(map2, fileValue));
}
}
// End of main
public static String foo(Map map, String source) {
int startPos = Integer.parseInt((String) map.get("startPosition"));
int length = Integer.parseInt((String) map.get("length"));
return getField(startPos, length, source);
}
public static String getField(int start, int length, String source) {
return source.substring(start, start+length);
}
It correctly displays the output:
Value: 000000099999
Value: 444444******4444
Value: YY
I know that maybe the config file has some lists and other unnecessary values and what nots, and that maybe the program needs a little improvement, but I think that I can take it from here and implement what I had in mind.
EDIT:
I made this other one, using Apache Commons, this is what I have in the configuration property file:
#properties defining the statement file
#properties for account number
statement.accountNumber.startPosition = 128
statement.accountNumber.length = 12
statement.account.rules = ${statement.accountNumber.startPosition} ${statement.accountNumber.length}
#properties for card number
statement.cardNumber.startPosition = 140
statement.cardNumber.length = 22
statement.card.rules = ${statement.cardNumber.startPosition} ${statement.cardNumber.length}
#properties for registry name
statement.registryName.startPosition = 162
statement.registryName.length = 2
statement.registry.rules = ${statement.registryName.startPosition} ${statement.registryName.length}
And this is how I read it:
// Inside Main
String valorLeido = "0713000000007451D 00000000000111110001000000099999444444******4444 YY";
Parameters params = new Parameters();
FileBasedConfigurationBuilder<FileBasedConfiguration> builder =
new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties()
.setFileName("config.properties"));
try {
Configuration config = builder.getConfiguration();
Iterator<String> keys = config.getKeys();
String account = getValue(getRules(config, "statement.account.rules"), valorLeido);
String cardNumber = getValue(getRules(config, "statement.card.rules"), valorLeido);
String registryName = getValue(getRules(config, "statement.registry.rules"), valorLeido);
} catch (org.apache.commons.configuration2.ex.ConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// End of Main
public static String getRules(Configuration config, String rules) {
return config.getString(rules);
}
public static String getValue(String rules, String source) {
String[] tokens = rules.split(" ");
int startPos = Integer.parseInt(tokens[0]);
int length = Integer.parseInt(tokens[1]);
return getField(startPos, length, source);
}
I'm not entirely sure, I think that with the YAML file it looks simpler, but I really like the control I get with the Apache Commons Config, since I can pass around the Configuration object to each registry, and the registry knows what "rules" it wants to get, let's say that the Registry class only cares about "statement.registry.rules" and that's it, with the YAML option I'm not entirely sure of how to do that yet, maybe I'll need to experiment with both options a little bit more, but I like where this is going.
PS:
That weird value I used in fileValue is what I'm dealing with, now add nearly 1,000 characters to the length of the line and you'll understand why I want to have a config file for parsing it (don't ask why....clients be crazy)

Giving array as parameter to a Jena built-in

I need to create a new built-in for Jena. With this one I would like to be able to extract the minimum date from where it is.
I just wondering if it is possible to give a class of datas to a built-in instead of just one parameter.
Here is the bodyCall of my function :
#Override
public boolean bodyCall(Node[] args, int length, RuleContext context) {
System.out.println("Entra");
checkArgs(length, context);
BindingEnvironment env = context.getEnv();
Node n1 = getArg(0, args, context);
Node n2 = getArg(1, args, context);
//int count = 0;
//do{
//System.out.println("RULE"+context.getEnv().getGroundVersion(n2).getLiteralLexicalForm()); count ++;}while(count <2);
System.out.println("Date 1: " + n1 + " and Date 2: " + n2);
if (n1.isLiteral() && n2.isLiteral()) {
Object v1 = n1.getLiteralValue();
Object v2 = n2.getLiteralValue();
Node max = null;
if (v1 instanceof XSDDateTime && v2 instanceof XSDDateTime) {
XSDDateTime nv1 = (XSDDateTime) v1;
XSDDateTime nv2 = (XSDDateTime) v2;
Calendar data1 = new GregorianCalendar (nv1.getYears(), nv1.getMonths(), nv1.getDays());
Calendar data2 = new GregorianCalendar (nv2.getYears(), nv2.getMonths(), nv2.getDays());
SimpleDateFormat df = new SimpleDateFormat();
df.applyPattern("yyyy-dd-MM");
if (data1.compareTo(data2) > 0)
{
System.out.println("la data piu' grande e' DATA1: " +df.format(data1.getTime()));
max = args[0];
}
else
{
max = args[1];
System.out.print("la data piu' grande e' DATA1: " +df.format(data1.getTime()));
}
return env.bind(args[2], max);
}
}
// Doesn't (yet) handle partially bound cases
return false;
}
});
This is my simple rule:
#prefix ex: http://www.semanticweb.org/prova_rules_M#
#prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
[maxDate:
(?p rdf:type ex:Persona)
(?p http://www.semanticweb.org/prova_rules_M/persona#data_nascita ?c)
(?p http://www.semanticweb.org/prova_rules_M/persona#data_nascita ?d)
maxDate(?c,?d,?x)
-> print(?x)
]
I give to the built-in three parameters. Two for input and one for output.
My idea is using two varibles : ?c and ?d. In both of them there is a birthday date. I would like to get the first record from the ?c and the next record from the ?d. But, it looks like that Jena takes each time the first record.
Is it possible, by Java, telling that I want the second record and scroll the results ?
For example, my ontology is composed by two dates:
1)1992-04-13T00:00:00.0;
2)1988-04-25T00:00:00.0
I want to have 1) in ?c and 2) in ?d and then, make an algorithm to get the minimum between them.
ps : In the "bodyCall" above there is my try to get the maximum between to dates that I give to the rule. It works fine for this purpose.
Thank you all.
When you implement bodyCall(Node[], int, RuleContext) or headAction(Node[], int, RuleContext) as part of implementing a Builtin, you are given an array of arguments that represents the arguments to to the builtin. In a rule, you can hand any number of variables to the the builtin (not only one).
It loosely seems like (and you can correct me if I am misinterpreting your question) that you are looking to work over some class expression in order to get the data that you need. If your overall goal is to operate on 'a class of data', then there are multiple ways to achieve this.
(easiest) Formulate your class expression as statements within the body of the rule. This will ensure that your builtin is passed only individuals of the appropriate class. Chaining together multiple preconditions can allow you to only operate on certain individuals (a 'class of data').
(potentially nontrivial) If you intend to have your builtin operate on a class, use the RuleContext passed to your bodyCall(...) or headAction(...) in order to find individuals that satisfy your class expression (by calling RuleContext#find(...) or some other method).
As an example, let's say that we wanted to act on each member of the class urn:ex:Question. In the first solution, we'd formulate a rule similar to the following:
[eachIndividual: (?x rdf:type urn:ex:Question) -> builtin(?x)]
This would ensure that we'd operate on every single instance of urn:ex:Question. An example of the second solution would be to pass the class expression to your builtin directly. Your question does not indicate how you would identify the class in question, so I will arbitrarily assume that you are interested in classes which are rdfs:subClassOf urn:ex:Question.
[eachSubclass: (x? rdfs:subClassof urn:ex:Question) -> builtin(?x)]
In this case, you would need to somehow operate on your 'class of data' within your builtin. As mentioned previously, you could potentially use the RuleContext to do so.
EDIT
Let us assume that you have 40 individuals of type urn:ex:Question, and each individual has a property urn:ex:dateSubmitted that indicates when it was submitted. This can be rather trivially solved using a SPARQL query:
SELECT ?post WHERE {
?post a urn:ex:Question .
?post urn:ex:dateSubmitted ?date .
}
ORDER BY ?date
LIMIT 1
Edit 2
Based on the new information in your update, you can probably just modify your body call to look like the following:
#Override
public boolean bodyCall( final Node[] args, final int length, final RuleContext context )
{
checkArgs(length, context);
final Node n1 = getArg(0, args, context);
final Node n2 = getArg(1, args, context);
if (n1.isLiteral() && n2.isLiteral()) {
final Node max = Util.compareTypedLiterals(n1, n2) < 0 ? n2 : n1;
return context.getEnv().bind(args[2], max);
}
return false;
}

Categories

Resources