Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/wkgcass/pojo-agent

A tool which enhances your pojo, powered by java-agent.
https://github.com/wkgcass/pojo-agent

Last synced: about 2 months ago
JSON representation

A tool which enhances your pojo, powered by java-agent.

Awesome Lists containing this project

README

        

# pojo-agent

A tool which enhances your pojo, powered by java-agent.

**maven**

```xml

io.vproxy
pojo-agent-api
1.2.0

```

**gradle**

```groovy
implementation group: 'io.vproxy', name: 'pojo-agent-api', version: '1.2.0'
```

## current supported functions

* Helps you know that a setter is invoked or not.
* Auto implement `updateFrom` method, which helps you assign properties from one entity to another.
* Auto property validation, supports `@MustExist`, `@MustNotExist`, `@MustNotNull`.

## build

```
./gradlew clean jar
```

The agent is in `build/libs/pojo-agent.jar`.
You can also get the pre-built agent in the release pages.

## test

```
./gradlew clean suite
```

## how to use

Works on java 8, 11, 17, 21, and kotlin.

### step 1

Add `@Pojo` annotation on you pojo:

```java
@Pojo
public class MyEntity {
private String id;
// getters ...
// setters ...
}
```

or you can use `data class` if you are using kotlin:

```kotlin
@Pojo
data class MyEntity(
var id: String = ""
)
// getters and setters are generated by kotlin compiler
```

#### step 1.1: updateFrom

You may let your entity class implement the template interface `PojoUpdateFrom`, or you can simply add methods with the same signatures defined in this interface.

* `preUpdateFrom` is optional, if defined, it will be called before calling `updateFrom`
* `postUpdateFrom` is optional, if defined, it will be called after calling `updateFrom`

Both `preUpdateFrom` and `postUpdateFrom` are optional and can be private (if not implementing the `PojoUpdateFrom` template interface).

```java
@Pojo
public class MyEntity implements PojoUpdateFrom {
@PojoAutoImpl
@Override
public void updateFrom(SimpleEntity another) {
throw new RequirePojoAutoImplException();
}
}
```

#### step 1.2: validate

You may let your entity class implement the template interface `PojoValidate`, or you can simply add methods with the same signatures defined in this interface.

* `preValidate` is optional, if defined, it will be called before calling `validate`
* `postValidate` is optional, if defined, it will be called after calling `validate`

Both `preValidate` and `postValidate` are optional and can be private (if not implementing the `PojoValidate` template interface).

Add `@MustExist`, `@MustNotExist`, `@MustNotNull` annotations on fields which you want to validate. All these annotations takes one **optional** argument: an integer, which is a bit mask, which helps you specify what kind of validation is required when executing an action.

```java
import static io.vproxy.pojoagent.api.CommonActions.*;

@Pojo
public class MyEntity implements PojoValidate {
@MustNotExist(ALL) // must not exist in any condition
private int id;

@MustExist(CREATE) // must exist when creating
@MustNotExist(~CREATE) // must not exist except creating
@MustNotNull(CREATE) // must exist and must not be null when creating
private String name;

@PojoAutoImpl
@Override
public ValidationResult validate(int action) {
throw new RequirePojoAutoImplException();
}

// getters setters ...
}
```

You may leave the annotation argument empty, which will be 0 by default, and use `entity.validate(0)` (or `entity.validate()` if you are implementing the template interface) to validate the entity.

The result of the `validate` method is a non-null object: `ValidationResult`.

#### step 1.3: setAllFields and unsetAllFields

Pojo-agent provides you a way to consider all fields are set or not set (if a field is `set`, the field existence assertion against that field will pass).

You may let your entity class implement the template interfaces `PojoSetAllFields` or `PojoUnsetAllFields`, or you can simply add methods with the same signatures defined in these interfaces.

```java
@Pojo
public class MyEntity implements PojoSetAllFields, PojoUnsetAllFields {
// fields ...
// getters setters ...

@PojoAutoImpl
@Override
public void setAllFields() {
throw new RequirePojoAutoImplException();
}

@PojoAutoImpl
@Override
public void unsetAllFields() {
throw new RequirePojoAutoImplException();
}
}
```

### step 2

Add `@PojoCaller` on the method which uses functions in `PojoAgent` helper class:

```java
@PojoCaller
public void manipulateMyEntity(MyEntity entity) {
// ...
}
```

### step 3

Use `PojoAgent` helper class in the `@PojoCaller` functions:

```java
// check whether a field is set
PojoAgent.fieldIsSet(entity.getId())

// unset the field
PojoAgent.unsetField(entity.getId())
// then you will get `false` from `fieldIsSet` call after calling `unsetField`
// However note that the value of this property will not be modified, you can still get correct result from `getId()`

// set the field without touching it's setter
PojoAgent.setField(entity.getId())
// then you will get `true` from `fieldIsSet` call after calling `setField`
```

### step 4

Add `-javaagent:${path-to-pojo-agent.jar}` to your `java` command.

> If you are using java 17, you may need to add extra arguments: `--add-opens java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED --add-opens java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED`

### optional

Add `PojoAgent.ensurePojoAgent();` to the entry of your application. This method will check and throw an exception if `pojo-agent` is not loaded.

## sample

The sample code is in `./sample/src/main/`

Run:

* `./gradlew clean sample`
* `./gradlew clean ktsample`

to see the output.