I am trying to figure out a way to version complex Data Objects in Java. For this, I intend to store both the initial document and the diffs in a database (as well as the current object for faster access), and then be able to restore any version from the initial document by applying the diffs onto it as patches.
Thus far, I have come across Javers and java-object-diff for that, however...
Javers has no way of patching
In the java-object-diff documentation, it seems like you need both the initial as well as the target version to apply diffs, which seems kinda pointless since I want to get the target version
This seems like something that someone would have implemented a long time ago. However, thus far, I have not been able to figure out a working solution.
Can someone please help me with this?
For reference, I am currently trying to get it to work somewhat like this:
#Test
void applyDiffDeltaShouldCorrectlyApplyDeltaToCity() {
GameCharacter sylvia = GameCharacters.sylvia();
GameCharacter sylviaEdited = GameCharacters.sylviaEdited();
DiffNode characterDelta
= objectDiffer.compare(sylvia, sylviaEdited);
GameCharacter sylviaWithAppliedDelta
= applyDelta(sylvia, characterDelta);
assertEquals(sylviaEdited.getName(), sylviaWithAppliedDelta.getName());
}
public static <T> T applyDelta(T baseObject, DiffNode diff) {
//This won't work, but it would be cool if it did:
T updatedObject = baseObject + diff;
return updatedObject ;
}
Related
I am running a hierachical Spring Statemachine and - after walking through the inital transitions into state UP with the default substate STOPPED - want to use statemachine.getState(). Trouble is, it gives me only the parent state UP, and I cannot find an obvious way to retrieve both the parent state and the sub state.
The machine has states constructed like so:
StateMachineBuilder.Builder<ToolStates, ToolEvents> builder = StateMachineBuilder.builder();
builder.configureStates()
.withStates()
.initial(ToolStates.UP)
.state(ToolStates.UP, new ToolUpEventAction(), null)
.state(ToolStates.DOWN
.and()
.withStates()
.parent(ToolStates.UP)
.initial(ToolStates.STOPPED)
.state(ToolStates.STOPPED,new ToolStoppedEventAction(), null )
.state(ToolStates.IDLE)
.state(ToolStates.PROCESSING,
new ToolBeginProcessingPartAction(),
new ToolDoneProcessingPartAction());
...
builder.build();
ToolStates and ToolEvents are just enums. In the client class, after running the builder code above, the statemachine is started with statemachine.start(); When I subsequently call statemachine.getState().getId(); it gives me UP. No events sent to statemachine before that call.
I have been up and down the Spring statemachine docs and examples. I know from debugging that the entry actions of both states UP and STOPPED have been invoked, so I am assuming they are both "active" and would want to have both states presented when querying the statemachine. Is there a clean way to achieve this ? I want to avoid storing the substate somewhere from inside the Action classes, since I believe I have delegated all state management issues to the freakin Statemachine in the first place and I would rather like to learn how to use its API for this purpose.
Hopefully this is something embarrasingly obvious...
Any advice most welcome!
The documentation describes getStates():
https://docs.spring.io/spring-statemachine/docs/current/api/org/springframework/statemachine/state/State.html
java.util.Collection<State<S,E>> getStates()
Gets all possible states this state knows about including itself and substates.
stateMachine.getState().getStates();
to wrap it up after SMA's most helpful advice: turns out the stateMachine.getState().getStates(); does in my case return a list of four elements:
a StateMachineState instance containing UP and STOPPED
three ObjectState instances containing IDLE, STOPPED and PROCESSING,
respectively.
this leads me to go forward for the time being with the following solution:
public List<ToolStates> getStates() {
List<ToolStates> result = new ArrayList<>();
Collection<State<ToolStates, ToolEvents>> states = this.stateMachine.getState().getStates();
Iterator<State<ToolStates, ToolEvents>> iter = states.iterator();
while (iter.hasNext()) {
State<ToolStates, ToolEvents> candidate = iter.next();
if (!candidate.isSimple()) {
Collection<ToolStates> ids = candidate.getIds();
Iterator<ToolStates> i = ids.iterator();
while (i.hasNext()) {
result.add(i.next());
}
}
}
return result;
}
This maybe would be more elegant with some streaming and filtering, but does the trick for now. I don't like it much, though. It's a lot of error-prone logic and I'll have to see if it holds in the future - I wonder why there isn't a function in the Spring Statemachine that gives me a list of the enum values of all the currently active states, rather than giving me everything possible and forcing me to poke around in it with external logic...
We have a piece of production code in our application that reads raw DB rows something like that:
List<Map<String, Object>> results =
txnNamedJdbcTemplate.queryForList(
transactionDbQueries.getProperty(QUERY_FETCH_REPORT_DETAILS).trim(), paramMap);
, then it does a whole load of field transformations to produce an object of desired structure:
private Report extractReportData(long reportId, List<Map<String, Object>> results) {
Map<String, Object> reportRow = results.get(0);
Timestamp completeTs = (Timestamp) reportRow.getOrDefault(RS_PARAM_END_DATETIME, null);
Timestamp lastOpenedTs =
(Timestamp) reportRow.getOrDefault(RS_PARAM_LAST_OPENED_DATETIME, null);
String reportData =
reportRow.get(RS_PARAM_REPORT_DATA) == null
? StringUtils.EMPTY
: ((PGobject) reportRow.get(RS_PARAM_REPORT_DATA)).getValue();
Duration executionTime =
reportRow.containsKey(RS_PARAM_DURATION)
? Duration.ofSeconds(Long.parseLong(reportRow.get(RS_PARAM_DURATION).toString()))
: null;
String reportRunLevel = (String) reportRow.getOrDefault(RS_PARAM_ACCESS_LEVEL, null);
boolean reportOpened = (Boolean) reportRow.getOrDefault(RS_PARAM_OPENED_STATUS, Boolean.FALSE);
String reportCategory = (String) reportRow.getOrDefault(RS_PARAM_REPORT_CATEGORY, null);
Long scheduledId =
reportRow.get(RS_PARAM_SCHEDULED_ID) != null
? Long.parseLong(reportRow.get(RS_PARAM_SCHEDULED_ID).toString())
: null;
return Report.builder()
.reportId(reportId)
.reportName((String) reportRow.get(RS_PARAM_REPORT_NAME))
.reportType((String) reportRow.get(RS_PARAM_REPORT_TYPE))
.reportCategory(reportCategory)
.reportStatusDesc(
ReportStatus.values()[(Integer) reportRow.get(RS_PARAM_STATUS_ID) - 1].getDesc())
.submittedBy((String) reportRow.get(RS_PARAM_USER_NAME))
.submittedById((int) reportRow.get(RS_PARAM_USER_ID))
.submittedTime((Timestamp) reportRow.get(RS_PARAM_SUBMIT_DATETIME))
.completedTime(completeTs)
.lastOpeningTime(lastOpenedTs)
.reportData(reportData)
.reportRunLevel(reportRunLevel)
.opened(reportOpened)
.executionTime(executionTime)
.scheduledId(scheduledId)
.build();
}
I know it's not the prettiest bit of code, but that's legacy system and is beside the point.
Now, I had to test that code to make sure we can read the same object and verify the fields, so I had the following 3 scenarios:
Clone this code in a test class. I've built a test utility that does just that. Clearly, that resulted in duplication of the same un-pretty code, which is not ideal.
An alternative way would be to outsource this code to some utility class and let both prod and test code use it, for the sake of avoiding duplication.
Also, there is a way to change access modifier of the production class and let test that.
That is an integration test that uses proper DB instance so using mocks isn't gonna do - we need to read actual data. The example is actually not the best and is only for illustration. The main question: do we reuse production code in the tests or is it best to duplicate it?
I strongly lean toward option #1, on the premise that if we introduce a bug in the transformation in the prod code - how do we detect it? For that, I believe code segregation is the best way.
Are there any other opinions or reasons behind these, please?
If you going to duplicate the code in test scope, what if some bug fix has been added in future and what if your test did't alert you about something happened on your production code flow?Also think about how you maintain your duplicate code in future.
Usually tests are not only meant to validate your present code.But they also document and feedback/system that will notify any changes/breakages in code.
If you couldn't write a test case for your piece, then its a time to refactor your code.
On a practical note it's not always good idea to dismantle the existing working piece, it may need bit time and depth knowledge on history of code base and strong test suite.But its worth to get our hands little dirty
I would suggest any one below,
Refactor your code, align it with Single Responsibility and cover each unit with test case(as you stated atleast you can move them to some util).
integration test
use powermock or any similar tool to test private methods(Note : Some organisation may not like tools like powermock for security concerns)
I am trying to create a JIRA plugin that does the following:
For each issue, takes all linked issues which are linked by "duplicates" or "is duplicated by" (or other predefined link types).
For each such issue, get a list (not necessarily a List object) of the voters on that issue.
My problem is that the javadoc has little to no information. Following a tutorial, I currently have:
public class VotersCount extends AbstractJiraContextProvider {
#Override
public Map<String, Integer> getContextMap(User user, JiraHelper jiraHelper) {
Map<String, Integer> contextMap = new HashMap<>();
Issue currentIssue = (Issue) jiraHelper.getContextParams().get("issue");
// Issue[] linkedIssues = currentIssue.getLinkedIssuesBy(...); //Step 1 mock code
// Voter[] voters = linkedissues[3].getVoters(); //Step 2 mock code
int count = voters.length; //Pretend there is some calculation here
contextMap.put("votersCount", count);
return contextMap;
}
}
(and I use votersCount in the .vm file.)
However, I see no explanation in the javadocs for AbstractJiraContextProvider and getContextMap so I'm not even sure if it's the right approach.
In my own research I found the class ViewVoters which has the method Collection<UserBean> getVoters(), which is something I can work with, but I don't know how to obtain or construct such an object in a way which will interact with a given issue.
I am looking for a working code to replace my 2 lines of mock code.
1) Use one of the methods from IssueLinkService. Maybe getIssueLinks
2) issueVoterAccessor.getVoterUserkeys
Instances of IssueLinkService and IssueVoterAccessor should be injected as parameters to constructor of your VotersCount.
I solved it by using the following:
To get issues linked to Issue issue by specified link types:
LinkCollection linkCollection = ComponentAccessor.getIssueLinkManager().getLinkCollectionOverrideSecurity(issue);
Set<IssueLinkType> linkTypes = linkCollection.getLinkTypes();
// Perform operations on the set to get the issues you want.
for (IssueLinkType linkType : linkTypes) {
List<Issue> l1 = linkCollection.getOutwardIssues(linkType.getName());
List<Issue> l2 = linkCollection.getInwardIssues(linkType.getName());
}
To get all the voters on Issue issue:
ComponentAccessor.getVoteManager().getVoterUserkeys(issue);
I was later shown that one can extend CalculatedCFType and override getValueFromIssue which hands you the current issue as a parameter instead of the using
Issue currentIssue = (Issue) jiraHelper.getContextParams().get("issue");
I am implementing REST through RESTlet. This is an amazing framework to build such a restful web service; it is easy to learn, its syntax is compact. However, usually, I found that when somebody/someprogram want to access some resource, it takes time to print/output the XML, I use JaxbRepresentation. Let's see my code:
#Override
#Get
public Representation toXml() throws IOException {
if (this.requireAuthentication) {
if (!this.app.authenticate(getRequest(), getResponse()))
{
return new EmptyRepresentation();
}
}
//check if the representation already tried to be requested before
//and therefore the data has been in cache
Object dataInCache = this.app.getCachedData().get(getURI);
if (dataInCache != null) {
System.out.println("Representing from Cache");
//this is warning. unless we can check that dataInCache is of type T, we can
//get rid of this warning
this.dataToBeRepresented = (T)dataInCache;
} else {
System.out.println("NOT IN CACHE");
this.dataToBeRepresented = whenDataIsNotInCache();
//automatically add data to cache
this.app.getCachedData().put(getURI, this.dataToBeRepresented, cached_duration);
}
//now represent it (if not previously execute the EmptyRepresentation)
JaxbRepresentation<T> jaxb = new JaxbRepresentation<T>(dataToBeRepresented);
jaxb.setFormattedOutput(true);
return jaxb;
}
AS you can see, and you might asked me; yes I am implementing Cache through Kitty-Cache. So, if some XML that is expensive to produce, and really looks like will never change for 7 decades, then I will use cache... I also use it for likely static data. Maximum time limit for a cache is an hour to remain in memory.
Even when I cache the output, sometimes, output are irresponsive, like hang, printed partially, and takes time before it prints the remaining document. The XML document is accessible through browser and also program, it used GET.
What are actually the problem? I humbly would like to know also the answer from RESTlet developer, if possible. Thanks
My scenario is: One step in my jira workflow should have the ability to unschedule a task i.e. set a Fix Version to "None".
I noticed that I was not able to update fix version in a workflow post function - I don't know exactly why, but anyway I did implement a jira plugin to help me solve my problem but I know I'm going against jira structure (even java good coding practices :)). I am not sure if my implementation can cause problems, but indeed it is working in my jira instance 4.1.x.
How I've implemented a plugin to update fix version in a post function, 2 very similar ways:
public class BrandsclubPostFunctionUnschedule extends AbstractJiraFunctionProvider {
// Here I create an empty Collection to be the new value of FixVersion (empty because I need no version in Fix Version)
public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException {
MutableIssue issue = this.getIssue(transientVars);
Collection<Version> newFixVersion = new ArrayList<Version>();
issue.setFixVersions(newFixVersion);
issue.store();
}
}
public class BrandsclubPostFunctionUnschedule extends AbstractJiraFunctionProvider {
// here I clear the Collection I got from "old" Fix Version and I have to set it again to make it work.
public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException {
MutableIssue issue = this.getIssue(transientVars);
Collection fixVersions = issue.getFixVersions();
fixVersions.clear();
issue.setFixVersions(fixVersions);
issue.store();
}
}
I presume that a real solution should use classes like: ChangeItemBean, ModifiedValue, IssueChangeHolder - taking as example the updateValue methods from CustomFieldImpl (from jira source code, project: jira, package: com.atlassian.jira.issue.fields).
My point of publishing this here is:
Does anyone know how to implement a jira plugin containing a post function to change Fix Version correctly?
If you want to do it properly take a look in the code for
./jira/src/java/com/atlassian/jira/workflow/function/issue/UpdateIssueFieldFunction.java processField()
Postfunctions that take input parameters are not documented yet it seems. Other places to go for code are other open source plugins.
Atlassian has a tutorial on doing just about exactly what you want to do, here:
I do it like in this snippet:
List<GenericValue> genericValueList = issueManager.getIssues(issues);
versionManager.moveIssuesToNewVersion(genericValueList, lastVersion, newVersion);