We are setting up a flowable application to use multiple bpmns with reusable java delegates. In application.yml, we have properties set for each bpmn
e.g.
app1.mail.active: true
app2.mail.active: false
ApplicationProperties file is set with #ConfigurationProperties() to get the properties.
Created a Spring bean:
#Bean("applicationProperties")
public ApplicationProperties applicationProperties(){
return new ApplicationProperties();
}
I am trying to use a script task using groovy and initialize the appropriate properties. It seems Spring beans are not available inside the script.
<scriptTask id="sid-C8B8BE3F-F6CB-4559-B48C-5BC14AB76494" name="Initialize Process" scriptFormat="groovy">
<script><![CDATA[
// I want to be able to access applicationProperties bean here
def _properties = applicationProperties.getApp1()
execution.setVariable("properties", _properties)
]]></script>
</scriptTask>
When initializing bpmn for app1, I want to set a variable for properties related to app1 and similar to app2. I am able to use existing spring beans elsewhere in the same bpmn but not inside groovy script task.
Thank you in advance.
The solution that I came up was to create a service task with java delegate to initialize application properties and common variables based on the bpmn instance and set a properties variable, then as part of the same service task I added an executionListener to set variables that are specific to that instance, using groovy script.
<serviceTask id="initProcess" name="Init Process" flowable:delegateExpression="${initProcessDelegate}">
<extensionElements>
<flowable:executionListener event="end" class="org.flowable.engine.impl.bpmn.listener.ScriptExecutionListener">
<flowable:field name="script">
<flowable:string><![CDATA[
import config.ApplicationProperties;
ApplicationProperties.Props props = (ApplicationProperties.Props) execution.getVariable("properties");
System.out.println("properties " + properties.getUrl());
]]></flowable:string>
</flowable:field>
<flowable:field name="language">
<flowable:string><![CDATA[groovy]]></flowable:string>
</flowable:field>
</flowable:executionListener>
</extensionElements>
</serviceTask>
public class ProcessInitDelegate implements JavaDelegate {
Logger log = LoggerFactory.getLogger(getClass());
#Autowired
ApplicationProperties applicationProperties;
public void execute(DelegateExecution execution) {
log.info("<--------------- ProcessInitDelegate ------------->");
try {
// set up properties for current instance
String instance = (String) execution.getVariable("processInstance");
Object properties = applicationProperties.getInnerInstance(instance);
if (instance.equals("<your_instance_name>")) {
ApplicationProperties.Props instanceProperties = (ApplicationProperties.Props) properties;
execution.setVariable("properties, instanceProperties);
}
} catch (Exception e) {
log.error("Error setting up process {} ", e.getMessage());
}
}
}
You can swop over to SecureJavaScript and add the bean to the trusted beans in your custom config.
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-secure-javascript</artifactId>
<version>6.6.0</version>
</dependency>
#Bean
public SecureJavascriptConfigurator secureJavascriptConfigurator() {
SecureJavascriptConfigurator configurator =
new SecureJavascriptConfigurator()
.setMaxStackDepth(10)
.setMaxScriptExecutionTime(3000L)
.setMaxMemoryUsed(3145728L)
.setNrOfInstructionsBeforeStateCheckCallback(10)
.setEnableAccessToBeans(true);
return configurator;
}
#Bean
#ConditionalOnClass(SpringProcessEngineConfiguration.class)
public EngineConfigurationConfigurer<SpringProcessEngineConfiguration> customProcessEngineConfigurer() {
return configuration -> {
configuration.addConfigurator(secureJavascriptConfigurator());
Map<Object, Object> beans = new HashMap<>();
beans.put("applicationProperties", applicationProperties);
configuration.setBeans(beans);
};
}
Related
How do we customize the Jackson ObjectMapper used by WebFlux OutboundGateway? The normal customization done via Jackson2ObjectMapperBuilder or Jackson2ObjectMapperBuilderCustomizer is NOT respected.
Without this customization, LocalDate is serialized as SerializationFeature.WRITE_DATES_AS_TIMESTAMPS. Sample output - [2022-10-20] and there is NO way to customize the format
I assume you really talk about Spring Boot auto-configuration which is applied to the WebFlux instance. Consider to use an overloaded WebFlux.outboundGateway(String uri, WebClient webClient) to be able to auto-wire a WebClient.Builder which might be already configured with the mentioned customized ObjectMapper.
Registering a bean of type com.fasterxml.jackson.databind.module.SimpleModule will automatically be used by the pre-configured ObjectMapper bean. In SimpleModule, it is possible to register custom serialization and deserialization specifications.
To put that into code, a very simple solution would be the following:
#Bean
public SimpleModule odtModule() {
SimpleModule module = new SimpleModule();
JsonSerializer<LocalDate> serializer = new JsonSerializer<>() {
#Override
public void serialize(LocalDate odt, JsonGenerator jgen, SerializerProvider provider) throws IOException {
String formatted = odt.format(DateTimeFormatter.ISO_LOCAL_DATE);
jgen.writeString(formatted);
}
};
JsonDeserializer<LocalDate> deserializer = new JsonDeserializer<>() {
#Override
public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
return LocalDate.parse(jsonParser.getValueAsString());
}
};
module.addSerializer(LocalDate.class, serializer);
module.addDeserializer(LocalDate.class, deserializer);
return module;
}
Note that using lambdas for the implementations has sometimes resulted in weird behaviors for me, so I tend not to do that.
Am trying to start activiti engine from jsf
#ManagedBean(name = "activitiProcess")
#ViewScoped
public class ActivitiProcess implements Serializable {
private String filename = "D:/WORKSPACE/activiti1/src/main/resources/diagrams/MyProcess.bpmn";
public ActivitiProcess() {
}
public void startProcess() {
System.out.println("hello world");
try {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine(); //returns null
RepositoryService repositoryService = engine.getRepositoryService();
repositoryService.createDeployment().addInputStream("myProcess.bpmn20.xml", new FileInputStream(filename)).deploy();
RuntimeService runtimeService = engine.getRuntimeService();
ProcessDefinition definition = engine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey("myProcess").singleResult();
FormService formService = engine.getFormService();
// List<FormProperty> formList = formService.getStartFormData(definition.getId()).getFormProperties();
Map<String, String> formProperties = new HashMap<String, String>();
/* use the jsf variable instead */
formProperties.put("filePath", "D://somePath");
formService.submitStartFormData(definition.getId(), formProperties);
} catch (Exception e) {
e.printStackTrace();
}
}
}
the engine always returns as null , I read that the activiti.cfg.xml file should be in the class path , I've put the file under web-inf/classes but still , engine returns null which means activiti can't initialize the engine , any ideas ?
add this
#Inject
private ProcessEngine engine;
if you want to run a CDI Bean inside a container, you are not allowed to use the new keyword. Instead, you need to inject the bean and the container does the
rest, meaning, the container is the one responsible for managing the life cycle of the bean: it creates the instance; it gets rid of it. So how do you initialize a bean if you can’t call a constructor? Well, the container gives you a handle after constructing an instance and before destroying it.
I am using Spring 3.1 with annotations to create a DataSource that uses embedded HSQL.
#Bean
public DataSource dataSource() throws Exception {
EmbeddedDatabaseFactoryBean bean = new EmbeddedDatabaseFactoryBean();
bean.setDatabaseType(EmbeddedDatabaseType.HSQL);
bean.afterPropertiesSet();
DataSource object = bean.getObject();
return object;
}
I am also configuring a SessionFactory like this
#Bean
public SessionFactory sessionFactory() {
SessionFactory sessionFactory = new LocalSessionFactoryBuilder(dataSource)
.setNamingStrategy(namingStrategy())
.addProperties(hibernateProperties)
.addAnnotatedClass(Some.class)
.buildSessionFactory();
logger.info("Created session factory: " + sessionFactory + " with dataSource: " + dataSource);
return sessionFactory;
}
The problem is if I create some other beans using #Component in which I populate the database, the SQL script fails because the database has not been created. My hibernate.properties includes the following line to generate DDL
properties.put("hibernate.hbm2ddl.auto", "create-drop");
So it is some sort of ordering issue with bean creating. However this problem only occurs in Linux (Kubuntu 12.04) and not on Windows 7!
I have worked out that in my #Component bean which populates the database I had to add the #DependsOn annotation like this
#Component
#DependsOn({"dataSource", "sessionFactory"})
public class DevSqlPopulator {
...
}
I think the issue is that you are calling a InitializingBean method afterPropertiesSet method yourself instead of letting Spring call it once all the properties are cleanly set. Try doing this instead:
#Bean
public EmbeddedDatabaseFactoryBean dataSource() throws Exception {
EmbeddedDatabaseFactoryBean bean = new EmbeddedDatabaseFactoryBean();
bean.setDatabaseType(EmbeddedDatabaseType.HSQL);
return bean;
}
Now it is a clean factory bean and Spring will take care of the rest of the lifecycle.
I have a service that needs to invoke a runnable class.
Here are the lines of code that are being used in my service.
#Autowired
private LinkBrc2MemberProfile brcTask;
// Background Task.
SimpleAsyncTaskExecutor sate = new SimpleAsyncTaskExecutor();
sate.createThread(new LinkBrc2MemberProfile(user));
Here is my Runnable class
#Service
public class LinkBrc2MemberProfile implements Runnable {
private final Logger log = LoggerFactory.getLogger(LinkBrc2MemberProfile.class);
#Autowired
private LoyaltyDao dao;
private Member member;
public LinkBrc2MemberProfile() {
super();
}
public LinkBrc2MemberProfile(Member member) {
this.member = member;
}
public void run() {
log.debug("*** Member User Name: " + member.getString("USER_NAME"));
String emailAddress = member.getString("USER_NAME");
Map<String, Object> map = dao.findBrcByEmailAddress( emailAddress );
log.debug("==========================================================");
if( ! map.isEmpty() ) {
try {
//a.CUSTOMER_ID, a.EMAIL_ADDRESS, b.card_no
String customerId = (String) map.get("CUSTOMER_ID");
String brcCardNumber = (String) map.get("CARD_NO");
log.debug("\ncustomerId: " + customerId + " brcCardNumber: " + brcCardNumber);
if(!brcCardNumber.equals("")) {
// Add the Be Rewarded Card.
HashMap<String, String> userAttributes = new HashMap<String, String>();
String brcNumber = member.getString("BREWARDED_CARD_NO");
if (brcNumber.equals("")) {
userAttributes.put("BREWARDED_CARD_NO", brcCardNumber);
try {
member.putAll(userAttributes);
} catch (Exception e) {
String errorMessage = "Unable to save user's BRC information due to: " + e.getMessage();
log.error("{}", errorMessage);
}
}
}
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
}
}
}
I'm not seeing any errors in the log but at the same time it does not appear to be invoking the Runnable class. Am I missing an annotation somewhere? Are there any good examples that you can point me to, the only ones I have found use XML files to configure the runnable class I would like to use annotations. Thanks in Advance.
I've updated my service to do the following.
Please help, my DAO is NULL so it looks like my #Autowired in my Runnable class is not wiring it in.
I've added the following bean to my bean-config.xml file.
<bean id="brcType" class="com.ws.ocp.service.LinkBrc2MemberProfile" scope="prototype"/>
I removed my #Autowired annotation and added the following to my service class.
ClassPathResource rsrc = new ClassPathResource("bean-config.xml");
XmlBeanFactory factory = new XmlBeanFactory(rsrc);
LinkBrc2MemberProfile brcTask = (LinkBrc2MemberProfile) factory.getBean("brcType");
SimpleAsyncTaskExecutor sate = new SimpleAsyncTaskExecutor();
// Set Member attribute
brcTask.setMember(user);
// Executer
sate.execute(brcTask);
Why is my dao still null?
The runnable will throw a NullPointerException, since you create it yourself (using the new operator), instead of letting Spring create it. This obviously means that the autowired DAO attribute won't be autowired, which will lead to a NPE when calling dao.findBrcByEmailAddress(...).
You should get your Runnable instance from the bean factory (as a prototype), set its member attribute, and then submit it to the executor.
To answer your question of how to properly use a Prototype-Bean, this is my favorite way:
#Component
abstract class MyBean {
/* Factory method that will be installed by Spring */
#Lookup
protected abstract YourPrototypeBean createBean();
void someCode() {
YourPrototypeBean bean = createBean();
}
}
Since it's a factory method, you can create as many instances as you like.
How can I access a jsf managed bean (say, icefaces) from a blazeds client?
Will it be possible to share the same session information also ? (for eg, if I have a page with a jsf/icefaces component and a swf client - can they use the same session?)
first of all you have to implement the FlexFactory in your own Factory:
http://livedocs.adobe.com/blazeds/1/blazeds_devguide/factory_2.html
I change the SpringFactory a little bit so you can access to the current instance of the given bean:
public class BeanFactory implements FlexFactory {
private static final String SOURCE = "source";
public void initialize(String id, ConfigMap configMap) {}
public FactoryInstance createFactoryInstance(String id, ConfigMap properties) {
BeanFactoryInstance instance = new BeanFactoryInstance(this, id, properties);
instance.setSource(properties.getPropertyAsString(SOURCE, instance.getId()));
return instance;
}
public Object lookup(FactoryInstance inst) {
BeanFactoryInstance factoryInstance = (BeanFactoryInstance) inst;
return factoryInstance.lookup();
}
static class BeanFactoryInstance extends FactoryInstance {
BeanFactoryInstance(BeanFactory factory, String id, ConfigMap properties) {
super(factory, id, properties);
}
public String toString() {
return "BeanFactory instance for id=" + getId() + " source=" + getSource() + " scope=" + getScope();
}
public Object lookup() {
HttpServletRequest hsr = FlexContext.getHttpRequest();
String beanName = getSource();
try
{
Object o = hsr.getSession().getAttribute(beanName);
return o;
}
catch (Exception e)
{
ServiceException se = new ServiceException();
String msg = "Java Bean '" + beanName + "' does not exist.";
se.setMessage(msg);
se.setRootCause(e);
se.setDetails(msg);
se.setCode("Server.Processing");
throw se;
}
}
}}
in the service-config.xml (WEB-INF/flex folder) you have to register this factory:
<factories>
<factory id="beanFactory" class="packageName.BeanFactory"/>
</factories>
then you have to register the factory in your destination in the remoting-config.xml like this:
<destination id="remoteService">
<properties>
<factory>beanFactory</factory>
<source>beanName</source>
<scope>session</scope>
</properties>
</destination>
so what is this BeanFactory doing:
when you want to access per remote from flex to java or to the jee-application, you declare a remoteobject in flex with the destination "remoteService" which is configured in the remoting-config.xml. the moment you access from flex to java by calling a server-sided-method, the beanfactory looks after the current instance of the bean you declare in the remoting-config.xml by getting the Request over the FlexContext:
HttpServletRequest hsr = FlexContext.getHttpRequest();
now you get the session and with the beanName the instance by callinghsr.getSession().getAttribute(beanName)
this is only working with application and session beans and only if jsf instantiated the bean before the BeanFactory want to access the bean...
when you want to integrate a swf-file with icefaces you should take the ice:outputMedia-Tag an set the player-attribute to "flash"
if you work with eclipse to develop your jee-application and you integrate the tomcat in eclipse you can set the server root folder in the properties of your flex-project to the tomcat folder (flex builder):
(Sorry not time for making this looks good ;) )
now you can start the tomcat server directly in eclipse and you can debug on flex and java too :)
hope this helps!