WIP prototype to use micronaut with hazelcast to create an in-memory data source that micronaut works from and uses Hazelcast's system for cross-app communication
- POST /form (Create a Form)
- GET /form/:uuid (Get a form)
- PATCH /form (Update a form)
- POST /form/:uuid/schema?isDefault=true (Create a Form Schema)
- PATH /form/:uuid/schema/:uuid?isDefault=true (Update a Form Schema)
- GET /form/:uuid/schema (Get the default schema for the form)
- GET /form/:uuid/schemas (Get all schemas)
- GET /form/:uuid/schema/:uuid (Get a specific form schema for a specific form)
- POST /form/validate (Generic validation)
- POST /form/:uuid/validate (Validation against the Default Form Schema)
- POST /form/:uuid/schemas/:schemaUuid/validate (Validation Against a specific Form Schema for a Specific Form)
- POST /form/:uuid/schemas/:schemaUuid/submit (For use with the Submission Handler)
Swagger file: `http://localhost:8080/swagger/forms-manager-1.0.yml
- Swagger-Ui:
http://localhost:8080/swagger-ui/index.html
- ReDoc:
http://localhost:8080/redoc/index.html
- RapiDoc:
http://localhost:8080/rapidoc/index.html
- Go to project
- Run
docker build -t forms-manager .
- Run
docker run -p 8080:8080 --name forms-manager forms-manager
Notes:
-
Use Distributed Task for sync requests: Single Request <-> Single Response
-
Use ReliableTopic for broadcasts / Messages that do not have a single destination and can be received by many.
-
Added a InjectAware annotation to tell MN when to inject context
-
If custom code needs to be shipped around to each member (the member does not actual have the code), then Jet tasks should be used.
-
If required to use confirmation authentication (to perform a sensitive action), then we do JWT + Password login on the endpoint.
-
Shiro Annotations supported through Micronaut SecurityRule beans
-
Issue with performance on Paging Predicate for Hazelcast: hazelcast/hazelcast#10828. A replacement of the Hazelcast query engine is supposed to be introduced for 4.1/4.2 that would "fix" this issue. Timeline is ~Fall 2020.
-
Users are owned by a Tenant. A user can have access to many tenants (through permissions), but lives in only one tenant.
-
A Administrative users or users who control multiple tenants will still have a "home tenant" to which they log in with (where they user exists), and then their permissions provide which tenants they can access.
-
Distributed Query for Hazelcast only works with items that are in Mem (obviously ;) ). So If items are evicted from the mem based on TTL, then query will not find them. Items that require search need to be findable based ID. Use a object as ID that is a makeup of known values for that item.
-
MapKeys are based on a combination of multiple values in a Entity that make up the unique text. That text is this turned into a UUID v3
-
When using the Near Cache on a IMap with Shrio Authz cache, make sure Principal Objects are Serializable (example PrimaryPrincipal.class had to be made serializable) so it can be cached by Shrio. @TODO Review for ways to remove this need.
-
add expression handler to build custom dynmaic permissions for shiro wildcard https://stackoverflow.com/questions/2286648/named-placeholders-in-string-formatting
-
InjectAware needs to be added to all Tasks. The inherit does not work apply to the inject.. it would only inject on the extended properties
-
Fields that are
@Inject
should try and always make them public because that way it does not require reflection. -
ExecutorService in MN has overlap interfaces with Hazelcast: as a result when you create a bean for IExecutorService it will trigger Bean Event triggers for ExecutorService and cause the app to fail.
-
There are 4 Serializers:
- jackson used internally by Camunda
- jackson created by Micronuat (with Singleton imports and the Application.yml configuration)
- Jackson used for Smile: formsmanager.core.serialization.JacksonSmileSerialization.class
- Jackson used for Map Store Persistence (JDBC persistence with Hazelcast Mapstore (How the data is stored as JSON in the DB)): formsmanager.core.map.persistence.serialization.JacksonDBSerializationFactory.class
-
Add ability for dynamic permissions applied to objects such as: a Single BU memberhsip but difference between staff and citizne: how do you filter out services that are only for the staff? Would need to be able to apply a logic to staff that only staff can see a specific service (maybe a custom predicate rule applied?)
questions
- Member selection for distributed tasks: To only have specific nodes work on specific tasks.
todo:
- Add Avro support
- Build an annotation processor for automating field logic updates such as Optimistic Locking increments, UpdatedDate, etc. Basically any field that cannot be updated by the user.
- Move controllers to module that is Hazelcast client based
- Add Camunda node
- Add TTL for maps with configuration options to clear out/evict memory for seldom used objects. (Evict does not delete from the hazelcast mapstore, it only evicts from in-memory)
- Add local caching (with TTL) for WildcardPermissions that were generated from Strings in the User Entity
- Add updated to UserDetails (Micronaut security) for working with Subject from Shiro, so we can use the Micronaut Annotation support
@secured
and accessing the Shiro permission validator. - Eventually move to an ID Generator that is not built in UUID, as UUID is only 99.99 and could be collisions
- Add user registration limits for username and password: length, password complexity, etc
- Create a user Entity update page for Admin and for Regular users.
- ** deal with scenarios of who owns tenants and groups: and how someone can assign the owner of an object: (Likely a permission)**
- ** Convert to modern binary serialization to replace Serialization for Shiro Cache (Using Hz) **
- Create a new PrincipalCollection class. Dont use SimplePrincipalCollection as it does not have good enough typing...
- Create a Mixin Bean support to auto add mixins based on configs
- Add Indexes and composite indexes in hz config for Camunda History entities
- Remove use of Injecting IMap as a Bean due to potential deadlocks with Eager MapStore loading
Python execution service:
https://groups.google.com/forum/#!topic/hazelcast/jGZcxpNDc5k https://github.com/hazelcast/hazelcast-python-client https://github.com/hazelcast/hazelcast-python-client/blob/master/hazelcast/proxy/executor.py
Formio Links:
- https://formio.github.io/formio.js/app/examples/
- Web builder: https://formio.github.io/formio.js/app/builder
- CUSTOM SUBMISSION: https://formio.github.io/formio.js/app/examples/customendpoint.html
- Thank you page: https://formio.github.io/formio.js/app/examples/thanyou.html
- Multiple Languages: https://formio.github.io/formio.js/app/examples/language.html
A Hazelcast based CRUD Repository is four components:
- EntityWrapper
- HazelcastRepository
- MapStore
- MapStoreRepository
See FormRepository.class for example usage.
The entity wrapper is an extend of MapStoreItemWrapperEntity which is a generic for creating the DB entity that will be stored in the JdbcRepository.
Example:
@Entity
class FormEntityWrapper(key: UUID,
classId: String,
value: FormEntity) : MapStoreItemWrapperEntity<FormEntity>(key, classId, value)
This is the repository specific to interacting with Hazelcast. It is the repository that you would inject into your services that wish to perform CRUD operations.
The repository provides:
- create
- update
- exists
- delete
- find by key
Example:
@Singleton
class FormHazelcastRepository(private val jetService: HazelcastJetManager) :
HazelcastCrudRepository<UUID, FormEntity>(
jetService = jetService,
mapName = "forms"
) {
}
A MapStore Repository extends the CruddableMapStoreRepository which extends the Micronaut CrudRepository. This repository provides the JDBC connectivity that the MapStore will use.
Example:
@JdbcRepository(dialect = Dialect.H2)
interface FormsMapStoreRepository : CrudableMapStoreRepository<FormEntityWrapper>
The MapStore should extend from the CrudableMapStore. The CrudableMapStore provides ready to use setup with the CrudableMapStoreRepository.
Example:
@Singleton
class FormsMapStore(mapStoreRepository: FormsMapStoreRepository) :
CurdableMapStore<FormEntity, FormEntityWrapper, FormsMapStoreRepository>(mapStoreRepository)
- Create: Optimistic Locking performs automatically at the Entry Processor level before the insert/update logic is executed.
- Update logic requires specific updates of fields such as:
formHazelcastRepository.update(formEntity) { originalItem, newItem -> newItem.copy( ol = originalItem.ol + 1, id = originalItem.id, type = originalItem.type, createdAt = originalItem.createdAt, updatedAt = Instant.now() ) }
- During an Update, the new item is injected into the .update() to apply the internal automatic checks and updates (such as the Optimistic locking check).
Words:
Default Strategy Simple Basic Factory Service System Handler
"roles:create:${entity.tenant}" "roles:read:${g.tenant}" "roles:update:${originalItem.tenant}"
"groups:create:${groupEntity.tenant}" "groups:read:${g.tenant}" "groups:update:${originalItem.tenant}"
"users:read:${ue.tenant}:${ue.internalId}" "users:update:${userEntity.tenant}:${userEntity.internalId}"
"tenants:create" "tenants:read:${te.internalId}" "tenants:update:${originalItem.internalId}"
"forms:create:${formEntity.owner}:${formEntity.tenant}" "forms:read:${fe.owner}:${fe.tenant}" "forms:update:${originalItem.owner}:${originalItem.tenant}" "forms:update:${fe.owner}:${fe.tenant}" "form_schemas:update:${fe.owner}:${fe.tenant}" "forms:update:${fe.owner}:${fe.tenant}" "form_schemas:create:${fe.owner}:${fe.tenant}" "form_schemas:read:${fe.owner}:${fe.tenant}" "form_schemas:read:${fe.owner}:${fe.tenant}" "form_schemas:validate:${fe.owner}:${fe.tenant}"
Prevent race conditions and support operation conditions. Retry correlations that failed to correlate.
Sometimes you have correlation occurring to multiple instances. But not all instances were ready at the same time, and durring the correlation, some were missed. Retry support is provided to continue to correlate until all expected correlations occurred.
Actions:
- Retry if no matches
- Exponential Retry
- Expected Matches: Keep Retry until number of expected matches. Throw error if too many matches?
- Retry after expiry
- Correlate with Response + timeout: if timeout is reached, the result is not waited for, but client will still get a 200.
- Correlate without Response
- Callback URL on completion?
- Correlate with Start Event
- Correlate with Activity Execution
- Correlate with StartEvent and/or Activity Execution
- Max Events in Buffer
- Max Correlated Messages In Buffer
- Max Correlating Messages in Buffer
- Pause Un-Correlated
- Metadata about Message Submitter
- Delegate code support to submit into buffer from scripts and delegates.
- Complex permissions support: Send Messages per topic, process Def, with and without variables, etc.
tasks:complete tasks:view tasks:assign tasks:claim tasks:unclaim
tasks:read_variable:*
Form Submissions are multiple variables. They are json If data should not be shared, then it should be split into a different variable for visibility by another user. (can do this because the practice is that variables are more stable) Example: UT1 -> Submit form. in UT2 user should only be able to see specific fields of a Form. So in UT2, a new form should be created that loads that specific data. If further constraints are needed, then Process Instance separation, or Variable permissions should be used.
Further permissions logic and special considerations should VERY likely be used within a Cases model. WHere a Process Instance is where is being done, but the outcome is stored in a case.
Task Complete:
- Is Assignee
- Is member of Candidate Users
- Is Member of Candidate Group
- Has instance permission
View Completed Task Info:
- Is Assignee
- Is Member of Candidate Group
- Has Instance Permission
- Participated in process instance (optional toggle) (query of activity history for that process instance)
Process Instance:
- Has Instance Permission
- Participated in Task in instance (query of activity history for that process instance)
- Has Instance Permission to Definition
- Member of Process Instance Owner BU
- Member of Process Definition Owner BU
Process Definition:
- Process Definition Owner BU
- Definition instance Permission
Dynamics:
- Tenant
- BU membership
- Process Instance
Workflow for the workaholic
Real world business problems with real world workflow solutions
-
Forms Manager
- Form Schemas Storage and Management
- Apply a Form Schema to a BPMN Form key
- BPMN Start Event Forms Support
- BPMN User Task Forms Support
- Submit data
-
Camunda Workflow Engine
- Reactive External Tasks
- Hazelcast History Event Provider
- Micronaut Context
- Message Buffer
- Shiro Security (optional)
- Hazelcast backed variables
- Camunda Extensions Properties Caching and API
- Business Calendar Support
-
Hazelcast Data Grid + Hazelcast Jet
- Store any entity as native object and let hazelcast store it in the DB for you!
- Distributed Queries
- Repository Interfaces for Hazelcast Map access: Store your data like a regular Data Repository interface
-
Query API: use SQL Where Query Syntax for query of objects and json data.
- Query Camunda History Data
- Query All objects in the platform.
-
Shiro Security
- Groups, Roles, Permissions
- JWT Security
-
Multi-Tenancy
-
Services
-
Business Units