5 Commits

Author SHA1 Message Date
b84c311921 v1.0.2 pom 2024-04-16 14:51:04 -04:00
63d34e6044 Merge branch 'develop' into stable 2024-04-16 14:50:42 -04:00
d55d03be30 v1.0.1 pom 2024-03-19 09:51:06 -04:00
89b616e18a Merge branch 'develop' into stable 2024-03-19 09:50:50 -04:00
e4438da594 v1.0.0 pom 2024-03-14 17:53:46 -04:00
5 changed files with 40 additions and 159 deletions

View File

@@ -13,24 +13,20 @@ You can install this like any JAR extension. It just needs to be put in the web
There are several configuration properties available to shape how this extension shares models in your APS installation.
| Property | Default | Purpose |
| -------------------------------------------------------- | -------- | ------- |
| `inteligr8.modelShareExtension.enabled` | `true` | Enablement; `false` will disable all the behaviors of this extension. |
| `inteligr8.modelShareExtension.scanDelayInMillis` | `30000` | The delay before the first scan for all models to share. |
| `inteligr8.modelShareExtension.scanIntervalInMillis` | `600000` | The interval between scans for all models to share. |
| `inteligr8.modelShareExtension.groups.canRead` | | What groups should be granted read permission to models of all types. |
| `inteligr8.modelShareExtension.groups.canWrite` | | What groups should be granted write permission to models of all types. |
| `inteligr8.modelShareExtension.groups.canReadApps` | | What groups should be granted read permission to App models. |
| `inteligr8.modelShareExtension.groups.canWriteApps` | | What groups should be granted write permission to App models. |
| `inteligr8.modelShareExtension.groups.canReadProcesses` | | What groups should be granted read permission to Process models. |
| `inteligr8.modelShareExtension.groups.canWriteProcesses` | | What groups should be granted write permission to Process models. |
| `inteligr8.modelShareExtension.groups.canReadForms` | | What groups should be granted read permission to Form models. |
| `inteligr8.modelShareExtension.groups.canWriteForms` | | What groups should be granted write permission to Form models. |
| `inteligr8.modelShareExtension.modelChunkSize` | `50` | When querying for all models, how many models should be queried per page. |
| `inteligr8.modelShareExtension.shareChunkSize` | `25` | When querying all user/group share permissions on a single model, how many records should be queried per page. |
| Property | Default | Purpose |
| -------------------------------------------------------- | ------- | ------- |
| `inteligr8.modelShareExtension.groups.canRead` | | What groups should be granted read permission to models of all types. |
| `inteligr8.modelShareExtension.groups.canWrite` | | What groups should be granted write permission to models of all types. |
| `inteligr8.modelShareExtension.groups.canReadApps` | | What groups should be granted read permission to App models. |
| `inteligr8.modelShareExtension.groups.canWriteApps` | | What groups should be granted write permission to App models. |
| `inteligr8.modelShareExtension.groups.canReadProcesses` | | What groups should be granted read permission to Process models. |
| `inteligr8.modelShareExtension.groups.canWriteProcesses` | | What groups should be granted write permission to Process models. |
| `inteligr8.modelShareExtension.groups.canReadForms` | | What groups should be granted read permission to Form models. |
| `inteligr8.modelShareExtension.groups.canWriteForms` | | What groups should be granted write permission to Form models. |
| `inteligr8.modelShareExtension.shareAllModelsOnStartup` | `false` | Set to `true` to perform a startup scanning of models, sharing them according to the configuration. |
| `inteligr8.modelShareExtension.modelChunkSize` | `50` | When querying for all models, how many models should be queried per page. |
| `inteligr8.modelShareExtension.shareChunkSize` | `20` | When querying all user/group share permissions on a single model, how many records should be queried per page. |
You can specify these at startup as JVM system properties.
When specifying groups, use commas to separate each entry. You can also prefix each group with "sys:" or "org:" to target a specific system or organizational/functional group. The extension will query by group name and then external ID in each case.
Any specification of a specific group permission (e.g. `canReadApps`) will override any specification for the same group in the generic `canRead` or `canWrite` property.

12
pom.xml
View File

@@ -5,7 +5,7 @@
<groupId>com.inteligr8.alfresco.activiti</groupId>
<artifactId>model-share-activiti-app-ext</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0.2</version>
<packaging>jar</packaging>
<name>Model Share APS Extension</name>
@@ -39,11 +39,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.release>17</maven.compiler.release>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.release>11</maven.compiler.release>
<aps.version>24.3.0</aps.version>
<aps.version>2.4.1</aps.version>
</properties>
<dependencies>
@@ -154,7 +154,7 @@
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.7.0</version>
<version>1.6.13</version>
<configuration>
<serverId>ossrh</serverId>
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>

View File

@@ -17,7 +17,6 @@ package com.activiti.extension.conf;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGenerator;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* A means for injecting packages to scan for the Spring context.
@@ -25,7 +24,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
* @author brian@inteligr8.com
*/
@Configuration
@EnableScheduling
@ComponentScan(
basePackages = "com.inteligr8.alfresco.activiti.share",
nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class

View File

@@ -21,10 +21,6 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.activiti.domain.idm.User;
import com.activiti.security.SecurityUtils;
import com.activiti.service.api.UserService;
@Component
public class ModelShareExtension implements Bootstrappable {
@@ -32,12 +28,6 @@ public class ModelShareExtension implements Bootstrappable {
@Autowired
private ModelShareWorker worker;
@Autowired
private UserService userService;
@Value("${inteligr8.modelShareExtension.runAs:admin@app.activiti.com}")
private String runAsUser;
@Value("${inteligr8.modelShareExtension.enabled:true}")
private boolean enabled;
@@ -48,35 +38,20 @@ public class ModelShareExtension implements Bootstrappable {
public void onBootstrap() {
this.logger.trace("onBootstrap()");
if (!this.enabled)
return;
this.logger.info("Model Share Extension enabled");
this.bootstrapped = true;
this.logger.info("Model Share Extension initialized");
}
public boolean isEnabled() {
return this.enabled;
if (this.enabled) {
this.bootstrapped = true;
this.logger.info("Model Share Extension initialized");
}
}
// execute every 10 minutes
@Scheduled(
fixedRateString = "${inteligr8.modelShareExtension.scanIntervalInMillis:600000}",
initialDelayString = "${inteligr8.modelShareExtension.scanDelayInMillis:30000}"
)
@Scheduled(fixedRate = 600000)
private void scheduled() {
if (!this.enabled || !this.bootstrapped)
return;
this.logger.trace("scheduled()");
this.logger.debug("Assuming user: {}", this.runAsUser);
User runAsUser = this.userService.findUserByEmail(this.runAsUser);
SecurityUtils.assumeUser(runAsUser);
this.logger.debug("Assumed user: {}", runAsUser.getId());
this.worker.shareAllModels();
this.logger.trace("scheduled()");
this.worker.share();
}
}

View File

@@ -25,7 +25,6 @@ import java.util.Map.Entry;
import java.util.Set;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +34,6 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;
import com.activiti.domain.editor.AbstractModel;
import com.activiti.domain.editor.Model;
import com.activiti.domain.editor.ModelShareInfo;
import com.activiti.domain.editor.SharePermission;
@@ -106,8 +104,8 @@ public class ModelShareWorker implements Bootstrappable {
@Override
public void onBootstrap() {
this.logger.trace("onBootstrap()");
this.logger.trace("onBootstrap({})");
this.tenantId = this.tenantService.findTenantId();
this.groups = this.toMap(this.findGroups(this.readGroupNames), SharePermission.READ);
this.toMap(this.groups, this.findGroups(this.writeGroupNames), SharePermission.WRITE);
@@ -208,110 +206,36 @@ public class ModelShareWorker implements Bootstrappable {
map.put(key, value);
}
public void shareAllModels() {
this.shareApsFormModels();
this.shareApsDataModels();
this.shareApsDecisionTableModels();
this.shareApsProcessModels();
this.shareApsAppModels();
if (this.tenantId != null) {
this.shareAllActivitiModels(this.tenantId);
} else {
this.shareAllActivitiModels(null);
}
}
private void shareApsFormModels() {
this.logger.trace("Discovering APS form models");
List<AbstractModel> models = this.modelService.getModelsByModelTypeAndReferenceId(Model.MODEL_TYPE_FORM, null);
for (AbstractModel model : models) {
this.logger.debug("Discovered APS form model: {} [{}]", model.getName(), model.getId());
this.shareModel((Model) model);
}
}
private void shareApsDataModels() {
this.logger.trace("Discovering APS data models");
List<AbstractModel> models = this.modelService.getModelsByModelTypeAndReferenceId(Model.MODEL_TYPE_DATA_MODEL, null);
for (AbstractModel model : models) {
this.logger.debug("Discovered APS data model: {} [{}]", model.getName(), model.getId());
this.shareModel((Model) model);
}
}
private void shareApsDecisionTableModels() {
this.logger.trace("Discovering APS decision table models");
List<AbstractModel> models = this.modelService.getModelsByModelTypeAndReferenceId(Model.MODEL_TYPE_DECISION_TABLE, null);
for (AbstractModel model : models) {
this.logger.debug("Discovered APS decision table model: {} [{}]", model.getName(), model.getId());
this.shareModel((Model) model);
}
}
private void shareApsProcessModels() {
this.logger.trace("Discovering APS process models");
List<AbstractModel> models = this.modelService.getModelsByModelTypeAndReferenceId(Model.MODEL_TYPE_BPMN, null);
for (AbstractModel model : models) {
this.logger.debug("Discovered APS process model: {} [{}]", model.getName(), model.getId());
this.shareModel((Model) model);
}
}
private void shareApsAppModels() {
this.logger.trace("Discovering APS app models");
List<AbstractModel> models = this.modelService.getModelsByModelTypeAndReferenceId(Model.MODEL_TYPE_APP, null);
for (AbstractModel model : models) {
this.logger.debug("Discovered APS app model: {} [{}]", model.getName(), model.getId());
this.shareModel((Model) model);
}
}
private void shareAllActivitiModels(Long tenantId) {
this.logger.trace("Discovering activiti models in tenant {} ...", tenantId);
public void share() {
this.logger.trace("Discovering models ...");
long modelCount = 0L;
int page = 1;
int perPage = this.modelChunkSize;
ModelQuery query = this.services.getRepositoryService().createModelQuery()
.latestVersion();
if (tenantId != null)
query.modelTenantId(String.valueOf(tenantId));
else query.modelWithoutTenantId();
this.logger.trace("Discovering activiti models with page size: {}", perPage);
List<org.activiti.engine.repository.Model> models = query.listPage((page-1)*perPage, perPage);
if (models.isEmpty()) {
this.logger.debug("No activiti models; procDef count: {}", this.services.getRepositoryService().createProcessDefinitionQuery().latestVersion().count());
}
List<org.activiti.engine.repository.Model> models = this.services.getRepositoryService().createModelQuery().listPage((page-1)*perPage, perPage);
while (!models.isEmpty()) {
this.logger.trace("Fetched page #{} of {} activiti models in tenant {}", page, models.size(), tenantId);
this.logger.trace("Fetched page #{} of {} models", page, models.size());
for (org.activiti.engine.repository.Model model : models) {
this.logger.debug("Discovered model: {}: {}: {}", model.getCategory(), model.getId(), model.getName());
this.shareModel(model);
this.share(model);
}
modelCount += models.size();
page++;
models = query.listPage((page-1)*perPage, perPage);
models = this.services.getRepositoryService().createModelQuery().listPage((page-1)*perPage, perPage);
}
this.logger.trace("Discovered {} activiti models in tenant {}", modelCount, tenantId);
this.logger.trace("Discovered {} models", modelCount);
}
public void shareModel(org.activiti.engine.repository.Model orgModel) {
public void share(org.activiti.engine.repository.Model orgModel) {
Model model = (Model) this.modelService.getModel(Long.valueOf(orgModel.getId()));
this.shareModel(model);
}
public void shareModel(Model model) {
Map<HashableGroup, SharePermission> shares = this.fetchCurrentModelShares(model);
switch (model.getModelType()) {
case Model.MODEL_TYPE_APP:
case 3:
for (Entry<HashableGroup, SharePermission> group : this.groups.entrySet()) {
if (this.appDefsGroups.containsKey(group.getKey())) {
this.logger.trace("The default group permission is overridden by the app group permission: {}", group.getKey());
@@ -322,7 +246,7 @@ public class ModelShareWorker implements Bootstrappable {
for (Entry<HashableGroup, SharePermission> group : this.appDefsGroups.entrySet())
this.share(shares, model, group.getKey(), group.getValue());
break;
case Model.MODEL_TYPE_BPMN:
case 0:
for (Entry<HashableGroup, SharePermission> group : this.groups.entrySet()) {
if (this.processDefsGroups.containsKey(group.getKey())) {
this.logger.trace("The default group permission is overridden by the app group permission: {}", group.getKey());
@@ -333,7 +257,7 @@ public class ModelShareWorker implements Bootstrappable {
for (Entry<HashableGroup, SharePermission> group : this.processDefsGroups.entrySet())
this.share(shares, model, group.getKey(), group.getValue());
break;
case Model.MODEL_TYPE_FORM:
case 1:
for (Entry<HashableGroup, SharePermission> group : this.groups.entrySet()) {
if (this.formDefsGroups.containsKey(group.getKey())) {
this.logger.trace("The default group permission is overridden by the app group permission: {}", group.getKey());
@@ -360,15 +284,9 @@ public class ModelShareWorker implements Bootstrappable {
for (ModelShareInfo share : shares)
map.put(new HashableGroup(share.getGroup()), share.getPermission());
pageable = pageable.next();
shares = this.shareInfoRepo.findByModelIdOrderByShareDateAsc(model.getId(), pageable);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Found existing shares: {}", map);
} else {
this.logger.debug("Found existing shares: {}", map.size());
}
return map;
}
@@ -424,13 +342,7 @@ public class ModelShareWorker implements Bootstrappable {
@Override
public boolean equals(Object obj) {
if (obj instanceof HashableGroup) {
return this.group.getId().equals(((HashableGroup) obj).getGroup().getId());
} else if (obj instanceof Group) {
return this.group.getId().equals(((Group) obj).getId());
} else {
return false;
}
return this.group.getId().equals(((Group) obj).getId());
}
@Override