Optaplanner shadow variable with more than one source - optaplanner

Optaplanner allows for a shadow variable to have more than one source (sources = {}) but only one variableListsnerClass. In my implementation i have a planning entity with shadow variables that should be able to change by two listsners, but this is not supported it seems or am i wrong? is there a way to have two listeners affect one shadow variable?
I have the following planning entities: PlannerActivity, PlannerTask and PlannerTaskResourceAllocation.
Any change on a PlannerActivity startIndex (genuine var) is listened to by the ActivityStartIndexVariableListener which moves the startindex (shadow var) and endIndex (shadow var) on all tasks belonging to that activity. this works fine
In addition to that, any change on a PlannerTaskResourceAllocation resource (geniune var), is listened to by TaskResourceVariableListener, and when the resource is a product, also updates the ohHandAmounts for that product, this also works fine.
The problem i have is that i need to add logic that when a resource is changed on a PlannerTaskResourceAllocation and that resource is an equipment, i need to possibly recalculate the task duration is the new equipment might be slower or faster than what was assigned before.
so what i need here is that the PlannerActivity and PlannerTask startIndex and endIndex should be able to be changed by the TaskResourceVariableListener as well, but they are already listed to by the
ActivityStartIndexVariableListener, and there's no way for me to specify two listeners for one shadow variable.
PlannerTask:
public class PlannerTask extends InventoryTransactionCause {
private static final long serialVersionUID = 1L;
#Getter
#Setter
private Activity activity;
#Getter
#Setter
private Integer indexInActivity;
// shadow variable
private Integer startIndex;
#Getter
#Setter
private double startOffset;
// shadow variable
private Integer length;
// shadow variable
private Integer endIndex;
#Getter
#Setter
private double endOffset;
#Getter
#Setter
private Integer originalStartIndex;
#Getter
#Setter
private Integer originalEndIndex;
#Getter
#Setter
private String state;
// getters and setters for shadow variables
// this is one of the shadow variables that i need affected by two
// listeners, one is the ActivityStartIndexVariableListener and the
// other is TaskResourceVariableListener
#CustomShadowVariable(variableListenerClass = ActivityStartIndexVariableListener.class,
sources = { #CustomShadowVariable.Source(entityClass = PlannerActivity.class, variableName = "endIndex"),
#CustomShadowVariable.Source(entityClass = PlannerTaskResourceAllocation.class,
variableName = "resource") })
public Integer getStartIndex() {
return this.startIndex;
}
public void setStartIndex(Integer startIndex) {
this.startIndex = startIndex;
}
#CustomShadowVariable(variableListenerClass = ActivityStartIndexVariableListener.class,
sources = { #CustomShadowVariable.Source(entityClass = PlannerActivity.class, variableName = "endIndex"),
#CustomShadowVariable.Source(entityClass = PlannerTaskResourceAllocation.class,
variableName = "resource") })
public Integer getEndIndex() {
return this.endIndex;
}
public void setEndIndex(Integer endIndex) {
this.endIndex = endIndex;
}
#CustomShadowVariable(variableListenerClass = TaskResourceVariableListener.class,
sources = { #CustomShadowVariable.Source(entityClass = PlannerTaskResourceAllocation.class,
variableName = "resource") })
public Integer getLength() {
return this.length;
}
public void setLength(Integer length) {
this.length = length;
}
}

This is supported with the variableListenerRef attribute: the first shadow variable has a normal shadow variable annotation and the second shadow variable points to the first shadow variable with #CustomShadowVariable(variableListenerRef = #PlanningVariableReference(variableName = "firstShadow"))
For example, 1 variable listener that changes 2 shadow variables that is based on 2 genuine variables:
#PlanningVariable(valueRangeProviderRefs = "valueRange")
public TestdataValue getPrimaryValue() {
return primaryValue;
}
public void setPrimaryValue(TestdataValue primaryValue) {
this.primaryValue = primaryValue;
}
#PlanningVariable(valueRangeProviderRefs = "valueRange")
public TestdataValue getSecondaryValue() {
return secondaryValue;
}
public void setSecondaryValue(TestdataValue secondaryValue) {
this.secondaryValue = secondaryValue;
}
#CustomShadowVariable(variableListenerClass = ComposedValuesUpdatingVariableListener.class,
sources = {#CustomShadowVariable.Source(variableName = "primaryValue"),
#CustomShadowVariable.Source(variableName = "secondaryValue")})
public String getComposedCode() {
return composedCode;
}
public void setComposedCode(String composedCode) {
this.composedCode = composedCode;
}
#CustomShadowVariable(variableListenerRef = #PlanningVariableReference(variableName = "composedCode"))
public String getReverseComposedCode() {
return reverseComposedCode;
}
public void setReverseComposedCode(String reverseComposedCode) {
this.reverseComposedCode = reverseComposedCode;
}

You can make shadow variables that depend on shadow variables.
Create a custom shadow variable (with a VariableListener impl) for startIndex that depends on endIndex and length (which are both shadow vars).

Related

how to mock and test inside/outside get and set methods?

I dont know why but Im always getting NullPointer and no idea why and how exactly this test should looks like. Its about method: webServiceTemplate():
#Configuration
public class ErdConfiguration {
#Autowired
private EJwtProperties eJwtProperties;
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
// this package must match the package in the <generatePackage> specified in pom.xml
marshaller.setContextPath("erdUserRoles.wsdl");
return marshaller;
}
public WebServiceTemplate webServiceTemplate() {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller());
webServiceTemplate.setUnmarshaller(marshaller());
webServiceTemplate.setDefaultUri(eJwtProperties.getRoles().getErdServiceUri());
return webServiceTemplate;
}
}
and EJwtProperties class which it uses:
public class EJwtProperties {
private Map<String, String> claims = new HashMap<>();
private String signingKey;
private SourceTokenConfig sourceToken = new SourceTokenConfig();
private RolesConfig roles = new RolesConfig();
private List<String> generateEjwtRoles = Collections.emptyList();
private boolean cacheDisabled = false;
#Data
public static class SourceTokenConfig {
private boolean embedSourceToken = false;
private String embeddedTokenClaimName = "source-token";
}
#Data
public static class RolesConfig {
private boolean rolesEnabled = false;
private String rolesClaimName = "roles";
private String erdAppId;
private String erdServiceUri;
}
}
My code so far looks like this and got null pointer while Im trying to check getRoles() in when-thenReturn :
#Mock
private EJwtProperties eJwtProperties;
#InjectMocks
private ErdConfiguration underTest;
Jaxb2Marshaller marshaller;
#BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
#Test
void webServiceTemplateTest() {
EJwtProperties.RolesConfig roles = new EJwtProperties.RolesConfig();
roles.setErdServiceUri("testErdServiceUri");
eJwtProperties.setRoles(roles);
underTest = new ErdConfiguration();
when(eJwtProperties.getRoles()).thenReturn(roles); //this one passed
when(eJwtProperties.getRoles().getErdServiceUri()).thenReturn(roles.getErdServiceUri()); //here nullPointer
// underTest.webServiceTemplate(); //this is what I was planning to do next
//assertEquals(underTest.webServiceTemplate(), eJwtProperties.getRoles().getErdServiceUri()); //or this
// assertEquals(marshaller, underTest.webServiceTemplate().getMarshaller());
// assertEquals(marshaller, underTest.webServiceTemplate().getUnmarshaller());
}
}
Please keep in mind that I'm still learning tests. Id be thankful for any help. How the hack it should looks like? What am I missing that it return null ? Should I initialize whole properties??

#JsonIdentityReference does not recognize equal values

I'm trying to serialize an object (Root), with some duplicated entries of MyObject. Just want store the whole objects one, I'm using #JsonIdentityReference, which works pretty well.
However, I realize that it will generate un-deserializable object, if there're equal objects with different reference. I wonder if there's a configuration in Jackson to change this behavior, thanks!
#Value
#AllArgsConstructor
#NoArgsConstructor(force = true)
class Root {
private List<MyObject> allObjects;
private Map<String, MyObject> objectMap;
}
#Value
#AllArgsConstructor
#NoArgsConstructor(force = true)
#JsonIdentityReference
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
class MyObject {
private String id;
private int value;
}
public class Main {
public static void main() throws JsonProcessingException {
// Constructing equal objects
val obj1 = new MyObject("a", 1);
val obj2 = new MyObject("a", 1);
assert obj1.equals(obj2);
val root = new Root(
Lists.newArrayList(obj1),
ImmutableMap.of(
"lorem", obj2
)
);
val objectMapper = new ObjectMapper();
val json = objectMapper.writeValueAsString(root);
// {"allObjects":[{"id":"a","value":1}],"objectMap":{"lorem":{"id":"a","value":1}}}
// Note here both obj1 and obj2 are expanded.
// Exception: Already had POJO for id
val deserialized = objectMapper.readValue(json, Root.class);
assert root.equals(deserialized);
}
}
I'm using Jackson 2.10.
Full stacktrace:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id (java.lang.String) [[ObjectId: key=a, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=java.lang.Object]] (through reference chain: Root["objectMap"]->java.util.LinkedHashMap["lorem"]->MyObject["id"])
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:394)
at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:353)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1714)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:371)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1257)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:157)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:527)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:364)
at com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:29)
at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:138)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:288)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:151)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4202)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3205)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3173)
at Main.main(Main.java:53)
Caused by: java.lang.IllegalStateException: Already had POJO for id (java.lang.String) [[ObjectId: key=a, type=com.fasterxml.jackson.databind.deser.impl.PropertyBasedObjectIdGenerator, scope=java.lang.Object]]
at com.fasterxml.jackson.annotation.SimpleObjectIdResolver.bindItem(SimpleObjectIdResolver.java:24)
at com.fasterxml.jackson.databind.deser.impl.ReadableObjectId.bindItem(ReadableObjectId.java:57)
at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeSetAndReturn(ObjectIdValueProperty.java:101)
at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeAndSet(ObjectIdValueProperty.java:83)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:369)
... 14 more
As I mentioned earlier, this setup only works if obj1 == obj2, as the two objects with same ID should be identity-equal. In that case, the second object would also net get expanded during serialization (alwaysAsId = false only expands the first object).
However, if you want to have this setup and are fine with the serialization, you could use a custom Resolver for deserialization that stores a single instance per key:
#JsonIdentityReference(alwaysAsId = false)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", resolver = CustomScopeResolver.class)
static class MyObject {
private String id;
// ...
}
class CustomScopeResolver implements ObjectIdResolver {
Map<String, MyObject> data = new HashMap<>();
#Override
public void bindItem(final IdKey id, final Object pojo) {
data.put(id.key.toString(), (MyObject) pojo);
}
#Override
public Object resolveId(final IdKey id) {
return data.get(id.key);
}
#Override
public ObjectIdResolver newForDeserialization(final Object context) {
return new CustomScopeResolver();
}
#Override
public boolean canUseFor(final ObjectIdResolver resolverType) {
return false;
}
}
NEW EDIT: Apparently, its very easy: Just turn on objectMapper.configure(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID, true); so that the DefaultSerializerProvider uses a regular Hashmap instead of an IdentityHashMap to manage the serialized beans.
DEPRECATED: Update for Serialization: It is possible to achieve this by adding a custom SerializationProvider:
class CustomEqualObjectsSerializerProvider extends DefaultSerializerProvider {
private final Collection<MyObject> data = new HashSet<>();
private final SerializerProvider src;
private final SerializationConfig config;
private final SerializerFactory f;
public CustomEqualObjectsSerializerProvider(
final SerializerProvider src,
final SerializationConfig config,
final SerializerFactory f) {
super(src, config, f);
this.src = src;
this.config = config;
this.f = f;
}
#Override
public DefaultSerializerProvider createInstance(final SerializationConfig config, final SerializerFactory jsf) {
return new CustomEqualObjectsSerializerProvider(src, this.config, f);
}
#Override
public WritableObjectId findObjectId(final Object forPojo, final ObjectIdGenerator<?> generatorType) {
// check if there is an equivalent pojo, use it if exists
final Optional<MyObject> equivalentObject = data.stream()
.filter(forPojo::equals)
.findFirst();
if (equivalentObject.isPresent()) {
return super.findObjectId(equivalentObject.get(), generatorType);
} else {
if (forPojo instanceof MyObject) {
data.add((MyObject) forPojo);
}
return super.findObjectId(forPojo, generatorType);
}
}
}
#Test
public void main() throws IOException {
// Constructing equal objects
final MyObject obj1 = new MyObject();
obj1.setId("a");
final MyObject obj2 = new MyObject();
obj2.setId("a");
assert obj1.equals(obj2);
final Root root = new Root();
root.setAllObjects(Collections.singletonList(obj1));
root.setObjectMap(Collections.singletonMap(
"lorem", obj2));
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializerProvider(
new CustomEqualObjectsSerializerProvider(
objectMapper.getSerializerProvider(),
objectMapper.getSerializationConfig(),
objectMapper.getSerializerFactory()));
final String json = objectMapper.writeValueAsString(root);
System.out.println(json); // second object is not expanded!
}

Optaplanner doesn't see my planning variable

So I have a class Cycle:
#PlanningEntity
public class Cycle {
private Integer START = 29;
private Integer END = 42;
private final int SUSTAIN_START = 29;
private final int SUSTAIN_END = 72;
#PlanningVariable(valueRangeProviderRefs = { "startRange" } )
public Integer getStartIndex() {
System.out.println("DEBUG: getStartIndex");
return START;
}
public void setStartIndex(Integer i) {
System.out.println("DEBUG: setStartIndex");
START = i;
END = i+13;
}
#ValueRangeProvider(id = "startRange")
public List<Integer> getStartIndexes () {
ArrayList<Integer> startRange = new ArrayList<Integer>();
for(int i = SUSTAIN_START; i < SUSTAIN_END; i++) {
startRange.add(i);
}
return startRange;
}
}
and Optplanner doesn't ever call any of these methods, and I have no clue why. I've tried adding a "#ProblemFactCollectionProperty" annotatation on the getStartIndexes method, but I read in the docs that that annotation should only be used within a PlanningSolution.
I've also tried using scanAnnotatedClasses and explicitly specifying the entityClass in my solver config and neither seemed to make a difference.
Why can't Optaplanner see my variable and why doesn't Optaplanner change it?
More Info: I'm using Optaplanner version 7.30.0.Final and I have another planning entity with two planning variables that are seen and changed by Optaplanner.
The problem, per #yurloc's comment, was that I was missing the PlanningEntityCollectionProperty for Cycles in my PlanningSolution class. Thanks for the help!

Wicket Wizard show index of step

for my Wicket Wizard I want to display an information like: "Wizard step 1 of 4". I started by getting the number of steps and I already came across the first problem:
public WizardPanel(String id) {
super(id, false);
// false deactivates the default style.
setDefaultModel(new CompoundPropertyModel<WizardPanel>(this));
WizardModel model = new WizardModel();
model.add(new FirstStep());
model.add(new SecondStep());
model.add(new ThirdStep());
model.add(new ConfirmationStep());
Iterator<IWizardStep> iterator = model.stepIterator();
for(int i = 1; iterator.hasNext(); i ++){
System.out.println(String.valueOf(i));
}
init(model);
}
My Iterator creates an infinite loop. Shouldn't there only be four objects he can iterate through?
Or is there even a basic implementation for this kind of pagination I haven't found yet?
Because my next step would be to get the current index out of model.getActiveStep(); and find which number it is.
I thought I share my solution (as far as it now is)
public class OverviewComponent extends Panel{
private static final long serialVersionUID = 1L;
private List<WizardStep> steps;
private WizardModel model;
public OverviewComponent(String id, WizardModel model) {
super(id);
this.model = model;
steps = fillList();
this.add(new ListView<WizardStep>("steps", steps) {
private static final long serialVersionUID = 1L;
/**
* Wrap a markup container around the item append a css attribute to every item
*/
#Override
protected void populateItem(ListItem<WizardStep> item) {
WebMarkupContainer stepOverviewItem;
item.add(stepOverviewItem = new WebMarkupContainer("stepOverviewItem"));
stepOverviewItem.add(new Label("index", Model.of(item.getIndex()+1)));
stepOverviewItem.add(new AttributeModifier("class", getCSSProperty(item.getModelObject())));
stepOverviewItem.setOutputMarkupId(true);
stepOverviewItem.setOutputMarkupPlaceholderTag(true);
}
});
}
public String getCSSProperty(WizardStep step) {
if (step.equals(model.getActiveStep())) {
return "active";
} else if (!step.isComplete()) {
return "pending";
} else if (step.isComplete()) {
return "completed";
}
return "";
}
public List<WizardStep> fillList() {
List<WizardStep> iSteps = new ArrayList<WizardStep>();
Iterator<IWizardStep> iterator = model.stepIterator();
while (iterator.hasNext()) {
iSteps.add((WizardStep)iterator.next());
}
return iSteps;
}
}

Method to check if number is contained in ArrayList will not work, NullPointerExcepton. Can you use ArrayList method inside a constructed method?

This is a project I am working on and it is supposed to take input from the user then which is an area code then see if it is contained in a array list. My method that I have created will not work in another class and I am not sure why, it is returning a NullPointerException.
The NullPointerException is shown at this line of code: if (mountainTime.contains(input))
This is the class with methods
package finalPro;
import java.util.ArrayList;
public class Final
{
public Final()
{
input = 0;
timezone = 0;
}
public void checkIfTrue(int y)
{
input = y;
if (mountainTime.contains(input))
{
timezone = 1;
}
else
timezone = 0;
System.out.println(timezone);
}
public int getZone()
{
return timezone;
}
public ArrayList<Integer> mountainTime;
private int input;
private int timezone;
}
Here is test class
package finalPro;
import java.util.ArrayList;
import javax.swing.JOptionPane;
public class FinalLogic
{
public static void main(String[] args)
{
ArrayList<Integer> mountainTime = new ArrayList<Integer>();
mountainTime.add(480);
mountainTime.add(602);
mountainTime.add(623); //Arizona area codes
mountainTime.add(928);
mountainTime.add(520);
mountainTime.add(303);
mountainTime.add(719); //Colorado
mountainTime.add(720);
mountainTime.add(970);
mountainTime.add(406); //Montana
mountainTime.add(505); //New Mexico
mountainTime.add(575);
mountainTime.add(385);
mountainTime.add(435); //Utah
mountainTime.add(801);
mountainTime.add(307); //Wyoming
Final myMap = new Final();
{
String x = JOptionPane.showInputDialog("Please enter a number: ");
int input = Integer.parseInt(x);
myMap.checkIfTrue(input);
}
}
}
I hope it's not too late, I haven't done anything special to fix your code, just some movement of code,
Removed the initialization logic from class FinalLogic to Final class .(btw Final name is not really good, you might be aware final is reserved word in Java. Although your name is case sensitive but still)
package finalPro;
import javax.swing.JOptionPane;
public class FinalLogic {
public static void main(String[] args) {
Final myMap = new Final();
String x = JOptionPane.showInputDialog("Please enter a number: ");
int input = Integer.parseInt(x);
myMap.checkIfTrue(input);
}
}
And here is your class Final
package finalPro;
import java.util.ArrayList;
public class Final {
public Final() {
input = 0;
timezone = 0;
// moved all initialization logic to constructor
mountainTime = new ArrayList<>();
mountainTime.add(480);
mountainTime.add(602);
mountainTime.add(623); // Arizona area codes
mountainTime.add(928);
mountainTime.add(520);
mountainTime.add(303);
mountainTime.add(719); // Colorado
mountainTime.add(720);
mountainTime.add(970);
mountainTime.add(406); // Montana
mountainTime.add(505); // New Mexico
mountainTime.add(575);
mountainTime.add(385);
mountainTime.add(435); // Utah
mountainTime.add(801);
mountainTime.add(307); // Wyoming
}
public void checkIfTrue(int y) {
input = y;
if (mountainTime.contains(input)) {
timezone = 1;
} else
timezone = 0;
System.out.println(timezone);
}
public int getZone() {
return timezone;
}
public ArrayList<Integer> mountainTime;
private int input;
private int timezone;
}
I tried in my workspace, it gives no NPE, Hope it helps.