12 Commits

Author SHA1 Message Date
3322901cb1 v1.0.5 pom 2024-05-03 12:12:47 -04:00
bf06433a4d Merge branch 'develop' into stable 2024-05-03 12:12:26 -04:00
e437234c28 share models by tenant 2024-05-03 12:12:15 -04:00
e5debe9a47 v1.0.4 pom 2024-05-03 12:11:48 -04:00
8b1c958ddc Merge branch 'develop' into stable 2024-05-02 15:58:06 -04:00
5d172e234e fix enabled typo 2024-05-02 15:57:57 -04:00
10a732adb6 v1.0.3 2024-04-22 13:04:41 -04:00
58e84c88d9 Merge branch 'develop' into stable 2024-04-22 13:04:24 -04:00
185047e755 using @EnableScheduling 2024-04-22 13:04:06 -04:00
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
188059e0f3 refactored to scheduled execution; not event driven 2024-04-16 14:50:19 -04:00
9 changed files with 94 additions and 224 deletions

View File

@@ -13,20 +13,24 @@ 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.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. |
| 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. |
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.

View File

@@ -5,7 +5,7 @@
<groupId>com.inteligr8.alfresco.activiti</groupId>
<artifactId>model-share-activiti-app-ext</artifactId>
<version>1.0.1</version>
<version>1.0.5</version>
<packaging>jar</packaging>
<name>Model Share APS Extension</name>

View File

@@ -17,6 +17,7 @@ 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.
@@ -24,6 +25,7 @@ import org.springframework.context.annotation.FullyQualifiedAnnotationBeanNameGe
* @author brian@inteligr8.com
*/
@Configuration
@EnableScheduling
@ComponentScan(
basePackages = "com.inteligr8.alfresco.activiti.share",
nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class

View File

@@ -1,33 +0,0 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.alfresco.activiti.share;
import org.activiti.engine.delegate.event.ActivitiEntityEvent;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.impl.persistence.entity.Entity;
public interface ActivitiEntityEventListener extends ActivitiEventListener {
@Override
default void onEvent(ActivitiEvent event) {
ActivitiEntityEvent eevent = (ActivitiEntityEvent) event;
this.onEntityEvent(event.getType(), (Entity) eevent.getEntity());
}
void onEntityEvent(ActivitiEventType eventType, Entity entity);
}

View File

@@ -1,61 +0,0 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.alfresco.activiti.share;
import java.util.List;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.repository.Model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class EngineStartEventListener extends NoFailActivitiEventListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${inteligr8.modelShareExtension.modelChunkSize:50}")
private int modelChunkSize;
@Autowired
private ProcessEngine services;
@Autowired
private ModelShareWorker shareWorker;
@Override
public void onEvent(ActivitiEvent event) {
this.logger.info("Starting to query all models to share all of them");
int page = 1;
int perPage = this.modelChunkSize;
List<Model> models = this.services.getRepositoryService().createModelQuery().listPage((page-1)*perPage, perPage);
while (!models.isEmpty()) {
for (Model model : models) {
this.logger.debug("Discovered model: {}: {}: {}", model.getCategory(), model.getId(), model.getName());
this.shareWorker.share(model);
}
page++;
models = this.services.getRepositoryService().createModelQuery().listPage((page-1)*perPage, perPage);
}
}
}

View File

@@ -1,55 +0,0 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.alfresco.activiti.share;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.impl.persistence.entity.Entity;
import org.activiti.engine.impl.persistence.entity.ModelEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class ModelEntityEventListener extends NoFailActivitiEventListener implements ActivitiEntityEventListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ModelShareWorker shareWorker;
@Override
public void onEntityEvent(ActivitiEventType eventType, Entity entity) {
this.validate(eventType);
if (entity instanceof ModelEntity) {
ModelEntity mentity = (ModelEntity) entity;
this.logger.debug("Caught model event: {}: {}: {}: {}", mentity.getCategory(), entity.getId(), mentity.getKey(), mentity.getName());
this.shareWorker.share(mentity);
} else {
this.logger.trace("Ignoring entity type: {}", entity.getClass());
}
}
private void validate(ActivitiEventType eventType) {
switch (eventType) {
case ENTITY_CREATED:
break;
default:
throw new IllegalArgumentException();
}
}
}

View File

@@ -14,12 +14,11 @@
*/
package com.inteligr8.alfresco.activiti.share;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@@ -28,43 +27,40 @@ public class ModelShareExtension implements Bootstrappable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private ProcessEngine services;
@Autowired
private ModelEntityEventListener entityListener;
@Autowired
private EngineStartEventListener startupListener;
/**
* On startup, scan all models and share them according to the configuration defined elsewhere.
*/
@Value("${inteligr8.modelShareExtension.shareAllModelsOnStartup:false}")
private boolean shareAllModelsOnStartup;
private ModelShareWorker worker;
@Value("${inteligr8.modelShareExtension.enabled:true}")
private boolean enabled;
private boolean bootstrapped;
@Override
public void onBootstrap() {
this.logger.trace("onBootstrap()");
this.registerListeners();
if (!this.enabled)
return;
this.logger.info("Model Share Extension enabled");
this.bootstrapped = true;
this.logger.info("Model Share Extension initialized");
}
private void registerListeners() {
this.logger.info("registerListeners()");
public boolean isEnabled() {
return this.enabled;
}
// execute every 10 minutes
@Scheduled(
fixedRateString = "${inteligr8.modelShareExtension.scanIntervalInMillis:600000}",
initialDelayString = "${inteligr8.modelShareExtension.scanDelayInMillis:30000}"
)
private void scheduled() {
if (!this.enabled || !this.bootstrapped)
return;
this.services.getRuntimeService().addEventListener(
this.entityListener,
ActivitiEventType.ENTITY_CREATED);
if (this.shareAllModelsOnStartup) {
// we can't use ENGINE_CREATED as it has already been created
this.services.getRuntimeService().addEventListener(
this.startupListener,
ActivitiEventType.ENGINE_CREATED);
this.startupListener.onEvent(null);
}
this.logger.trace("scheduled()");
this.worker.shareAllModels();
}
}

View File

@@ -24,6 +24,8 @@ import java.util.Map;
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;
@@ -47,6 +49,9 @@ public class ModelShareWorker implements Bootstrappable {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${inteligr8.modelShareExtension.modelChunkSize:50}")
private int modelChunkSize;
@Value("${inteligr8.modelShareExtension.shareChunkSize:25}")
private int shareChunkSize;
@@ -74,6 +79,9 @@ public class ModelShareWorker implements Bootstrappable {
@Value("${inteligr8.modelShareExtension.groups.canWriteForms:#{null}}")
private String writeFormDefsGroupNames;
@Autowired
private ProcessEngine services;
@Autowired
private ModelShareService shareService;
@@ -97,8 +105,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);
@@ -199,7 +207,42 @@ public class ModelShareWorker implements Bootstrappable {
map.put(key, value);
}
public void share(org.activiti.engine.repository.Model orgModel) {
public void shareAllModels() {
if (this.tenantId != null)
this.shareAllModels(this.tenantId);
this.shareAllModels(null);
}
public void shareAllModels(Long tenantId) {
this.logger.trace("Discovering models in tenant {} ...", this.tenantId);
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();
List<org.activiti.engine.repository.Model> models = query.listPage((page-1)*perPage, perPage);
while (!models.isEmpty()) {
this.logger.trace("Fetched page #{} of {} models in tenant {}", page, models.size(), this.tenantId);
for (org.activiti.engine.repository.Model model : models) {
this.logger.debug("Discovered model: {}: {}: {}", model.getCategory(), model.getId(), model.getName());
this.shareModel(model);
}
modelCount += models.size();
page++;
models = query.listPage((page-1)*perPage, perPage);
}
this.logger.trace("Discovered {} models in tenant {}", modelCount, this.tenantId);
}
public void shareModel(org.activiti.engine.repository.Model orgModel) {
Model model = (Model) this.modelService.getModel(Long.valueOf(orgModel.getId()));
Map<HashableGroup, SharePermission> shares = this.fetchCurrentModelShares(model);
@@ -268,8 +311,8 @@ public class ModelShareWorker implements Bootstrappable {
}
private void share(Model model, HashableGroup group, SharePermission permission) {
this.logger.debug("Sharing model with group: {}: {}: {}", model.getId(), model.getName(), group);
this.shareService.shareModelWithGroup(model, group.getGroup(), permission);
this.logger.info("Sharing model with group: {}: {}: {}", model.getId(), model.getName(), group);
this.shareService.shareModelWithGroup(model, group.getGroup(), permission).getShareDate();
}
public void unshareAll(Model model) {

View File

@@ -1,26 +0,0 @@
/*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.inteligr8.alfresco.activiti.share;
import org.activiti.engine.delegate.event.ActivitiEventListener;
public abstract class NoFailActivitiEventListener implements ActivitiEventListener {
@Override
public boolean isFailOnException() {
return false;
}
}