How to detect whether the screen is off in OS X? - java

I'm making an application that should only try to run if the display is on (among other things), but I couldn't find a way to check via commandline. Checking how long the user has been idle and their preferred idle time via pmset -g doesn't always work either, because of various programs that can change this.
Is there a way (via commandline, or in Java) to check whether the screen is currently off in OS X?

This bit of C will detect if the screens are asleep or awake:
CGDirectDisplayID ids[20];
uint32_t num_ids;
if (kCGErrorSuccess != CGGetActiveDisplayList(20, ids, &num_ids)) {
printf("Oops\n");
return 1;
}
boolean_t asleep = true;
for (int i = 0; i < num_ids; i++) {
asleep &= CGDisplayIsAsleep(ids[i]);
}
if (asleep) {
printf("Asleep\n");
return 1;
} else {
printf("Awake\n");
return 0;
}
This can be horridly translated to some JNA using:
CoreGraphics.java:
import com.sun.jna.*;
import com.sun.jna.ptr.IntByReference;
public interface CoreGraphics extends Library {
CoreGraphics INSTANCE = (CoreGraphics)Native.loadLibrary("CoreGraphics", CoreGraphics.class);
class int32_t extends IntegerType {
public static final int SIZE = 4;
public int32_t() {
this(0);
}
public int32_t(long value) {
super(SIZE, value, false);
}
};
public static class CGError extends int32_t {
public static final CGError Success = new CGError(0);
public CGError() { this(0); }
public CGError(int value) { super(value); }
};
public static class CGDirectDisplayID extends int32_t {
};
CGError CGGetActiveDisplayList(int maxDisplays, CGDirectDisplayID[] activeDisplays, IntByReference displayCount);
boolean CGDisplayIsAsleep(CGDirectDisplayID disp);
}
Example program making use of the interface - jna_monitors_run.java:
import com.sun.jna.*;
import com.sun.jna.ptr.IntByReference;
public class jna_monitors_run {
static final int MAX_DISPLAYS = 20;
public static void main(String[] args) {
IntByReference ib = new IntByReference();
CoreGraphics.CGDirectDisplayID ids[] = new CoreGraphics.CGDirectDisplayID[MAX_DISPLAYS];
if (! CoreGraphics.CGError.Success.equals(CoreGraphics.INSTANCE.CGGetActiveDisplayList(
MAX_DISPLAYS, ids, ib))) {
System.exit(2);
}
boolean is_asleep = true;
int i = ib.getValue();
while (--i >= 0) {
is_asleep &= CoreGraphics.INSTANCE.CGDisplayIsAsleep(ids[i]);
}
System.out.println(is_asleep);
System.exit(is_asleep ? 1 : 0);
}
}

Related

Why is this getter functions only returning the initial value

I'm learning object-oriented programming and started learning about inheritance. The assignment my teacher gave me was to make a counter object with 6 "buttons": Increment, Decrement, Reset, AddMemory, ResetMemory, and Quit. It is fairly straight-forward what each button does.
The requirements are that I have to use the JOptionPane command, I have to make a Counter class with a counter attribute, increment, decrement, reset, and quit methods, I have to make a MemoryCounter class with a memory attribute, restMemory, and addMemory method. I also have to make a MemoryCounterConsoleMenu class which makes the input box from the JOptionPane command and executes the appropriate method. The final thing I have to do is make a MemoryCounterTest class that brings the MemoryCounterConsoleMenu and MemoryCounter classes together
So I did all that and here it is:
The first one is the Counter class
public class Counter
{
private int counter = 0;
public void increment()
{
setCounter(getCounter() + 1);
}
public void decrement()
{
setCounter(getCounter() - 1);
}
public void reset()
{
setCounter(0);
}
public void setCounter(int counter) {
this.counter = counter;
}
public int getCounter() {
return counter;
}
}
This is the MemoryCounter class
public class MemoryCounter extends Counter
{
private int memory = 0;
public void resetMem()
{
setMemory(0);
}
public void addMem()
{
setMemory(getCounter());
}
public void setMemory(int memory)
{
this.memory = memory;
}
public int getMemory()
{
return memory;
}
}
Next is the MemoryConsoleMenu
public class MemoryCounterConsoleMenu
{
static MemoryCounter memCounter = new MemoryCounter();
static Counter counter = new Counter();
public static int console()
{
System.out.println(memCounter.getMemory());
Object[] options = {"Reset Mem", "Add Mem", "Increment", "Decrement", "Reset", "Quit" };
int objectIndex = JOptionPane.showOptionDialog(null, "Counter = " + counter.getCounter() + "Memory = "
+ memCounter.getMemory(), "MemoryCounter",JOptionPane.PLAIN_MESSAGE,
JOptionPane.PLAIN_MESSAGE, null, options, options[5]);
return objectIndex;
}
public static int change(int objectIndex)
{
if(objectIndex == 0)
{
memCounter.resetMem();
return 1;
}
else if(objectIndex == 1)
{
memCounter.addMem();
return 2;
}
else if(objectIndex == 2)
{
counter.increment();
return 3;
}
else if(objectIndex == 3)
{
counter.decrement();
return 4;
}
else if(objectIndex == 4)
{
counter.reset();
return 5;
}
else
{
return 6;
}
}
}
Finally, there is the MemoryCounterTest
public class MemoryCounterTest
{
public static void main(String[] args)
{
MemoryCounterConsoleMenu memoryConsole = new MemoryCounterConsoleMenu();
for(int i = 0; i != 6;)
{
i = memoryConsole.change(memoryConsole.console());
}
}
}
Everything works properly except for the memory value. It stays at a constant zero. I've done some troubleshooting myself and found that the only problem in the code is in the "addMem()" method is the MemoryCounter class particularly the implementation of the "getCounter()" method. It will only return 0 for some reason.
After figuring this out I have made no ground on why the problem is occuring or how to fix it
It stays at 0 because they are two separate counters.
MemoryCounter class extends the Counter class, so you don't need a separate
static Counter counter = new Counter();
Just do everything via memCounter.

How do I properly map CERT_SELECT_STRUCT in JNA

I am using jna-4.5.1 in my Java Project.
This is cryptdlg structure CERT_SELECT_STRUCT I want to replicate.
typedef struct tagCSSA {
DWORD dwSize;
HWND hwndParent;
HINSTANCE hInstance;
LPCSTR pTemplateName;
DWORD dwFlags;
LPCSTR szTitle;
DWORD cCertStore;
HCERTSTORE *arrayCertStore;
LPCSTR szPurposeOid;
DWORD cCertContext;
PCCERT_CONTEXT *arrayCertContext;
LPARAM lCustData;
PFNCMHOOKPROC pfnHook;
PFNCMFILTERPROC pfnFilter;
LPCSTR szHelpFileName;
DWORD dwHelpId;
HCRYPTPROV hprov;
} CERT_SELECT_STRUCT_A, *PCERT_SELECT_STRUCT_A;
Sample Java code for my project.
public class Crypto {
public interface Cryptdlg extends Library {
Cryptdlg INSTANCE = (Cryptdlg) Native.loadLibrary("Cryptdlg", Cryptdlg.class, W32APIOptions.DEFAULT_OPTIONS);
public boolean CertSelectCertificate(CERT_SELECT_STRUCT pCertSelectInfo);
public static class CERT_SELECT_STRUCT extends Structure {
private static final List<String> fieldOrder = createFieldsOrder("dwSize", "hwndParent", "hInstance",
"pTemplateName", "dwFlags", "szTitle", "cCertStore", "arrayCertStore", "szPurposeOid",
"cCertContext", "arrayCertContext", "lCustData", "pfnHook", "pfnFilter", "szHelpFileName",
"dwHelpId", "hprov");
public static class ByReference extends CERT_SELECT_STRUCT implements Structure.ByReference {
}
public int dwSize = size();
public HWND hwndParent;
public HINSTANCE hInstance;
public String pTemplateName;
public int dwFlags;
public String szTitle;
public int cCertStore;
public Pointer arrayCertStore;
public String szPurposeOid;
public int cCertContext;
public Pointer arrayCertContext;
public WinDef.LPARAM lCustData;
public Pointer pfnHook = null;
public Pointer pfnFilter = null;
public String szHelpFileName;
public int dwHelpId;
public HCRYPTPROV hprov;
public CERT_SELECT_STRUCT() {
super();
}
public WinCrypt.CERT_CONTEXT[] getArrayCertContext() {
WinCrypt.CERT_CONTEXT[] elements = new WinCrypt.CERT_CONTEXT[cCertContext];
for (int i = 0; i < elements.length; i++) {
elements[i] = (WinCrypt.CERT_CONTEXT) Structure.newInstance(WinCrypt.CERT_CONTEXT.class,
arrayCertContext.getPointer(i * Native.POINTER_SIZE));
elements[i].read();
}
return elements;
}
public void setArrayCertContext(WinCrypt.CERT_CONTEXT[] arrayCertContexts) {
if (arrayCertContexts == null || arrayCertContexts.length == 0) {
arrayCertContext = null;
cCertContext = 0;
} else {
cCertContext = arrayCertContexts.length;
Memory mem = new Memory(Native.POINTER_SIZE * arrayCertContexts.length);
for (int i = 0; i < arrayCertContexts.length; i++) {
mem.setPointer(i * Native.POINTER_SIZE, arrayCertContexts[i].getPointer());
}
arrayCertContext = mem;
}
}
public void setArrayCertStore(WinCrypt.HCERTSTORE[] stores) {
if (stores == null || stores.length == 0) {
arrayCertStore = null;
cCertStore = 0;
} else {
Memory mem = new Memory(Native.POINTER_SIZE * stores.length);
for (int i = 0; i < stores.length; i++) {
mem.setPointer(i * Native.POINTER_SIZE, stores[i].getPointer());
}
cCertStore = stores.length;
arrayCertStore = mem;
}
}
public WinCrypt.HCERTSTORE[] getArrayCertStore() {
if (arrayCertStore == null || cCertStore == 0) {
return new WinCrypt.HCERTSTORE[0];
} else {
WinCrypt.HCERTSTORE[] result = new WinCrypt.HCERTSTORE[cCertStore];
for (int i = 0; i < result.length; i++) {
result[i] = new WinCrypt.HCERTSTORE(arrayCertStore.getPointer(i * Native.POINTER_SIZE));
}
return result;
}
}
#Override
protected List<String> getFieldOrder() {
return fieldOrder;
}
}
}
public void CertSelect() {
Cryptdlg cryptdlg = Cryptdlg.INSTANCE;
...// parentHwnd and hCertStore are initalized and passed to this method
Cryptdlg.CERT_SELECT_STRUCT certSel = new Cryptdlg.CERT_SELECT_STRUCT();
WinCrypt.CERT_CONTEXT[] pContexts = new WinCrypt.CERT_CONTEXT[1];
certSel.hwndParent = parentHwnd;
certSel.szTitle = "title";
certSel.cCertStore = 1;
certSel.setArrayCertStore(new WinCrypt.HCERTSTORE[] {hCertStore});
pCertSelectInfo.cCertContext = 1;
pContexts[0] = new WinCrypt.CERT_CONTEXT.ByReference();
certSel.setArrayCertContext(pContexts);
cryptdlg.CertSelectCertificate(certSel); //line 60
...
}
}
When I call this method I get "java.lang.Error: Invalid memory access" at the dll call cryptdlg.CertSelectCertificate(certSel) at line 60 above.
java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:419)
at com.sun.jna.Function.invoke(Function.java:354)
at com.sun.jna.Library$Handler.invoke(Library.java:244)
at com.sun.proxy.$Proxy13.CertSelect(Unknown Source)
at com.project.Crypto.CertSelect(Crypto.java:60)
I am not sure why we are getting the exception. I followed the example mentioned here.
[UPDATE]
For what its worth,
When I modify the type of "setArrayCertStore" from Pointer to HCERTSTORE[] I am not getting any exception but no certificate are getting pulled.
It makes me think if arrayCertStore is initalized correctly or not.
WinCrypt.HCERTSTORE[] cStoreArray = new WinCrypt.HCERTSTORE[1];
pCertSelectInfo.cCertStore = 1;
cStoreArray[0] = hCertStore;
pCertSelectInfo.arrayCertStore = cStoreArray;
And the structure definition is changed as follows
public WinCrypt.HCERTSTORE[] arrayCertStore;
And HCRYPTPROV is defined as
public static class HCRYPTPROV extends BaseTSD.ULONG_PTR {
public HCRYPTPROV() {}
public HCRYPTPROV(long value) {
super(value);
}
}
==================================
[EDIT]
After discussion with Daniel and other people. Here is the updated code which works
public class Crypto {
public interface Cryptdlg extends Library {
Cryptdlg INSTANCE = (Cryptdlg) Native.loadLibrary("Cryptdlg", Cryptdlg.class, W32APIOptions.DEFAULT_OPTIONS);
public boolean CertSelectCertificate(CERT_SELECT_STRUCT pCertSelectInfo);
public static class CERT_SELECT_STRUCT extends Structure {
private static final List<String> fieldOrder = createFieldsOrder("dwSize", "hwndParent", "hInstance",
"pTemplateName", "dwFlags", "szTitle", "cCertStore", "arrayCertStore", "szPurposeOid",
"cCertContext", "arrayCertContext", "lCustData", "pfnHook", "pfnFilter", "szHelpFileName",
"dwHelpId", "hprov");
public static class ByReference extends CERT_SELECT_STRUCT implements Structure.ByReference {
}
public int dwSize;
public HWND hwndParent;
public HINSTANCE hInstance;
public String pTemplateName;
public int dwFlags;
public String szTitle;
public int cCertStore;
public Pointer arrayCertStore;
public String szPurposeOid;
public int cCertContext;
public Pointer arrayCertContext;
public WinDef.LPARAM lCustData;
public Pointer pfnHook = null;
public Pointer pfnFilter = null;
public String szHelpFileName;
public int dwHelpId;
public HCRYPTPROV hprov;
public CERT_SELECT_STRUCT() {
super();
}
public WinCrypt.CERT_CONTEXT[] getArrayCertContext() {
WinCrypt.CERT_CONTEXT[] elements = new WinCrypt.CERT_CONTEXT[cCertContext];
for (int i = 0; i < elements.length; i++) {
elements[i] = (WinCrypt.CERT_CONTEXT) Structure.newInstance(WinCrypt.CERT_CONTEXT.class,
arrayCertContext.getPointer(i * Native.POINTER_SIZE));
elements[i].read();
}
return elements;
}
public void setArrayCertContext(WinCrypt.CERT_CONTEXT[] arrayCertContexts) {
if (arrayCertContexts == null || arrayCertContexts.length == 0) {
arrayCertContext = null;
cCertContext = 0;
} else {
cCertContext = arrayCertContexts.length;
Memory mem = new Memory(Native.POINTER_SIZE * arrayCertContexts.length);
for (int i = 0; i < arrayCertContexts.length; i++) {
mem.setPointer(i * Native.POINTER_SIZE, arrayCertContexts[i].getPointer());
}
arrayCertContext = mem;
}
}
public void setArrayCertStore(WinCrypt.HCERTSTORE[] stores) {
if (stores == null || stores.length == 0) {
arrayCertStore = null;
cCertStore = 0;
} else {
Memory mem = new Memory(Native.POINTER_SIZE * stores.length);
for (int i = 0; i < stores.length; i++) {
mem.setPointer(i * Native.POINTER_SIZE, stores[i].getPointer());
}
cCertStore = stores.length;
arrayCertStore = mem;
}
}
public WinCrypt.HCERTSTORE[] getArrayCertStore() {
if (arrayCertStore == null || cCertStore == 0) {
return new WinCrypt.HCERTSTORE[0];
} else {
WinCrypt.HCERTSTORE[] result = new WinCrypt.HCERTSTORE[cCertStore];
for (int i = 0; i < result.length; i++) {
result[i] = new WinCrypt.HCERTSTORE(arrayCertStore.getPointer(i * Native.POINTER_SIZE));
}
return result;
}
}
#Override
public void write() {
this.dwSize = size();
super.write();
}
#Override
protected List<String> getFieldOrder() {
return fieldOrder;
}
}
}
public void CertSelect() {
Cryptdlg cryptdlg = Cryptdlg.INSTANCE;
Cryptdlg.CERT_SELECT_STRUCT certSel = new Cryptdlg.CERT_SELECT_STRUCT();
certSel.hwndParent = parentHwnd;
certSel.szTitle = "title";
certSel.cCertStore = 1;
certSel.setArrayCertStore(new WinCrypt.HCERTSTORE[] {hCertStore});
pCertSelectInfo.cCertContext = 1;
pCertSelectInfo.arrayCertContext = new Memory(Native.POINTER_SIZE);
pCertSelectInfo.arrayCertContext.setPointer(0, Pointer.NULL);
cryptdlg.CertSelectCertificate(certSel);
...
}
}
There are a few potential problem areas.
First, you are mixing the ANSI and Unicode versions.
The CertSelectCertificate() function has two variants, one ending in A and one in W. The -A functions are the ANSI (8-bit characters) while the -W variants are for Unicode/Wide-string (16 bit characters). The main difference in the methods is the number of characters in the strings in the CERT_SELECT_STRUCT structure., which similarly has two variants, CERT_SELECT_STRUCT_A and CERT_SELECT_STRUCT_W.
JNA automatically maps you to the correct version (the -W version in almost all cases in modern operating systems) using its default type mapper. You should probably add that type mapper explicitly in your library loading using the default options for Win32 libraries:
Cryptdlg INSTANCE = (Cryptdlg) Native.loadLibrary("Cryptdlg", Cryptdlg.class,
W32APIOptions.DEFAULT_OPTIONS);
However, you have an explicit call to use the -A type mapper in your CERT_SELECT_STRUCT() constructor:
public CERT_SELECT_STRUCT() {
super(W32APITypeMapper.ASCII);
}
This forces it to use the -A version of the structure, which only has 8 bits per character in its strings. You should just call super().
A second possibility, while not obvious from the docs, is that the first element, dwSize, is supposed to be the size of the structure. You should put that in your constructor:
this.dwSize = this.size();
A third possibility (and the most likely cause, if I were to guess) is in the line when you set the contents of the arrayCertContext field. It is documented as:
A pointer to an array of CERT_CONTEXT structures.
You define the array on the Java side as a structure (which has its own pointer) and manually set it into memory, but instead of populating the CERT_CONTEXT structure you put a Pointer there:
pContexts[0] = new WinCrypt.CERT_CONTEXT.ByReference();
That ends up filling up the first 8 bytes of the "structure" with the pointer address you just created, the first 4 bytes of which are assigned to dwCertEncodingType and the next 4 bytes (plus 4 null bytes) go to the pointer value of a ByteByReference() field.
Also, easier than your allocation of memory, allocation of arrays of structures could be done like this:
WinCrypt.CERT_CONTEXT[] pContextArray =
(WinCrypt.CERT_CONTEXT[]) new WinCrypt.CERT_CONTEXT().toArray(1);
As an aside, you might find structure definitions more streamlined in JNA 5.x (currently 5.6.0) which include a #FieldOrder annotation as a preferred method of creating the field order list.

Java: How to code for items to inherit raw materials' properties?

I am a Java beginner. My problem is, for example:
Fire: DMG +1
Stone: DEF +1
By combining to:
Fire-stone: fire + stone, and inherit both their properties (DMG 1, DEF 1).
Flame: fire + fire, and inherit 2 fire properties (DMG +2).
I've played around with classes and interfaces but doesn't seem to work. It seems to me that Java doesn't support multiple inheritance but multiple interfaces. I wonder how I could code each class/interface for this to work?
public static int DMG, DEF = 0;
public static String DESC = "";
interface fire {
DMG = 1; }
interface stone {
DEF = 1; }
class firestone implements fire, stone {
DESC = "Born in fire and stone";
//DMG and DEF calculations
}
You better use composition, here is what you could do:
public class Main {
public static void main(String[] args) {
Tool firestone = new Tool(new Fire(), new Stone());
}
private static interface Component {
int getDmg();
int getDef();
}
private static class Fire implements Component {
#Override
public int getDmg() {
return 1;
}
#Override
public int getDef() {
return 0;
}
}
private static class Stone implements Component {
#Override
public int getDmg() {
return 0;
}
#Override
public int getDef() {
return 1;
}
}
private static class Tool implements Component {
List<Component> components;
int dmg;
int def;
public Tool(Component... component) {
components = new ArrayList<>();
components.addAll(Arrays.asList(component));
dmg = 0;
def = 0;
for (Component c : components) {
dmg += c.getDmg();
def += c.getDef();
}
}
#Override
public int getDmg() {
return dmg;
}
#Override
public int getDef() {
return def;
}
}}
My implementation may be an overkill :P but it is extendable and you add more components that are more and more complicated.

JNA cast to struct from void**

I have to call a C library that receives as parameter a void**; it converts the void** into a known structure (depending on function internal information) then allocates memory and fills it with data.
I don't know how to call it from Java.
Below are some details:
The struct:
typedef struct {
uint32_t Size[64];
uint16_t *Datas[64];
} MyStruct_t;
The C function
int Cfunc( void **Evt) {
int i;
MyStruct_t *s;
s = (MyStruct_t *) malloc(sizeof(MyStruct_t));
for (i=0; i<8; i++) {
(s)->Size[i] = 512;
(s)->Datas[i] = malloc(512 * sizeof (short));
}
for (i=8; i<63; i++) {
(s)->Size[i] = 0;
(s)->Datas[i] = NULL;
}
*Evt = s;
}
My JNA wrapper class:
public class MyStruct_t extends Structure<MyStruct_t, MyStruct_t.ByValue, MyStruct_t.ByReference > {
/// the number of samples stored in Datas array
public int[] Size = new int[64];
/// the array of Size samples
public ShortByReference[] Datas = new ShortByReference[64];
public MyStruct_t() {
super();
initFieldOrder();
}
protected void initFieldOrder() {
setFieldOrder(new String[]{"Size", "Datas"});
}
public MyStruct_t(int Size[], ShortByReference Datas[]) {
super();
if (Size.length != this.Size.length)
throw new IllegalArgumentException("Wrong array size !");
this.Size = Size;
if (Datas.length != this.Datas.length)
throw new IllegalArgumentException("Wrong array size !");
this.Datas = Datas;
initFieldOrder();
}
#Override protected ByReference newByReference() { return new ByReference(); }
#Override protected ByValue newByValue() { return new ByValue(); }
#Override protected MyStruct_t newInstance() { return new MyStruct_t(); }
public static MyStruct_t[] newArray(int arrayLength) {
return Structure.newArray(MyStruct_t.class, arrayLength);
}
public static class ByReference extends MyStruct_t implements Structure.ByReference {};
public static class ByValue extends MyStruct_t implements Structure.ByValue {};
}
The Java function declaration:
public static native int Cfunc(MyStruct_t.ByReference Evt);
public static native int Cfunc(PointerByReference Evt);
If I call the Cfunc passing a PointerByReference I can see that memory is allocated and something is present but I don't know how to cast (?) it to the real structure (MyStruct_t).
If I call the Cfunc passing a MyStruct_t.ByReference, I find the structure not well allocated and values inside are not the expected.
What am I doing wrong?
To be clear, you are providing the address of a pointer. The callee sets the pointer value to the address of a struct which it allocates.
First, you pass the address of the pointer (a.k.a PointerByReference):
public static native int Cfunc(PointerByReference Evt);
Then, you extract the "returned" pointer value:
PointerByReference pref = new PointerByReference();
lib.CFunc(pref);
Pointer p = pref.getValue();
Then, you can create a new instance of your structure based on the "returned" pointer value:
MyStruct s = new MyStruct(p);
This, of course, requires that you implement the Pointer-based ctor for your structure:
public MyStruct(Pointer p) {
super(p);
read();
}
I found the mistake! I was using Structure class from jnaerator, I removed the jnaerator Structure class and used JNA Structure and now it works.
here is the modified and working java class:
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.ShortByReference;
public class t_t extends Structure {
public int[] lsize = new int[64];
public Pointer[] ldata = new Pointer[64];
public t_t() {
super();
initFieldOrder();
}
protected void initFieldOrder() {
setFieldOrder(new String[]{"size", "data"});
}
public t_t(int size[], Pointer data[]) {
super();
if (size.length != this.lsize.length)
throw new IllegalArgumentException("Wrong array size !");
this.lsize = size;
if (data.length != this.ldata.length)
throw new IllegalArgumentException("Wrong array size !");
this.ldata = data;
initFieldOrder();
}
public t_t(Pointer p){
super(p);
read();
}
public short[] getData(int i) {
if (this.lsize[i] == 0) return null;
return this.ldata[i].getShortArray(0, this.lsize[i]);
}
public int getSize(int i) {
return this.lsize[i];
}
public static class ByReference extends t_t implements Structure.ByReference {
};
public static class ByValue extends t_t implements Structure.ByValue {
};
}
Thanks technomage for the tip!
p1906

Is there a standard library/API/tool for implementing basic "rule chains" in Java?

I am writing a servlet that will be conditionally modifying HTTP headers according to some user-definable rules. (Edit: these rules are defined in an XML file that is read at start-up.) For example, add "X-UA-Compatible: IE=edge,chrome=1" to a response header if it does not already exist and if the request specified a "User-Agent" header matching a known pattern. Not having any better ideas, I attempted to make my own POJOs representing these rules. It "works" but I feel like there must be a more standard or more flexible way to do this.
Are there general-purpose libraries or tools (whether built-in or 3rd-party) that would solve this problem? I have heard and read a little about "rules engines" but they seem like much more complex/heavy tools not meant for problems as simple as mine.
To illustrate what I'm trying to do, I've created a simplified program that applies "rules" to numbers based on "conditions" like "is an even number". Here it is, sorry it's a bit lengthy.
Main.java
package my.example;
import java.util.*;
import my.example.conditions.*;
import my.example.rules.*;
public class Main {
public static void main(String args[]) {
// Some sample objects to evaluate
Collection<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8);
print(numbers);
// Define rules
Collection<Rule<Integer>> rules = new ArrayList<Rule<Integer>>();
rules.add(createRuleToMultiplyEvenNumbersBy4());
rules.add(createRuleToAdd1ToEveryNumber());
// Process the rules for each sample object
Collection<Integer> newNumbers = new ArrayList<Integer>();
for (Integer number : numbers) {
Integer newNumber = number;
for (Rule<Integer> rule : rules)
newNumber = rule.apply(newNumber);
newNumbers.add(newNumber);
}
print(newNumbers);
}
private static Rule<Integer> createRuleToMultiplyEvenNumbersBy4() {
MultiplyNumberRule rule = new MultiplyNumberRule(true, 4);
rule.addCondition(new NumberIsEvenCondition());
return rule;
}
private static Rule<Integer> createRuleToAdd1ToEveryNumber() {
AddNumberRule rule = new AddNumberRule(true, 1);
rule.addCondition(new ConstantCondition<Integer>(true));
return rule;
}
private static void print(Collection<Integer> numbers) {
System.out.print("Numbers: ");
for (Integer number : numbers) {
System.out.print(number);
System.out.print(" ");
}
System.out.print("\r\n");
}
}
Condition.java
package my.example.conditions;
public interface Condition<T> {
boolean appliesTo(T obj);
}
ConstantCondition.java
package my.example.conditions;
public class ConstantCondition<T> implements Condition<T> {
private boolean constant;
public ConstantCondition(boolean alwaysReturnThisValue) {
constant = alwaysReturnThisValue;
}
#Override
public boolean appliesTo(T target) {
return constant;
}
}
NumberIsEvenCondition.java
package my.example.conditions;
public class NumberIsEvenCondition implements Condition<Integer> {
#Override
public boolean appliesTo(Integer i) {
return (i % 2 == 0);
}
}
Rule.java
package my.example.rules;
public interface Rule<T> {
T apply(T target);
}
AbstractRule.java
package my.example.rules;
import java.util.*;
import my.example.conditions.Condition;
public abstract class AbstractRule<T> implements Rule<T> {
private Collection<Condition<T>> conditions;
private boolean requireAllConditions;
public AbstractRule(boolean requireAllConditions) {
conditions = new ArrayList<Condition<T>>();
this.requireAllConditions = requireAllConditions;
}
public void addCondition(Condition<T> condition) {
conditions.add(condition);
}
#Override
public T apply(T target) {
boolean isApplicable;
if (requireAllConditions)
isApplicable = allConditionsSatisfied(target);
else
isApplicable = atLeastOneConditionSatisfied(target);
if (isApplicable)
target = process(target);
return target;
}
// Check if all conditions are met
protected boolean allConditionsSatisfied(T target) {
for (Condition<T> condition : conditions) {
if (!condition.appliesTo(target))
return false;
}
return true;
}
// Check if any conditions are met
protected boolean atLeastOneConditionSatisfied(T target) {
for (Condition<T> condition : conditions) {
if (condition.appliesTo(target))
return true;
}
return false;
}
abstract T process(T target);
}
AddNumberRule.java
package my.example.rules;
public class AddNumberRule extends AbstractRule<Integer> {
private Integer addend;
public AddNumberRule(boolean requireAllConditions) {
this(requireAllConditions, 0);
}
public AddNumberRule(boolean requireAllConditions, Integer addend) {
super(requireAllConditions);
this.addend = addend;
}
#Override
public Integer process(Integer i) {
return i + addend;
}
}
MultiplyNumberRule.java
package my.example.rules;
public class MultiplyNumberRule extends AbstractRule<Integer> {
private Integer factor;
public MultiplyNumberRule(boolean requireAllConditions) {
this(requireAllConditions, 1);
}
public MultiplyNumberRule(boolean requireAllConditions, Integer factor) {
super(requireAllConditions);
this.factor = factor;
}
#Override
public Integer process(Integer i) {
return i * factor;
}
}
Well, I'd use Commons Chain
A popular technique for organizing the execution of complex processing
flows is the "Chain of Responsibility" pattern, as described (among
many other places) in the classic "Gang of Four" design patterns book.
Although the fundamental API contracts required to implement this
design patten are extremely simple, it is useful to have a base API
that facilitates using the pattern, and (more importantly) encouraging
composition of command implementations from multiple diverse sources.
it's a common Design Pattern, guess that fits your problem
I have modified the original code attempting to use Commons Chain, but it doesn't seem much different. Luiz E., is this roughly what you are suggesting? It seems like commons-chain does not include any notion of "conditions" -- this is left to the user as part of the implementation of Command.
Main.java
package my.example;
import java.util.*;
import org.apache.commons.chain.*;
import org.apache.commons.chain.impl.*;
import my.example.commands.*;
import my.example.conditions.*;
public class Main {
private static final String NUMBERS = "numbers";
public static void main(String args[]) throws Exception {
// Some sample objects to evaluate
Context context = new ContextBase();
setNumbersInContext(context, Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8));
printNumbersFromContext(context);
// Define rules
Chain ruleChain = new ChainBase();
ruleChain.addCommand(new MultiplyNumberCommand(4, new NumberIsEvenCondition()));
ruleChain.addCommand(new AddNumberCommand(1));
// Process the rules
ruleChain.execute(context);
printNumbersFromContext(context);
}
private static void printNumbersFromContext(Context context) {
Collection<Integer> numbers = getNumbersFromContext(context);
System.out.print("Numbers: ");
for (Integer number : numbers) {
System.out.print(number);
System.out.print(" ");
}
System.out.print("\r\n");
}
#SuppressWarnings("unchecked")
public static Collection<Integer> getNumbersFromContext(Context context) {
Object obj = context.get(NUMBERS);
try {
return (Collection<Integer>) obj;
}
catch (ClassCastException e) {
throw new IllegalStateException("Context did not contain the required data. ClassCastException message is: " + e.getMessage());
}
}
#SuppressWarnings("unchecked")
public static void setNumbersInContext(Context context, Collection<Integer> numbers) {
context.put(NUMBERS, numbers);
}
}
AbstractNumberCommand.java
package my.example.commands;
import static my.example.Main.getNumbersFromContext;
import static my.example.Main.setNumbersInContext;
import java.util.ArrayList;
import java.util.Collection;
import my.example.conditions.Condition;
import org.apache.commons.chain.*;
public abstract class AbstractNumberCommand implements Command {
private boolean continueProcessing = true;
protected Condition<Integer> condition;
protected AbstractNumberCommand(Condition<Integer> condition) {
this.condition = condition;
}
public void continueProcessing(boolean continueProcessing) {
this.continueProcessing = continueProcessing;
}
#Override
public boolean execute(Context context) throws Exception {
Collection<Integer> numbers = getNumbersFromContext(context);
Collection<Integer> newNumbers = new ArrayList<Integer>();
for (int number : numbers)
if (condition.appliesTo(number))
newNumbers.add(modifyNumber(number));
else
newNumbers.add(number);
setNumbersInContext(context, newNumbers);
if (continueProcessing)
return CONTINUE_PROCESSING;
else
return PROCESSING_COMPLETE;
}
protected abstract int modifyNumber(int number);
}
AddNumberCommand.java
package my.example.commands;
import my.example.conditions.*;
public class AddNumberCommand extends AbstractNumberCommand {
private int addend;
public AddNumberCommand() {
this(0);
}
public AddNumberCommand(int addend) {
this(addend, new ConstantCondition<Integer>(true));
}
public AddNumberCommand(int addend, Condition<Integer> condition) {
super(condition);
this.addend = addend;
}
#Override
protected int modifyNumber(int number) {
return number + addend;
}
}
Condition.java
package my.example.conditions;
public interface Condition<T> {
boolean appliesTo(T obj);
}
ConstantCondition.java
package my.example.conditions;
public class ConstantCondition<T> implements Condition<T> {
private boolean constant;
public ConstantCondition(boolean alwaysReturnThisValue) {
constant = alwaysReturnThisValue;
}
#Override
public boolean appliesTo(T target) {
return constant;
}
}
NumberIsEvenCondition.java
package my.example.conditions;
public class NumberIsEvenCondition implements Condition<Integer> {
#Override
public boolean appliesTo(Integer i) {
return (i % 2 == 0);
}
}

Categories