Situation is like this I define a Artifact Type like this:
<artifactType type="application/vnd.wso2-tets+xml" shortName="test" singularLabel="tests" pluralLabel="tests" hasNamespace="false" iconSet="9">
<storagePath>/applications/#{name}/#{overview_version}</storagePath>
<nameAttribute>overview_name</nameAttribute>
<ui>
<list>
<column name="Name">
<data type="path" value="overview_name" href="/applications/#{name}"/>
</column>
<column name="Version">
<data type="path" value="overview_version" href="#{storagePath}"/>
</column>
</list>
</ui>
<content>
<table name="Overview">
<field type="text" required="true">
<name>Name</name>
</field>
<field type="text" required="true">
<name>Version</name>
</field>
<field type="text-area">
<name>Description</name>
</field>
<field type="options">
<name label="Zcos">Zcos</name>
<values class="cn.oge.wso2.populator.AlgPopulator"/>
</field>
</table>
</content>
</artifactType>
At the same time I also defines a handler,the mediaType is "application/vnd.wso2-tets+xml" the handler code like this:
public class XcosMediaTypeHandler extends Handler {
public Resource get(RequestContext requestContext) throws RegistryException {
return null;
}
public void put(RequestContext requestContext) throws RegistryException {
Resource resource = requestContext.getResource();
String name = "";
String version = "";
String description = "";
String zcos = "";
byte[] content = (byte[]) resource.getContent();
ByteArrayInputStream in = new ByteArrayInputStream(content);
OMElement docElement = null;
try {
StAXOMBuilder builder = new StAXOMBuilder(in);
docElement = builder.getDocumentElement();
} catch (Exception ae) {
throw new RegistryException(
"Failed to parse the propject proposal. "
+ "All project proposals should be in XML format.");
}
System.out.println("==========================================");
OMElement firstElement = docElement.getFirstElement();
System.out.println(firstElement);
Iterator<OMElement> ite = firstElement.getChildElements();
for (OMElement e = ite.next(); ite.hasNext(); e = ite.next()) {
if (e.getLocalName().equals("name")) {
name = e.getText();
} else if (e.getLocalName().equals("version")) {
version = e.getText();
} else if (e.getLocalName().equals("description")) {
description = e.getText();
} else if (e.getLocalName().equals("zcos")) {
zcos = e.getText();
}
}
System.out.println("Name:" + name);
System.out.println("Version:" + version);
System.out.println("Description:" + description);
System.out.println("zcos:" + zcos);
}
public void importResource(RequestContext requestContext)
throws RegistryException {
System.out.println("importResource");
}
public void delete(RequestContext requestContext) throws RegistryException {
}
public void putChild(RequestContext requestContext)
throws RegistryException {
System.out.println("putChild");
}
public void importChild(RequestContext requestContext)
throws RegistryException {
System.out.println("importChild");
}
}
The question is coming , When I use UI to add a Artifact ,For example:
In the above code , I can get attribute value for Name,Version and Description. but I can't get attribute value for Zcos. In the define of Artifact the Zos of the filed type is options ,The other attributes of the filed type is text or text-area.
Why I can't get Zcos property value?Thanks in advance!
for less confusion make the filed reference lowercase like below,
<field type="options">
<name label="Zcos">zcos</name>
<values class="cn.oge.wso2.populator.AlgPopulator"/>
</field>
find below code to get zcos value,
// Get the first OMElement child with name 'overview'
OMElement elementOverview = getFirstChild(docElement, "overview");
// Get the first OMElement child with name 'zcos' and appending absolute path prefix.
String zcos = getFirstChild(elementOverview, "zcos").getText();
To find a sample code please find this blog post.
Related
I am using spring cloud GCP logging and trace in my GCP cloud run application
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<include resource="org/springframework/cloud/gcp/logging/logback-appender.xml"/>
<root level="INFO">
<appender-ref ref="STACKDRIVER"/>
</root>
</configuration>
After adding this I can see that logs are grouped
Now I have two questions
Can we have the trace ID and and span ID printed in the request log it self.
After adding stack driver, the spring default console pattern is not shown. All the logs are printed in raw format. They do not have thread-name, span id, appName etc.
Can these two be achieved?
Update
Trying to enhance the log
#Component
public class NukTraceIdLoggingEnhancer extends TraceIdLoggingEnhancer {
#Override
public void enhanceLogEntry(LogEntry.Builder builder) {
super.enhanceLogEntry(builder);
LogEntry.Builder newDummyBuilder = builder;
LogEntry logEntry = newDummyBuilder.build();
Payload<?> payload = logEntry.getPayload();
if (logEntry.getPayload() instanceof JsonPayload) {
JsonPayload jsonPayload = (JsonPayload) payload;
String content = (String) ((JsonPayload) payload).getDataAsMap().get("message");
content = "abc" + logEntry.getSpanId() + ", " + logEntry.getTrace() + content;
jsonPayload.getDataAsMap().replace("message", content);
builder.setPayload(jsonPayload);
}
}
#Override
public void enhanceLogEntry(LogEntry.Builder builder, ILoggingEvent e) {
super.enhanceLogEntry(builder, e);
LogEntry.Builder newDummyBuilder = builder;
LogEntry logEntry = newDummyBuilder.build();
Payload<?> payload = logEntry.getPayload();
if (logEntry.getPayload() instanceof JsonPayload) {
JsonPayload jsonPayload = (JsonPayload) payload;
String content = (String) ((JsonPayload) payload).getDataAsMap().get("message");
content = "XYZ" + logEntry.getSpanId() + ", " + logEntry.getTrace() + content;
jsonPayload.getDataAsMap().replace("message", content);
builder.setPayload(jsonPayload);
}
}
}
logback-spring.xml
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<property name="STACKDRIVER_LOG_NAME" value="${STACKDRIVER_LOG_NAME:-spring.log}"/>
<property name="STACKDRIVER_LOG_FLUSH_LEVEL" value="${STACKDRIVER_LOG_FLUSH_LEVEL:-WARN}"/>
<appender name="MY_STACKDRIVER" class="com.google.cloud.spring.logging.LoggingAppender">
<log>${STACKDRIVER_LOG_NAME}</log> <!-- Optional : default spring.log -->
<loggingEventEnhancer>abc.configuration.NukTraceIdLoggingEnhancer</loggingEventEnhancer>
<flushLevel>INFO</flushLevel> <!-- Optional : default ERROR -->
</appender>
<root level="INFO">
<appender-ref ref="MY_STACKDRIVER"/>
</root>
</configuration>
When I run a Unit Test for my plugin I get the following Exception being Thrown:
Message: Test method Plugins.Tests.UnitTest1.TestUnitPlugin threw exception:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
The following link will give you the stack trace:
Stacktrace
Even when I deploy & register my plugin to the online instance, I would get the same message!!
My Plugin code looks like this:
using System;
using System.Collections.Generic;
using System.ServiceModel;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
/// <summary>
/// This plugin takes the data provided in the contract lines and makes Unit Orders.. Inside the unit orders, an Alter Unit Orders table is present.
/// The Alter Unit Orders table describes the daily order for each day in the contract's duration.
/// </summary>
namespace DCWIMS.Plugins
{
[CrmPluginRegistration(MessageNameEnum.Update,
"contract",
StageEnum.PreOperation,
ExecutionModeEnum.Synchronous,
"title",
"Post-Update Contract",
1000,
IsolationModeEnum.Sandbox,
Image1Name = "PreImage",
Image1Type = ImageTypeEnum.PreImage,
Image1Attributes = "title")]
public class UnitPlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Extract the tracing service for use in debugging sandboxed plug-ins.
// Wil be registering this plugin, thus will need to add tracing service related code.
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
//obtain execution context from service provider.
IPluginExecutionContext context = (IPluginExecutionContext)
serviceProvider.GetService(typeof(IPluginExecutionContext));
// The InputParameters colletion contains all the data passed in the message request.
if (context.InputParameters.Contains("Target") &&
context.InputParameters["Target"] is Entity)
{
//obtain the target entity from the input parameters.
Entity entity = (Entity)context.InputParameters["Target"];
//Obtain Target Entity Id
var targetId = entity.Id.ToString();
//verify that the target entity represents the the contract entity and is active
if (entity.LogicalName != "contract" && entity.GetAttributeValue<OptionSetValue>("statecode").Value != 0)
return;
//obtain the organization service for web service calls.
IOrganizationServiceFactory serviceFactory =
(IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
try
{
//Get Contract StartDate
DateTime startDate = (DateTime)entity["activeon"];
//Get Contract EndDate
DateTime endDate = (DateTime)entity["expireson"];
//Get all weekdays in the contract duration
Eachday range = new Eachday();
var weekdays = range.WeekDay(startDate, endDate); //weekdays list
//Get Contract Number
string contractNumber = (string)entity["contractnumber"];
//Query and aggregate each Weekday's order for the 3 different meal times...
//AM SNACK
string unitsum_am = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_amsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_amsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_amsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_amsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_amsum' aggregate='sum' />
<filter type='and' >
<condition value='100000001' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_am_result =
service.RetrieveMultiple(new FetchExpression(unitsum_am));
var am_list = new List<int>();
foreach(var unit in unitsum_am_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_amsum"]).Value);
am_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_amsum"]).Value);
am_list.Add(tuesdaysum);
var wednesdaysum= ((int)((AliasedValue)unit["new_unitswednesday_amsum"]).Value);
am_list.Add(wednesdaysum);
var thursdaysum= ((int)((AliasedValue)unit["new_unitsthursday_amsum"]).Value);
am_list.Add(thursdaysum);
var fridaysum= ((int)((AliasedValue)unit["new_unitsfriday_amsum"]).Value);
am_list.Add(fridaysum);
}
//LUNCH
string unitsum_lunch = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_lunchsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_lunchsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_lunchsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_lunchsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_lunchsum' aggregate='sum' />
<filter type='and' >
<condition value='100000002' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_lunch_result =
service.RetrieveMultiple(new FetchExpression(unitsum_lunch));
var lunch_list = new List<int>();
foreach (var unit in unitsum_lunch_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_lunchsum"]).Value);
lunch_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_lunchsum"]).Value);
lunch_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_lunchsum"]).Value);
lunch_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_lunchsum"]).Value);
lunch_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_lunchsum"]).Value);
lunch_list.Add(fridaysum);
}
//PM SNACK
string unitsum_pm = #" <fetch aggregate='true' distinct='false' >
<entity name='contract' >
<link-entity name='contractdetail' from = 'contractid' to = 'contractid' >
<attribute name='new_mondayunits' alias='new_mondayunits_pmsum' aggregate='sum' />
<attribute name='new_tuesdayunits' alias='new_tuesdayunits_pmsum' aggregate='sum' />
<attribute name='new_unitswednesday' alias='new_unitswednesday_pmsum' aggregate='sum' />
<attribute name='new_unitsthursday' alias='new_unitsthursday_pmsum' aggregate='sum' />
<attribute name='new_unitsfriday' alias='new_unitsfriday_pmsum' aggregate='sum' />
<filter type='and' >
<condition value='100000003' attribute='new_servingtime' operator= 'eq' />
<condition value='0' attribute='statecode' operator= 'eq' />
<condition value='" + targetId + #"' attribute='contractid' operator= 'eq' />
</filter >
</link-entity>
</entity >
</fetch>";
EntityCollection unitsum_pm_result =
service.RetrieveMultiple(new FetchExpression(unitsum_pm));
var pm_list = new List<int>();
foreach (var unit in unitsum_pm_result.Entities)
{
var mondaysum = ((int)((AliasedValue)unit["new_mondayunits_pmsum"]).Value);
pm_list.Add(mondaysum);
var tuesdaysum = ((int)((AliasedValue)unit["new_tuesdayunits_pmsum"]).Value);
pm_list.Add(tuesdaysum);
var wednesdaysum = ((int)((AliasedValue)unit["new_unitswednesday_pmsum"]).Value);
pm_list.Add(wednesdaysum);
var thursdaysum = ((int)((AliasedValue)unit["new_unitsthursday_pmsum"]).Value);
pm_list.Add(thursdaysum);
var fridaysum = ((int)((AliasedValue)unit["new_unitsfriday_pmsum"]).Value);
pm_list.Add(fridaysum);
}
foreach(var day in weekdays)
{
var alterunit = new Entity("new_alterunitorder");
alterunit.Attributes.Add("new_orderdate", DateTime.Parse(day));
switch (day.Split(',')[0])
{
case "Monday":
alterunit.Attributes.Add("new_amsnack", am_list[0]);
alterunit.Attributes.Add("new_lunch", lunch_list[0]);
alterunit.Attributes.Add("new_pmsnack", pm_list[0]);
break;
case "Tuesday":
alterunit.Attributes.Add("new_amsnack", am_list[1]);
alterunit.Attributes.Add("new_lunch", lunch_list[1]);
alterunit.Attributes.Add("new_pmsnack", pm_list[1]);
break;
case "Wednesday":
alterunit.Attributes.Add("new_amsnack", am_list[2]);
alterunit.Attributes.Add("new_lunch", lunch_list[2]);
alterunit.Attributes.Add("new_pmsnack", pm_list[2]);
break;
case "Thursday":
alterunit.Attributes.Add("new_amsnack", am_list[3]);
alterunit.Attributes.Add("new_lunch", lunch_list[3]);
alterunit.Attributes.Add("new_pmsnack", pm_list[3]);
break;
case "Friday":
alterunit.Attributes.Add("new_amsnack", am_list[4]);
alterunit.Attributes.Add("new_lunch", lunch_list[4]);
alterunit.Attributes.Add("new_pmsnack", pm_list[4]);
break;
default:
Console.WriteLine($"An unexpected value ({day.Split(',')})");
break;
}
alterunit.Attributes.Add("new_name", contractNumber); //set the record name
//set the unit order record relation
alterunit.Attributes["new_orderlineid"] =
new EntityReference("new_alterunitorder", unitOrderId);
service.Create(alterunit);
}
}
catch (FaultException<OrganizationServiceFault> ex)
{
throw new InvalidPluginExecutionException("An error occured.. Phil is responsible.", ex);
}
catch (Exception ex)
{
tracing.Trace("An Error Occured: {0}", ex.ToString());
throw;
}
}
}
}
}
Here is the code for Eachday:
using System;
using System.Collections.Generic;
namespace DCWIMS.Plugins
{
public class Eachday
{
public List<string> WeekDay(DateTime from, DateTime thru)
{
List<string> days_list = new List<string>();
for (var day = from.Date; day.Date <= thru.Date; day = day.AddDays(1))
{
days_list.Add(day.ToLongDateString());
if (day.DayOfWeek == DayOfWeek.Sunday || day.DayOfWeek == DayOfWeek.Saturday)
days_list.Remove(day.ToLongDateString());
}
return days_list;
}
}
}
My unit test looks like this:
using DCWIMS.Plugins;
using Microsoft.Crm.Sdk.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Xrm.Sdk;
namespace Plugins.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
[TestCategory("Unit Test")]
public void TestUnitPlugin()
{
using (var pipline = new PluginPipeline(FakeMessageNames.Update, FakeStages.PreOperation, new Entity("contract")))
{
var plugin = new UnitPlugin();
pipline.Execute(plugin);
}
}
}
}
Even when I registered the plugin on the actual CRM instance I got this error message:
Here is the log file retrieved from CRM Online!
Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.Detail:
<OrganizationServiceFault xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ActivityId>4025e0f8-eed5-4b7f-a3b1-52a9f2a6f2cc</ActivityId>
<ErrorCode>-2147220956</ErrorCode>
<ErrorDetails xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
<Message>Unexpected exception from plug-in (Execute): DCWIMS.Plugins.UnitPlugin: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.</Message>
<Timestamp>2018-07-20T17:47:36.4061434Z</Timestamp>
<ExceptionRetriable>false</ExceptionRetriable>
<ExceptionSource i:nil="true" />
<InnerFault i:nil="true" />
<OriginalException i:nil="true" />
<TraceText>
[Plugins: DCWIMS.Plugins.UnitPlugin]
[dbb33fa1-448c-e811-815c-480fcff4b5b1: Pre-Create Contract]
An Error Occured: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at Microsoft.Xrm.Sdk.AttributeCollection.get_Item(String attributeName)
at DCWIMS.Plugins.UnitPlugin.Execute(IServiceProvider serviceProvider)
</TraceText>
</OrganizationServiceFault
>
Unit testing ain't my forte. But I will try to guide you.
First, my opinion on unit testing. I don't value it much in Dynamics because it's an environment that is hard to control. You can test that your business logic is well implemented in the plugin but a javascript, a business rule, a workflow or an action can derail your logic. Your plugin can say blue but an async workflow change it to red right after.
Instead, I prefer UI testing. It will test the process from A to Z and force the execution of all the components mentioned above. Here's a nice framework I recommend: https://github.com/Microsoft/EasyRepro
That being said. To solve your problem you also need to mock the Organization Service and return fake data. By that, I mean this line:
EntityCollection unitsum_am_result = service.RetrieveMultiple(new FetchExpression(unitsum_am));
Sadly, even if you mock, you can't really parse the fetchXml and validate it.
Then, after returning the fake data you should assert that this line, create an entity with the right sums:
service.Create(alterunit);
I want to hide quick info section through code instead of unchecking the check box in Application Options dialog box. Can someone help in this?
The following code does exactly what your looking for.
Add this code below:
namespace Custom.Framework.SC.Extensions.Pipelines
{
using Sitecore.Pipelines.LoggedIn;
using SC = Sitecore;
/// <summary>The default quick info.</summary>
public class DefaultQuickInfo : SC.Pipelines.LoggedIn.LoggedInProcessor
{
/// <summary>The process.</summary>
/// <param name="args">The args.</param>
public override void Process(LoggedInArgs args)
{
const string DefaultToVisible = "false";
SC.Diagnostics.Assert.ArgumentNotNull(args, "args");
SC.Diagnostics.Assert.IsTrue(
SC.Security.Accounts.User.Exists(args.Username),
args.Username);
var user = SC.Security.Accounts.User.FromName(
args.Username,
true);
SC.Diagnostics.Assert.IsNotNull(user, "user");
var sitecoreDomain = SC.SecurityModel.DomainManager.GetDomain(
"sitecore");
SC.Diagnostics.Assert.IsNotNull(sitecoreDomain, "sitecoreDomain");
if (user.Domain != sitecoreDomain
|| user.Name.ToLower().EndsWith("\\" + sitecoreDomain.AnonymousUserName))
{
SC.Diagnostics.Log.Warn(this + " : unexpected security domain or user : " + user.Name, this);
return;
}
var key = "/" + args.Username + "/UserOptions.ContentEditor.ShowQuickInfo";
if (string.IsNullOrEmpty(user.Profile[key]))
{
user.Profile[key] = DefaultToVisible;
user.Profile.Save();
}
}
}
}
Then patch in this configuration change to add the processor to the appropriate place:
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<processors>
<loggedin>
<processor patch:after="processor[position()=last()]" type="Custom.Framework.SC.Extensions.Pipelines.DefaultQuickInfo, Custom.Framework.SC.Extensions" />
</loggedin>
</processors>
</sitecore>
</configuration>
I have a list that is dynamically generated from the view.
when the button is clicked, a new row is added, the value is entered and saved.
.zul
<zk>
<window border="normal" title="hello" viewModel="#id('vm') #init('gemalto.CreateServiceVersion')" apply="org.zkoss.bind.BindComposer">
<grid id="demoGrid"
model="#load(vm.profileList) #template((each.editingStatus) ? 'editable' : 'noneditable')">
<columns sizable="true">
<column width="160px" >Value</column>
<column width="160px" ></column>
</columns>
<rows>
<template name="editable">
<row>
<textbox id="valueTextBox"
value="#load(each.serviceProfile.valueVariable) #save(each.serviceProfile.valueVariable, before='confirm')" />
<button
image="/img/save.png" label="save"
onClick="#command('confirm', currentVariable=each)"/>
</row>
</template>
<template name="noneditable">
<row>
<label value="#load(each.serviceProfile.valueVariable)" />
</row>
</template>
</rows>
</grid>
<div align="center">
<button label="Add" image="/img/create.png" onClick="#command('onAddNew')" />
</div>
</window>
</zk>
view model
public class CreateServiceVersion extends SelectorComposer<Component> {
private boolean isEditing = false;
private boolean displayEdit = true;
private boolean isAddNew = false;
private List<ServiceProfileStatus> profileList = new ArrayList<ServiceProfileStatus>();
public List<ServiceProfileStatus> getProfileList() {
return profileList;
}
#AfterCompose
public void afterCompose() {
profileList.add(new ServiceProfileStatus(new ServiceProfile("value1"), false));
profileList.add(new ServiceProfileStatus(new ServiceProfile("value2"), false));
}
#Command
public void CrudServiceVersion() {
Executions.sendRedirect("CrudServiceVersion.zul");
}
#Command
#NotifyChange({"profileList"})
public void onAddNew() {
if (!isEditing) {
ServiceProfileStatus sps = new ServiceProfileStatus(new ServiceProfile(""), displayEdit);
profileList.add(0, sps);
isAddNew = true;
isEditing = true;
}
}
#Command
public void confirm(#BindingParam("currentVariable") ServiceProfileStatus sps) {
isEditing = false;
isAddNew = false;
sps.setEditingStatus(isEditing);
BindUtils.postNotifyChange(null,null,sps,"*");
}
}
the problem is that I add a new item, the value is copied to all others items.
I put the images to see more clearly what is happening.
imgur.com/7u7OkPG
imgur.com/mf8PUYI
imgur.com/aJpNoXM
I tested with your code and it works normally with me.
Now I changed the code a little bit :
Removed the extends SelectorComposer from your viewmodel cause that is not needed for MVVM.
Changed your 2 templates to 1 template.
Usage of your boolean in your viewmodel in the zul.
Zul :
<?xml version="1.0" encoding="UTF-8"?>
<zk>
<window border="normal" title="hello" viewModel="#id('vm') #init('be.chillworld.CreateServiceVersion')" apply="org.zkoss.bind.BindComposer">
<grid id="demoGrid" model="#load(vm.profileList)">
<columns sizable="true">
<column width="160px" >Value</column>
<column width="160px" ></column>
</columns>
<rows>
<template name="model">
<row>
<textbox value="#load(each.serviceProfile.valueVariable) #save(each.serviceProfile.valueVariable, before='confirm')" />
<button disabled="#load(not each.editingStatus)" visible="#load(each.editingStatus)" image="/img/save.png" label="save"
onClick="#command('confirm', currentVariable=each)"/>
</row>
</template>
</rows>
</grid>
<div align="center">
<button disabled="#load(vm.mayCreateNew)" label="Add" image="/img/create.png" onClick="#command('onAddNew')" />
</div>
</window>
</zk>
Viewmodel :
package be.chillworld;
import java.util.ArrayList;
import java.util.List;
import org.zkoss.bind.BindUtils;
import org.zkoss.bind.annotation.AfterCompose;
import org.zkoss.bind.annotation.BindingParam;
import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zk.ui.Executions;
/**
*
* #author chillworld */
public class CreateServiceVersion {
private boolean isEditing = false;
private List<ServiceProfileStatus> profileList = new ArrayList<ServiceProfileStatus>();
public List<ServiceProfileStatus> getProfileList() {
return profileList;
}
#AfterCompose
public void afterCompose() {
profileList.add(new ServiceProfileStatus(new ServiceProfile("value1"), false));
profileList.add(new ServiceProfileStatus(new ServiceProfile("value2"), false));
}
#Command
public void CrudServiceVersion() {
Executions.sendRedirect("CrudServiceVersion.zul");
}
#Command
#NotifyChange({"profileList","mayCreateNew"})
public void onAddNew() {
isEditing = true;
ServiceProfileStatus sps = new ServiceProfileStatus(new ServiceProfile(""), true);
profileList.add(0, sps);
}
#Command
#NotifyChange("mayCreateNew")
public void confirm(#BindingParam("currentVariable") ServiceProfileStatus sps) {
isEditing = false;
sps.setEditingStatus(isEditing);
BindUtils.postNotifyChange(null, null, sps, "*");
}
public boolean getMayCreateNew() {
return isEditing;
}
}
ServiceProfileStatus.java :
package be.chillworld;
/**
*
* #author chillworld
*/
public class ServiceProfileStatus {
private ServiceProfile serviceProfile;
private boolean editingStatus;
public ServiceProfileStatus(ServiceProfile serviceProfile, boolean editingStatus) {
this.serviceProfile = serviceProfile;
this.editingStatus = editingStatus;
}
public boolean isEditingStatus() {
return editingStatus;
}
public void setEditingStatus(boolean editingStatus) {
this.editingStatus = editingStatus;
}
public ServiceProfile getServiceProfile() {
return serviceProfile;
}
public void setServiceProfile(ServiceProfile serviceProfile) {
this.serviceProfile = serviceProfile;
}
public ServiceProfileStatus(ServiceProfile serviceProfile) {
this.serviceProfile = serviceProfile;
}
}
ServiceProfile.java :
package be.chillworld;
/**
*
* #author chillworld
*/
public class ServiceProfile {
private String valueVariable;
public ServiceProfile(String valueVariable) {
this.valueVariable = valueVariable;
}
public String getValueVariable() {
return valueVariable;
}
public void setValueVariable(String valueVariable) {
this.valueVariable = valueVariable;
}
}
I want to save some informaion to database when I send a request to mule service.
for this issue: I wrote below config in xml file:
<mule xmlns:vm="http://www.mulesoft.org/schema/mule/vm" xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns:cxf="http://www.mulesoft.org/schema/mule/cxf" xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:mule-ss="http://www.mulesoft.org/schema/mule/spring-security"
xmlns:ss="http://www.springframework.org/schema/security" xmlns:jdbc="http://www.mulesoft.org/schema/mule/jdbc"
xmlns:spring="http://www.springframework.org/schema/beans" version="CE-3.3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/cxf http://www.mulesoft.org/schema/mule/cxf/current/mule-cxf.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/current/mule-vm.xsd
http://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd
http://www.mulesoft.org/schema/mule/spring-security http://www.mulesoft.org/schema/mule/spring-security/3.3/mule-spring-security.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.mulesoft.org/schema/mule/jdbc http://www.mulesoft.org/schema/mule/jdbc/current/mule-jdbc.xsd ">
<spring:beans>
<spring:bean id="Initializer" name="Initializer" class="org.mule.example.scripting.IpClient" doc:name="Bean"/>
</spring:beans>
<notifications>
<notification event="COMPONENT-MESSAGE"/>
<notification-listener ref="Initializer"/>
</notifications>
<configuration doc:name="Configuration">
<expression-language>
<global-functions>
def parseIp(fullIp) {
return
fullIp.substring(fullIp.indexOf('/') + 1, fullIp.indexOf(':'))
}
</global-functions>
</expression-language>
</configuration>
<http:connector name="httpConnector" doc:name="HTTP\HTTPS">
<service-overrides sessionHandler="org.mule.session.NullSessionHandler" />
</http:connector>
<mule-ss:security-manager>
<mule-ss:delegate-security-provider
name="memory-dao" delegate-ref="authenticationManager" />
</mule-ss:security-manager>
<spring:beans>
<ss:authentication-manager alias="authenticationManager">
<ss:authentication-provider>
<ss:user-service id="userService">
<ss:user name="weather" password="weather" authorities="ROLE_ADMIN" />
</ss:user-service>
</ss:authentication-provider>
</ss:authentication-manager>
</spring:beans>
<flow name="Serive_test" doc:name="Serive_test">
<http:inbound-endpoint host="localhost" port="8089"
path="service/local-weather" exchange-pattern="request-response"
doc:name="HTTP">
<mule-ss:http-security-filter realm="mule-realm" />
</http:inbound-endpoint>
<async doc:name="Async">
<set-variable variableName="remoteClientAddress"
value="#[parseIp(message.inboundProperties['MULE_REMOTE_CLIENT_ADDRESS'])]"
doc:name="Variable" />
<message-properties-transformer
doc:name="myproperty" scope="session">
<add-message-property key="message.payload.remoteClientAddress"
value="#[parseIp(message.inboundProperties['MULE_REMOTE_CLIENT_ADDRESS'])]" />
</message-properties-transformer>
<component doc:name="classTest" class="org.mule.example.scripting.IpClient" />
</async>
<cxf:proxy-service service="Weather" doc:name="Weather_webservice"
wsdlLocation="http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl" namespace="http://ws.cdyne.com/WeatherWS/"
payload="envelope"></cxf:proxy-service>
<copy-properties propertyName="SOAPAction" doc:name="Property"></copy-properties>
<cxf:proxy-client doc:name="Weather_webservice"
payload="envelope" />
<outbound-endpoint address="http://wsf.cdyne.com/WeatherWS/Weather.asmx"
exchange-pattern="request-response" doc:name="HTTP"></outbound-endpoint>
</flow>
and IpClient class:
public class IpClient implements Callable,ModelNotificationListener<ModelNotification> {
#Override
public void onNotification(ModelNotification notification) {
// TODO Auto-generated method stub
System.out.println("Notification order event: " + notification.getActionName() );
if(notification.getAction() == ModelNotification.MODEL_DISPOSED || notification.getAction() == ModelNotification.MODEL_STOPPED){
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
ConnectDB c = new ConnectDB("localhost:3306", "accounting", "root", "");
String sql = " INSERT INTO weather (ip, date, CurrentState) VALUES (?,?,?) ";
PreparedStatement ps = null;
try {
ps = c.getConnnection().prepareStatement(sql);
ps.setString(1, "127.0.0.1");
ps.setString(2, dateFormat.format(date).toString());
ps.setString(3, notification.getActionName());
ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
c.close();
}
}
}
#Override
public Object onCall(MuleEventContext eventContext) throws Exception {
MuleMessage msg = eventContext.getMessage();
String remClient = msg.getProperty("remoteClientAddress", PropertyScope.INVOCATION);
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
ConnectDB c = new ConnectDB("localhost:3306", "accounting", "root", "");
String sql = " INSERT INTO weather (ip, date, CurrentState) VALUES (?,?,?) ";
PreparedStatement ps = c.getConnnection().prepareStatement(sql);
ps.setString(1, remClient);
ps.setString(2, dateFormat.format(date).toString());
ps.setString(3, msg.getMuleContext().getLifecycleManager().getCurrentPhase());
ps.executeUpdate();
c.close();
return msg.getPayload();
}
}
this program just works correctly if I start to run service. for example my service run before suddenly it was dissposed(for example my wsdl(http://wsf.cdyne.com/WeatherWS/Weather.asmx?wsdl doesn't work)). my log program saved all records in state of start and after dissposing it works similar. if I stoped my service and run it again it works correctly and it saves all records in dissposed mode.
I don't know how to change my program that it saves correctly in runtime.
The (ill named) Initializer bean is not used as a <component> anywhere in the configuration so there's no way its onCall method will ever get called.
If your intention with:
<notification event="COMPONENT-MESSAGE"/>
is to have the Initializer called for each component invocation you then have to make it implement ComponentMessageNotificationListener (like you made it implement ModelNotificationListener).