My environment:
Alfresco Share v5.2.d (r134641-b15, Aikau 1.0.101.3, Spring Surf
5.2.d, Spring WebScripts 6.13, Freemarker 2.3.20-alfresco-patched, Rhino 1.7R4-alfresco-patched, Yui 2.9.0-alfresco-20141223)
Alfresco Community v5.2.0 (r134428-b13) schema 10005
When I start the workflow, I can assign executors - the list of users who will participate in the business process. I need to get a list of all those users and display this list in the reviewTask screen.
To display this data I have the custom FreeMarker template. From this template I'll call my Web Script.
To interact with the repository through REST I use the Web Script Framework MVC - the controller that executes the logic of receiving the list of users and FreeMarker template for the JSON response.
I've been offered the great idea, how it can be done. But I faced with some problem.
Let's say, I launched multiple instances of the business process. In the Workflow Console I can see the following:
id: activiti$1801 , desc: DESCRIPTION_1 , start date: Sun Mar 12 19:19:09 GST 2017 , def: activiti$activitiParallelReview v1
id: activiti$2005 , desc: DESCRIPTION_2 , start date: Sun Mar 12 20:11:57 GST 2017 , def: activiti$activitiParallelReview v1
id: activiti$2138 , desc: DESCRIPTION_3 , start date: Sun Mar 12 20:12:55 GST 2017 , def: activiti$activitiParallelReview v1
Thus I have three IDs:
activiti$1801
activiti$2005
activiti$2138
These IDs are available for me only in the Workflow Console.
How can I get these identifiers in the controller?
Can I, for example, write a class that will be called by Activiti? Let's say,
public class SomeListener implements TaskListener {
...
#Override
public void notify(DelegateTask task) {
...
String taskId = task.getId();
...
}
}
Then attach it to the complete event of the some task and pass ID to the controller.
Is this the right way?..
I would be very grateful for the information. Thanks to all.
I wrote a class that implements the ExecutionListener interface. Then added it as a listener on the transition from mswf:submitParallelReviewTask to mswf:activitiReviewTask.
All the properties that I need, I can get here (Thank to Gagravarr for his answer):
public class PropertiesReceiver implements ExecutionListener {
private static final long serialVersionUID = 1L;
private static Log logger = LogFactory.getLog(PropertiesReceiver.class);
#Override
public void notify(DelegateExecution delegateExecution) throws Exception {
// TODO Auto-generated method stub
String processInstanceId = delegateExecution.getProcessInstanceId();
logger.debug("processInstanceId == " + processInstanceId);
ActivitiScriptNodeList assignees =
(ActivitiScriptNodeList) delegateExecution.getVariable("bpm_assignees");
for (ActivitiScriptNode personNode : assignees) {
String username = (String) personNode.getProperties().get("cm:userName");
logger.debug("username == " + username);
}
}
}
alfrescotomcat-stdout.2017-03-13.log:
...
2017-03-13 11:03:12,244 DEBUG [mswf.bpm.PropertiesReceiver] [http-apr-8080-exec-3] processInstanceId == 26205
2017-03-13 11:03:12,248 DEBUG [mswf.bpm.PropertiesReceiver] [http-apr-8080-exec-3] username == first
2017-03-13 11:03:12,250 DEBUG [mswf.bpm.PropertiesReceiver] [http-apr-8080-exec-3] username == second
...
But this is a separate project, packaged in an AMP file.
Ok, another way. I can get the id of the process instance in ScriptExecutionListener:
execution.getProcessInstanceId();
And call the Alfresco REST API from the Share side to retrieve all the information that I need (Thanks to Martin Ehe).
For example, this call allows to get all the executors:
http://localhost:8080/alfresco/api/-default-/public/workflow/versions/1/processes/26205/variables
,where 26205 == id of the process instance.
But where should I save this ID to access it from Share? Can I add aspect to the workflow model and whether it's right?..
please try this.
public JSONObject test(String userName) {
JSONObject allTasks = new JSONObject();
companyHome = repository.getCompanyHome();
try {
List<WorkflowTask> wft=serviceRegistry.getWorkflowService().getAssignedTasks(userName,WorkflowTaskState.IN_PROGRESS );
JSONArray ja = new JSONArray();
System.out.println("WF sizes = "+wft.size());
for (WorkflowTask temp : wft) {
JSONObject userWFDetails = new JSONObject();
userWFDetails.put("taskId", temp.getId());
userWFDetails.put("taskDesc", temp.getDescription());
userWFDetails.put("wfInstanceId", temp.getPath().getInstance().getId());
System.out.println("wf tasks-"+temp);
System.out.println("task id-"+temp.getId());
System.out.println("wf instance id-"+temp.getPath().getInstance().getId());
System.out.println("wf path id-"+temp.getPath().getId());
ja.put(userWFDetails);
}
allTasks.put("userTasksDetails", ja);
} catch (Exception e) {
e.printStackTrace();
}
return allTasks;
}
pass your userName. and you will get all the user task details. workflow instance details in json format.
Related
A few examples for implementing HealthIndicator needs a KafkaTemplate. I don't actually manually create a KafkaTemplate but the HealthIndicator requires one. Is there a way to automatically grab the created KafkaTemplate (that uses the application.yml configurations)? This is versus manually creating duplicated configurations that already exist in the application.yml in a newly created consumerFactory.
See this answer.
(S)he wanted to get access to the template's ProducerFactory but you can use the same technique to just get a reference to the template.
That said, the binder comes with its own health indicator.
https://github.com/spring-cloud/spring-cloud-stream-binder-kafka/blob/a7299df63f495af3a798637551c2179c947af9cf/spring-cloud-stream-binder-kafka/src/main/java/org/springframework/cloud/stream/binder/kafka/KafkaBinderHealthIndicator.java#L52
We also had the same requirement of creating a custom health checker Spring Cloud stream. We leveraged the inbuild health checker(KafkaBinderHealthIndicator). But while injecting the KafkaBinderHealthIndicator bean facing lot of issue. So instead of that we inject the health checker holder HealthContributorRegistry, and got the KafkaBinderHealthIndicator bean from it.
Example:
#Component
#Slf4j
#RequiredArgsConstructor
public class KafkaHealthChecker implements ComponentHealthChecker {
private final HealthContributorRegistry registry;
public String checkHealth() {
String status;
try {
BindersHealthContributor bindersHealthContributor = (BindersHealthContributor)
registry.getContributor("binders");
KafkaBinderHealthIndicator kafkaBinderHealthIndicator = (KafkaBinderHealthIndicator)
bindersHealthContributor.getContributor("kafka");
Health health = kafkaBinderHealthIndicator.health();
status = UP.equals(health.getStatus()) ? "OK" : "FAIL";
} catch (Exception e) {
log.error("Error occurred while checking the kafka health ", e);
status = "DEGRADED";
}
return status;
}
}
I'm working on a subscription-based system developed using Asp Core 3 MVC and Sql Server. The payment is handled externally, not linked to the application in any way. All I need to do in the application is to check the user's status, that is managed by an admin. When a user registers the status will be Pending, when the admin approves the user, Approval Date will be saved in the database, and the status will be changed to Approved.
The tricky thing for me is that I want the application to wait for 365 days before it changes the user status to Expired. I've no idea from where to start this part and would appreciate your help.
The simplest way i can think of without using hosted services is to add a check on user login that subtracts the approval date from today's date and check if the difference is equal or greater than 365 days
Something like this:
if ((DateTime.Now - user.ApprovalDate).TotalDays >= 365)
{
//Mark the user as expired...
}
You really shouldn't trigger a background thread from your main application code.
The correct way to do this is with a background worker process that has been designed specifically for this scenario.
ASP.NET Core 3 has a project type that is specifically for this, and will continue to run the back ground and can be used for all of your maintenance tasks. You can create a worker process using dotnet new worker -o YourProjectName or selecting Worker Service from the project selection window in Visual Studio.
Within that service you can then create a routine that will be used to determine if the user has expired. Encapsulate this logic in a class that makes testing easy.
Working repl has been posted here.
using System;
public class MainClass {
public static void Main (string[] args) {
var user = new User(){ ApprovedDate = DateTime.Today };
Console.WriteLine (UserHelper.IsUserExpired(user));
// this should be false
user = new User(){ ApprovedDate = DateTime.Today.AddDays(-180) };
Console.WriteLine (UserHelper.IsUserExpired(user));
// this should be false
user = new User(){ ApprovedDate = DateTime.Today.AddDays(-365) };
Console.WriteLine (UserHelper.IsUserExpired(user));
// this should be true
user = new User(){ ApprovedDate = DateTime.Today.AddDays(-366) };
Console.WriteLine (UserHelper.IsUserExpired(user));
}
}
public class User {
public DateTime ApprovedDate {get; set;}
}
public static class UserHelper
{
public static bool IsUserExpired(User user){
//... add all the repective logic in here that you need, for example;
return (DateTime.Today - user.ApprovedDate.Date).TotalDays > 365;
}
}
Need to test GET that have Json as a response. I haven't found useful info in official documentation.
Feature: API_Retrieving_Platforms
As an authorized user...
#mytag
Scenario: Perform Get request
Given I am an authorized user
When I perform GET request "/api/hotels/lists/platforms",
Then I receive a JSON in response:
"""
[
{
"refId": 1,
"label": "Mobile"
},
{
"refId": 2,
"label": "Desktop"
}
]
"""
The step for retrieving Json is:
[Then(#"I receive a JSON in response:")]
public void ThenIReceiveAJSONInResponse(string JSON)
{
Assert.Equal(HttpStatusCode.OK, _responseMessage.StatusCode);
}
How to parse this?
In short: You're Cuking It Wrong.
The longer answer is that you need to write your steps differently so you remove the technical jargon from it, and focus on the business value.
Scenario: Retriving a list of platforms
Given I am an authorized user
When I retrieve a list of hotel platforms
Then I should receive the following hotel platforms:
| Platform |
| Mobile |
| Desktop |
Step: When I retrieve a list of hotel platforms
This step should make the GET request in C# code. Save the response of that GET request in the Scenario context.
Step: Then I should receive the following hotel platforms:
Makes a simple assertion, and omits technical information like the "Ref Id". The platform name is all you really care about.
A rough start to those steps would be:
using TechTalk.SpecFlow;
using TechTalk.SpecFlow.Assist;
[Binding]
public class PlatformSteps
{
private readonly ScenarioContext scenario;
/// <summary>
/// Gets or sets the hotel platforms on the scenario context
/// </summary>
private IEnumerable<SomeJsonResponseType> HotelPlatforms
{
get => (IEnumerable<SomeJsonResponseType>)scenario["HotelPlatformsResponse"];
set => scenario["HotelPlatformsResponse"] = value;
}
public PlatformSteps(ScenarioContext scenario)
{
this.scenario = scenario;
}
[When(#"^When I retrieve a list of hotel platforms")]
public void WhenIRetrieveAListOfHotelPlatforms()
{
HotelPlatforms = api.GetHotelPlatforms(); // Or whatever you API call looks like
}
[Then(#"^I should receive the following hotel platforms:")]
public void IShouldReceiveTheFollowingHotelPlatforms(Table table)
{
var actualPlatforms = HotelPlatforms.Select(r => r.PlatformName);
table.CompareToSet(actualPlatforms);
}
}
One way to improve this is by not putting the exact JSON in the Specflow step.
I'd suggest using something like
Then I receive a response that looks like 'myResponsefile.json'
You could then create step which processes the response and looks at a file in your repo to compare it with
[Then(#"I receive a response that looks like '(.*)'")]
public void IreceiveAJsonResponse(string responsefilenametocompare)
{
string receivedjson = GetMyReceivedjsonObject();
string filePathAndName = "myfile.json";
string json = File.ReadAllText(filePathAndName);
JToken expected = JToken.Parse(json);
JToken actual = JToken.Parse(receivedjson);
actual.Should().BeEquivalentTo(expected);
}
I want to retrieve some performance counters for the 'ASP.NET Applications' category from within my ASP.NET application.
My problem is, that I don't know how to determine the correct instance name for my current running ASP.NET application.
I currently use this code:
string instanceName = GetCurrentProcessInstanceName();
_perfCounters.Add(new PerformanceCounter("ASP.NET", "Application Restarts"));
_perfCounters.Add(new PerformanceCounter("ASP.NET Applications", "Pipeline Instance Count", instanceName));
_perfCounters.Add(new PerformanceCounter("ASP.NET Applications", "Requests Executing", instanceName));
_perfCounters.Add(new PerformanceCounter("ASP.NET Applications", "Requests/Sec", instanceName));
_perfCounters.Add(new PerformanceCounter("ASP.NET Applications", "Requests Failed", instanceName));
For GetCurrentProcessInstanceName I defined those helper methods from information and code snippets I found on the intertubes:
private static string GetCurrentProcessInstanceName()
{
return GetProcessInstanceName(GetCurrentProcessId());
}
private static int GetCurrentProcessId()
{
return Process.GetCurrentProcess().Id;
}
private static string GetProcessInstanceName(int pid)
{
PerformanceCounterCategory cat = new PerformanceCounterCategory("Process");
string[] instances = cat.GetInstanceNames();
foreach (string instance in instances)
{
using (PerformanceCounter cnt = new PerformanceCounter("Process", "ID Process", instance, true))
{
int val = (int) cnt.RawValue;
if (val == pid)
{
return instance;
}
}
}
return null;
}
Now the problem is, that I get the following error:
Instance 'w3wp' does not exist in the specified Category.
So obviously, the code snippets don't return the correct instance ID for the current application.
My question is: How can I determine (reliable) the correct value?
Additional information:
Since I learned that one should initially create a performance counter object and re-use this instance (vs. creating this object on every occasion you need this) I fire up a singleton when the application starts up. Out of this reason, I don't have access to a current HttpRequest, since this happens before a request hits the application.
I found the solution, and it's quite simpel:
string curentWebApplicationInstanceName = System.Web.Hosting.HostingEnvironment.ApplicationID.Replace('/', '_');
I have seen some of the questions/answers related to this topic here, however still I am not getting the suggestion which I want. So I am posting my question again here, and I would be thankful for your valuable time and answers.
I would like to create “Component, Page, SG, Publication, Folders “ via programmatically in SDL Tridion Content Manager, and later on, I would like to add programmatically created components in Page and attach CT,PT for that page, and finally would like to publish the page programmatically.
I have done these all the activities in SDL Tridion 2009 using TOM API (Interop DLL's), and I tried these activities in SDL Tridion 2011 using TOM.Net API. It was not working and later on I came to know that, TOM.Net API will not support these kinds of works and it is specifically only for Templates and Event System. And finally I came to know I have to go for Core services to do these kinds of stuffs.
My Questions:
When I create console application to create component programmatically using core service, what are the DLL’s I have to add as reference?
Earlier, I have created the exe and ran in the TCM server, the exe created all the stuffs, can I used the same approach using core services too? Will it work?
Is BC still available or Core Service replaced BC? (BC-Business Connector)
Can anyone send some code snippet to create Component/Page (complete class file will be helpful to understand better)
You will only need to reference Tridion.ContentManager.CoreService.Client.dll. You may want to reference Tridion.Common.dll to get access to some helpful classes such as TcmUri, but it is not needed.
You client program will make an explicit connection with the core service on a machine that you specify. If done properly, you can run the client both on the same machine as the Tridion Content Manager or on a different machine.
The Business Connector is still available, but has been superseded by the Core Service.
Have a look at these links:
Updating Components using the Core Service in SDL Tridion 2011
In SDL Tridion 2011, how can I process metadata on an item using the Core Service?
And the standard documentation on the topic connecting to the Core Service from .NET.
If you need more help with the code, I suggest you show us the code you've already written and explain what isn't working.
I will try to answer your questions:
You have to reference Tridion.ContentManager.CoreService.Client and add some stuff to app.config. It's described here
It will work from CM server, as well as from any other machine, provided it can access CoreService
CoreService is replacement for BC. BC is deprecated and will be dropped soon
You will get all the basic info from here.
This should be enough for you to start. If you will have specific problems - post them as a seperate questions.
From How can i use engine object in my console application
From a console application you should use the Core Service. I wrote a small example using the Core Service to search for items in the content manager.
Console.WriteLine("FullTextQuery:");
var fullTextQuery = Console.ReadLine();
if (String.IsNullOrWhiteSpace(fullTextQuery) || fullTextQuery.Equals(":q", StringComparison.OrdinalIgnoreCase))
{
break;
}
Console.WriteLine("SearchIn IdRef:");
var searchInIdRef = Console.ReadLine();
var queryData = new SearchQueryData
{
FullTextQuery = fullTextQuery,
SearchIn = new LinkToIdentifiableObjectData
{
IdRef = searchInIdRef
}
};
var results = coreServiceClient.GetSearchResults(queryData);
results.ToList().ForEach(result => Console.WriteLine("{0} ({1})", result.Title, result.Id));
Add a reference to Tridion.ContentManager.CoreService.Client to your Visual Studio Project.
Code of the Core Service Client Provider:
public interface ICoreServiceProvider
{
CoreServiceClient GetCoreServiceClient();
}
public class CoreServiceDefaultProvider : ICoreServiceProvider
{
private CoreServiceClient _client;
public CoreServiceClient GetCoreServiceClient()
{
return _client ?? (_client = new CoreServiceClient());
}
}
And the client itself:
public class CoreServiceClient : IDisposable
{
public SessionAwareCoreServiceClient ProxyClient;
private const string DefaultEndpointName = "netTcp_2011";
public CoreServiceClient(string endPointName)
{
if(string.IsNullOrWhiteSpace(endPointName))
{
throw new ArgumentNullException("endPointName", "EndPointName is not specified.");
}
ProxyClient = new SessionAwareCoreServiceClient(endPointName);
}
public CoreServiceClient() : this(DefaultEndpointName) { }
public string GetApiVersionNumber()
{
return ProxyClient.GetApiVersion();
}
public IdentifiableObjectData[] GetSearchResults(SearchQueryData filter)
{
return ProxyClient.GetSearchResults(filter);
}
public IdentifiableObjectData Read(string id)
{
return ProxyClient.Read(id, new ReadOptions());
}
public ApplicationData ReadApplicationData(string subjectId, string applicationId)
{
return ProxyClient.ReadApplicationData(subjectId, applicationId);
}
public void Dispose()
{
if (ProxyClient.State == CommunicationState.Faulted)
{
ProxyClient.Abort();
}
else
{
ProxyClient.Close();
}
}
}
When you want to perform CRUD actions through the core service you can implement the following methods in the client:
public IdentifiableObjectData CreateItem(IdentifiableObjectData data)
{
data = ProxyClient.Create(data, new ReadOptions());
return data;
}
public IdentifiableObjectData UpdateItem(IdentifiableObjectData data)
{
data = ProxyClient.Update(data, new ReadOptions());
return data;
}
public IdentifiableObjectData ReadItem(string id)
{
return ProxyClient.Read(id, new ReadOptions());
}
To construct a data object of e.g. a Component you can implement a Component Builder class that implements a create method that does this for you:
public ComponentData Create(string folderUri, string title, string content)
{
var data = new ComponentData()
{
Id = "tcm:0-0-0",
Title = title,
Content = content,
LocationInfo = new LocationInfo()
};
data.LocationInfo.OrganizationalItem = new LinkToOrganizationalItemData
{
IdRef = folderUri
};
using (CoreServiceClient client = provider.GetCoreServiceClient())
{
data = (ComponentData)client.CreateItem(data);
}
return data;
}
Hope this gets you started.