How to create new content in Alfresco from ServiceTask using Java? - alfresco

How can I create an xml file in Alfresco repository after the workflow has finished?
I created a ServiceTask but from there I have access only to DelegateExecution, ProcessEngine, and various services but NOT a NodeService.
Regards,
Mike
EDIT.
It is hard to believe that there is not a simple solution for such an elementary thing like creating a new file from ongoing workflow. (in such a case, REST API seems to be a lot better)
Firstly, in bpmn file ServiceTask needs to be created:
<serviceTask id="myServiceTask" activiti:class="com.example.myClass"></serviceTask>
Secondly, in myClass:
Map<Object, Object> registeredBeans = Context.getProcessEngineConfiguration().getBeans();
ServiceRegistry registry = (ServiceRegistry)registeredBeans.get(ActivitiConstants.SERVICE_REGISTRY_BEAN_KEY);
NodeService nodeService = registry.getNodeService();
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore");
ResultSet rs = registry.getSearchService().query(storeRef, SearchService.LANGUAGE_LUCENE, "PATH:\"/app:company_home/app:shared\"");
NodeRef companyHomeNodeRef = null;
try
{
if (rs.length() == 0)
{
throw new AlfrescoRuntimeException("Bad Lucene Search!");
}
companyHomeNodeRef = rs.getNodeRef(0);
// Create a map to contain the values of the properties of the node
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
props.put(ContentModel.PROP_NAME, "NewFile.txt");
// use the node service to create a new node
NodeRef node = nodeService.createNode(
companyHomeNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "TEST"),
ContentModel.TYPE_CONTENT,
props).getChildRef();
// Use the content service to set the content onto the newly created node
ContentWriter writer = registry.getContentService().getWriter(node, ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
writer.setEncoding("UTF-8");
writer.putContent("Message inside new content");
}
finally
{
rs.close();
}
I have to obtain NodeSerivce, then search for SharedFolder/Userhome using Lucene (shouldn't the "path" for common folders be declared somewhere?) and finally using a ContentWriter, I can put content to he Alfresco Repository.

I believe you're using Alfresco Activiti Engine and not the APS.
If you're using Alfresco Activiti then Inject the nodeService bean in the module-context.xml file and use it the delegate class.
Sample module-context.xml file.
<bean id="delegate"
parent="baseJavaDelegate"
class="com.example.mydelegate">
<property name="nodeService" ref="NodeService"/>
</bean>
Below is the sample java delegate.
public class mydelegate extends BaseJavaDelegate
{
private NodeService nodeService;
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
//rest of the code below
}
Now you got the nodeService inside your delete and you should be able to use it to create the xml file.
Like this you can inject other beans also.
Hope this helps you.

Related

Unity to DryIoC conversion ParameterOverride

We are transitioning from Xamarin.Forms to .Net MAUI but our project uses Prism.Unity.Forms. We have a lot of code that basically uses the IContainer.Resolve() passing in a collection of ParameterOverrides with some primitives but some are interfaces/objects. The T we are resolving is usually a registered View which may or may not be the correct way of doing this but it's what I'm working with and we are doing it in backend code (sometimes a service). What is the correct way of doing this Unity thing in DryIoC? Note these parameters are being set at runtime and may only be part of the parameters a constructor takes in (some may be from already registered dependencies).
Example of the scenario:
//Called from service into custom resolver method
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", IObjectWithData)
};
//Custom resolver method example
var resolverOverrides = new List<ResolverOverride>();
foreach(var parameterOverride in parameterOverrides)
{
resolverOverrides.Add(parameterOverride);
}
return _container.Resolve<T>(resolverOverrides.ToArray());
You've found out why you don't use the container outside of the resolution root. I recommend not trying to replicate this error with another container but rather fixing it - use handcoded factories:
internal class SomeFactory : IProductViewFactory
{
public SomeFactory( IService dependency )
{
_dependency = dependency ?? throw new ArgumentNullException( nameof(dependency) );
}
#region IProductViewFactory
public IProductView Create( int productID, IObjectWithData objectWithData ) => new SomeProduct( productID, objectWithData, _dependency );
#endregion
#region private
private readonly IService _dependency;
#endregion
}
See this, too:
For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.
For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.
For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.
Also, be aware of potential subtle differences in container behaviours: Unity's ResolverOverride works for the whole call to resolve, i.e. they override parameters of dependencies, too, whatever happens to match by name. This could very well be handled very differently by DryIOC.
First, I would agree with the #haukinger answer to rethink how do you pass the runtime information into the services. The most transparent and simple way in my opinion is by passing it via parameters into the consuming methods.
Second, here is a complete example in DryIoc to solve it head-on + the live code to play with.
using System;
using DryIoc;
public class Program
{
record ParameterOverride(string Name, object Value);
record Product(int productID);
public static void Main()
{
// get container somehow,
// if you don't have an access to it directly then you may resolve it from your service provider
IContainer c = new Container();
c.Register<Product>();
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", "blah"),
};
var parameterRules = Parameters.Of;
foreach (var po in parameterOverrides)
{
parameterRules = parameterRules.Details((_, x) => x.Name.Equals(po.Name) ? ServiceDetails.Of(po.Value) : null);
}
c = c.With(rules => rules.With(parameters: parameterRules));
var s = c.Resolve<Product>();
Console.WriteLine(s.productID);
}
}

How to reload apache commons configurations2 properties

can anyone guide me on how to perform a reload of an apache commons configuration2 properties. I'm unable to find any implementation of this anywhere. The apache docs are a bit too abstract. This is what I have so far but it's not working.
CombinedConfiguration cc = new CombinedConfiguration();
Parameters params = new Parameters();
File configFile = new File("config.properties");
File emsFile = new File("anotherconfig.properties");
ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration> configBuilder =
new ReloadingFileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.fileBased()
.setFile(configFile));
PeriodicReloadingTrigger reloadTrg = new PeriodicReloadingTrigger(configBuilder.getReloadingController(), null, 5, TimeUnit.SECONDS);
reloadTrg.start();
cc.addConfiguration(configBuilder.getConfiguration());
FileBasedConfigurationBuilder<FileBasedConfiguration> emsBuilder =
new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class)
.configure(params.properties()
.setFile(emsFile));
cc.addConfiguration(emsBuilder.getConfiguration());
DataSource ds = EmsDataSource.getInstance().getDatasource(this);
BasicConfigurationBuilder<DatabaseConfiguration> dbBuilder =
new BasicConfigurationBuilder<DatabaseConfiguration>(DatabaseConfiguration.class);
dbBuilder.configure(
params.database()
.setDataSource(ds)
.setTable("EMS_CONFIG")
.setKeyColumn("KEY")
.setValueColumn("VALUE")
);
cc.addConfiguration(dbBuilder.getConfiguration());
The configuration obtained from a builder is not updated automatically. You need to get the configuration from the builder every time you read it.
From Automatic Reloading of Configuration Sources:
One important point to keep in mind when using this approach to reloading is that reloads are only functional if the builder is used as central component for accessing configuration data. The configuration instance obtained from the builder will not change automagically! So if an application fetches a configuration object from the builder at startup and then uses it throughout its life time, changes on the external configuration file become never visible. The correct approach is to keep a reference to the builder centrally and obtain the configuration from there every time configuration data is needed.
use following code:
#Component
public class ApplicationProperties {
private PropertiesConfiguration configuration;
#PostConstruct
private void init() {
try {
String filePath = PropertiesConstants.PROPERTIES_FILE_PATH;
System.out.println("Loading the properties file: " + filePath);
configuration = new PropertiesConfiguration(filePath);
//Create new FileChangedReloadingStrategy to reload the properties file based on the given time interval
FileChangedReloadingStrategy fileChangedReloadingStrategy = new FileChangedReloadingStrategy();
fileChangedReloadingStrategy.setRefreshDelay(PropertiesConstants.REFRESH_DELAY);
configuration.setReloadingStrategy(fileChangedReloadingStrategy);
} catch (ConfigurationException e) {
e.printStackTrace();
}
}
public String getProperty(String key) {
return (String) configuration.getProperty(key);
}
public void setProperty(String key, Object value) {
configuration.setProperty(key, value);
}
public void save() {
try {
configuration.save();
} catch (ConfigurationException e) {
e.printStackTrace();
}
}
}

How one can be notified that a variable goes out of scope in Workflow Foundation 4?

We are using Workflow Foundation 4 to implement custom logic in our application. One particular thing is that we are using variables of a custom type that are associated with a ressource in an external system.
When such a variable is no longer in use in a workflow, I would like to dispose of the corresponding resource in the external system.
How can my custom host be notified at runtime that my variable goes out of scope and/or is disposed. Do I need my variable objects to derive from a particular class or interface ? Do I need to inject a particular extension in the workflow instance ?
One way could be to implement a custom TrackingParticipant. This can be used to watch for when an activity's state changes to a closed state. When it is closed, you can inspect the arguments to see if any are of a resource that you'd like to clean up.
It could look something like this:
public interface IResource
{
}
public class MyTrackingParticipant : TrackingParticipant
{
private readonly MyResourceManager resourceManager;
public MyTrackingParticipant(MyResourceManager resourceManager)
{
this.resourceManager = resourceManager;
}
protected override void Track(TrackingRecord record, TimeSpan timeout)
{
var activityStateRecord = record as ActivityStateRecord;
if (activityStateRecord != null && activityStateRecord.State == ActivityStates.Closed)
{
// Scan arguments to see if resources should be deallocated from resource manager.
foreach (var keyValuePair in activityStateRecord.Arguments)
{
// If the argument is of a resource type...
var resource = keyValuePair.Value as IResource;
if (resource != null)
this.resourceManager.DeallocateResource(resource);
}
}
}
}
And using the custom tracking participant is just like any other WF extension:
var resourceManager = new MyResourceManager();
var wfResourceTrackingParticipant = new MyTrackingParticipant(resourceManager);
var workflow1 = new Workflow1();
var workflowApp = new WorkflowApplication(workflow1);
workflowApp.Extensions.Add(wfResourceTrackingParticipant);

Using SDL Tridion 2011 Core Service to create Components programatically

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.

Simple file upload using Flex UI and Spring/Hibernate/BlazeDS backend?

I'm new to Flex and BlazeDS and I'm trying to implement a simple application which uses Flex on the front end and a Spring/Hibernate application on the back end, with communication between the two going over a BlazeDS channel.
I'm seeking direction as to the best and/or simplest way to approach this. I have the UI set up in such a way that the user is presented with a file chooser in which they pick the image file they want to upload. When this is chosen and submitted (as a form submission) then the server side should receive the image file data as well as some related metadata such as a description and date, then populate a Hibernate entity/POJO with the image file data and related metadata, and then persist the entity/POJO into the database.
I have found some examples of how you would do a file upload and download using servlets here and the FileReference class (here and here) but these don't appear to address the problem in a way which leverages BlazeDS and/or Spring/Hibernate. I want to put the image file data and related metadata (description, capture date, etc.) into a value object within the Flex application and then send this over BlazeDS to a service provided by my Spring/Hibernate application running on Tomcat. In this service I want to extract the image data (both the actual JPG/PNG/GIF data and the related metadata such as description, etc.) from the value object sent from the Flex app into an entity/POJO which is then persisted via Hibernate in my database.
Can this be done, and if so what's the best way to go about it? Am I mistaken in assuming that if I use BlazeDS then I am somehow bypassing the need to provide HTTP-based services such as servlets on the server side and instead I can use my Java services as "RemoteObjects"? Is there necessarily a one-to-one mapping between Java POJO/entity class and the Flex value object class when making this sort of transfer? If so is there a tool which creates corresponding Flex value objects from Java POJOs or vice versa.
Thanks in advance for your help, comments, suggestions, etc.
--James
Update: Some code to make this more clear:
I have this as my value object in Flex:
package valueobjects
{
import flash.utils.ByteArray;
[Bindable]
[RemoteClass(alias="com.abc.example.persistence.entity.Image")]
public class Image
{
public var id:Number;
public var captureDate:Date;
public var description:String;
public var imageData:ByteArray;
public function Image() {}
}
I am assuming that this can be used as a one-to-one mapping to the POJO class used by my service and DAO classes on the server-side, which looks like this:
package com.abc.example.persistence.entity;
import java.sql.Blob;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
#Entity(name = "IMAGE")
public class Image
extends AbstractBaseEntity<Long>
{
private String description;
private Date captureDate;
private Blob imageData;
#Column(name = "CAPTURE_DATE", nullable = true)
public Date getCaptureDate()
{
return captureDate;
}
#Column(name = "DESCRIPTION", nullable = true)
public String getDescription()
{
return description;
}
#Column(name = "IMAGE_DATA", nullable = true)
public Blob getImageData()
{
return imageData;
}
public void setCaptureDate(final Date captureDate)
{
this.captureDate = captureDate;
}
public void setDescription(final String description)
{
this.description = description;
}
public void setImageData(final Blob imageData)
{
this.imageData = imageData;
}
}
In my Flex application I populate the fields of an Image object with a description string, date, and image file data (based on the user's file selection and text input for the description) and then call a method on the RemoteObject which is mapped to the service running on Tomcat. I make the RemoteObject service call within my Flex code using the Image value object as the argument, but the service method running on the servier side actually expects an argument of the POJO/entity type, and it's here that I am thinking that some sort of conversion/transformation between the Flex value object and the Java POJO will occur (by virtue of the RemoteClass alias setting on the value object's class declaration), but it doesn't seem to be happening that way because when I debug the application the Java service is only getting null values when the service call is made.
In my Flex application I have a FileReference and Image value object as public, bindable variables:
[Bindable]
public var imageToBeArchivedFileReference:FileReference = new FileReference();
[Bindable]
public var imageToBeArchivedValueObject:valueobjects.Image = new valueobjects.Image();
There is also an event handler to browse for a file when the user clicks on a file select button:
protected function imageFileSelectButton_clickHandler(event:MouseEvent):void
{
var imageFileFilter:FileFilter = new FileFilter("Images (*.jpg, *.jpeg, *.gif, *.png)", "*.jpg;*.jpeg;*.gif;*.png");
var fileTypes:Array = new Array();
fileTypes.push(imageFileFilter);
imageToBeArchivedFileReference.addEventListener(Event.SELECT, imageToBeArchived_fileSelectHandler);
imageToBeArchivedFileReference.browse(fileTypes);
}
There is an event handler which builds the value object when the image file has been selected:
private function imageToBeArchived_fileSelectHandler(event:Event):void
{
imageToBeArchivedFileReference.load();
imageToBeArchivedValueObject = new valueobjects.Image()
imageToBeArchivedValueObject.imageData = imageToBeArchivedFileReference.data;
imageToBeArchivedValueObject.description = imageToBeArchivedDescription.text;
imageToBeArchivedValueObject.captureDate = imageToBeArchivedFileReference.creationDate;
}
and there's an event handler which is invoked when the user clicks on the submit button to perform the image save/upload:
protected function archiveImageButton_clickHandler(event:MouseEvent):void
{
imageArchivalService.archiveImage(imageToBeArchived);
}
On the server side my Java class is doing a simple save of the POJO:
public void archiveImage(final Image image)
{
imageDao.saveOrUpdate(image);
}
When I set a breakpoint in the method above and look at the image variable it looks to be empty, so I'm assuming that the transformation from the Flex value object to the Java POJO did not go as expected and that there's more to it than just adding a RemoteClass alias in the Flex value object class.
Check out this example, it is all there.
http://biemond.blogspot.com/2008/08/flex-upload-and-download-with-blazeds.html
Don't use the loader class, use the readBytes call.
Make sure you go to the comments, there are valuable info there.
Cheers

Resources