A new java component that is suppose to send along a list of pre-define variable to external party. User will then able to define which variable (a fixed set of variable) they want to send via a xml property file
Problem is: The pre-define variable is inside a class, and have business logic together with it, which i can't change or tap into (part of external api). eg as follow:
public class DataColumn implement java.io.Serializable{
public static final String _actionID = "ActionID";
public static final String _actionName = "ActionName";
public static final String _actionDesc = "ActionDescription";
public static final DataColumn ActionDesc = new DataColumn (_actionDesc);
public static final DataColumn ActionID = new DataColumn (_actionID);
public static final DataColumn ActionName = new DataColumn (_actionName);
....and about 100 other variale more
protected WebMediaReportColumn(java.lang.String value) {
_value_ = value;
_table_.put(_value_,this);
}
}
user will define in the xml file like following:
<interface>
<fields isRequired="true">
<field>ActionID</field>
<field>ActionName</field>
<field>ActionDescription</field>
</fields>
</interface>
In order to set which variable to send across, the code looks like following:
interface.setColumns(new DataColumn[] {
DataColumn.ActionID, DataColumn.ActionName, DataColumn.ActionDesc
}
);
Question is: how best to write a program that can base on what the user define in a property file, and create the Datacolumn[] Array accordingly? Without using If else block which is too long and hard to maintain.
Write all these statements in a property file.
actionID = ActionID
actionName = ActionName
actionDesc = ActionDescription
/*... and so on */
In your code create an Properties object and load this file into it. Create an String array and populate the values from this properties object.
Properties p = new Properties();
/*...load the file...*/
String []columnData = new String[p.keySet().size()];
int i=0;
for(Object str : p.keySet()) {
columnData[i++]=(String)p.getProperty((String)str);
}
/*now call the method and pass the array*/
interface.setColumns(columnData);
Related
I have setup #EnableMessageHistory
I have created custom transformers like this
public class FileMoveTransformer implements GenericTransformer<CustomerPojo, CustomerPojo> {
private boolean renameFile;
private String toLocation;
private static final Logger LOGGER = LoggerFactory.getLogger(FileMoveTransformer.class);
public FileMoveTransformer(String toLocation, final boolean renameFile) {
this.toLocation = toLocation;
this.renameFile = renameFile;
}
#Override
public CustomerPojo transform(CustomerPojo input) {
return input;
}
}
When I look at the Message history its showing like this
How do I change the "name" attribute to my own transformer as above will make not sense to print.
The MessageHistory makes it based on the bean name:
private static Properties extractMetadata(NamedComponent component) {
Entry entry = new Entry();
String name = component.getComponentName();
String type = component.getComponentType();
if (name != null && !name.startsWith("org.springframework.integration")) {
entry.setName(name);
if (type != null) {
entry.setType(type);
}
}
if (!entry.isEmpty()) {
entry.setTimestamp(Long.toString(System.currentTimeMillis()));
}
return entry;
}
Since you don't provide an explicit id for the endpoint which uses your FileMoveTransformer, you get that generated bean name based on the endpoint ConsumerEndpointFactoryBean class.
Since you don't show how you use this FileMoveTransformer, I only can abuse guessing that it is about an IntegrationFlow and you have something like this:
.transform(new FileMoveTransformer())
So, consider to add an id there like:
.transform(new FileMoveTransformer(), e -> e.id("fileMoveTransformer"))
https://docs.spring.io/spring-integration/reference/html/java-dsl.html#java-dsl-endpoints
Otherwise, please, share how you use it and we will let you know what need to be changed to bring your own custom id for the component and make your message history much readable.
I am working on a java application with Spring and JDBC connection.
I did not code the application myself and I am kind of new to some of the frameworks and implications of this.
I traced the path of how sql statements are passed, however I am stuck at some point where I do not find any details on the method that is called and the class it belongs to.
The method is imported via import de.dit.icr.frontend.ProxyBean;
Basically all queries are contained in a single "query.xml" file, that is passed into a "ProxyBean" object, which is then fed with the parameters map and then a "getStatement()" method is called that returns the prepared query string.
What I would like to do is to split the query.xml file into single sql files (one for each query) and implement a new method instead of the proxyBean.getStatement(), that would still take the parameters map, the name of the query, and prepare the statement.
In order to do that, I require your light on something:
- where does this ProxyBean class come from? From an external library, and if so, which one?
- Which method, which library could I use to create a string of an sql prepared statement from a sql file and a parameters map?
Thanks a lot for your help!
Here is a simplified view of the code:
import de.dit.icr.frontend.ProxyBean;
import de.dit.icr.util.Url;
import de.dit.itsales.dbi.util.DBUtil;
import de.dit.itsales.dbi.util.Log;
public class IASAdapterImpl implements IASAdapter {
private static final String[] QUERY_FILES = {"query.xml"};
private static final String RELATIVE_PATH_TO_QUERY_FILE = "queries";
public IASAdapterImpl() {
init();
}
private void init() {
String key = "queries";
String pathToQueryFile = DBUtil.getInstance().getConfigDir() + RELATIVE_PATH_TO_QUERY_FILE;
Url.register(key, pathToQueryFile);
createProxyBean();
}
public synchronized String getQuery(String queryName, Map<String,String> queryVariables, boolean resolveNames) {
ProxyBean proxyBean = createProxyBean();
setParameter(queryVariables, proxyBean);
String temp = proxyBean.getStatement(queryName, resolveNames);
return temp;
}
private ProxyBean createProxyBean() {
ProxyBean bean = new ProxyBean();
for (int i = 0; i < QUERY_FILES.length; i++) {
bean.setQuerySet(QUERY_FILES[i]);
}
return bean;
}
private void setParameter(Map<String,String> map, ProxyBean bean) {
if(map == null || map.isEmpty()) {
return;
}
for (Map.Entry<String,String> entry : map.entrySet()) {
String key = entry.getKey();
bean.set(key, entry.getValue());
}
}
Sample of query.xml:
<query name = "alle-fonds"><![CDATA[
select fondsnummer,
spokid,
mfnummer,
fondsname,
bewertungsdatum,
letztebewertung,
hauptfonds,
performancegraphrelevant
from rep.v_alle_fonds where anwender_login = '${loginname}'
and sprache = lower('${sprache}')
order by mfnummer
]]></query>
In drop down I get like this. find image attached. Actually in coulmn of "Name" field both 'Name' and 'Description' are displaying as comma(,) separated.
final ComboBoxItem comboBoxItem = new ComboBoxItem("attributeTypeId","Attr. Type");
ListGridField nameField = new ListGridField("name", "Name");
ListGridField descField = new ListGridField("description","Description");
descField.setShowHover(true);
comboBoxItem.setPickListFields(nameField, descField);
comboBoxItem.setPickListWidth(200);
comboBoxItem.setFilterLocally(true);
comboBoxItem.setColSpan(2);
comboBoxItem.setAddUnknownValues(false);
comboBoxItem.setValueField(FieldNames.ID_FIELD);
comboBoxItem.setDisplayField(FieldNames.NAME_FIELD);
comboBoxItem.setAutoFetchData(true);
OptionListDataSource attrTypeds = OptionListDataSource.getInstance(FieldNames.ATTRIBUTE_TYPE_FIELD);
attrTypeds.fetchData(null, new DSCallback() {
#Override
public void execute(final DSResponse response, final Object rawData, final DSRequest request) {
Record[] recList = response.getData();
LinkedHashMap<String, String[]> dataLinkMap = new inkedHashMap<String,String[]>(); //LinkedHashMap<String,
dataLinkMap.put("0", new String[]{"Select",""});
for (Record record : recList) {
String attrId = record.getAttribute(FieldNames.ID_FIELD);
String attrName = record.getAttribute(FieldNames.NAME_FIELD);
String attrDesc = record.getAttribute(FieldNames.DESCRIPTION_FIELD);
dataLinkMap.put(attrId, new String[]{attrName,attrDesc});
}
comboBoxItem.setValueMap(dataLinkMap);
}
});
Screen Shot
Here is some sample code to achieve what I understand you want to achieve:
public class TestCases implements EntryPoint {
public void onModuleLoad() {
DataSource logDS = DataSource.get("yourDSName");
final DynamicForm form = new DynamicForm();
form.setWidth(550);
form.setNumCols(2);
ListGridField nameField = new ListGridField(FieldNames.NAME_FIELD);
ListGridField descriptionField = new ListGridField(FieldNames.NAME_DESCRIPTION);
LinkedHashMap<String,String> hashMap = new LinkedHashMap<String,String>();
hashMap.put("-1", "Select");
ComboBoxItem myItem = new ComboBoxItem();
myItem.setTitle("ComboBox");
myItem.setOptionDataSource(logDS);
myItem.setDisplayField("category");
myItem.setValueField(FieldNames.ID_FIELD);
myItem.setSpecialValues(hashMap);
myItem.setPickListWidth(300);
myItem.setPickListFields(nameField, descriptionField);
form.setItems(myItem);
form.draw();
}
}
Notice:
In order to display various fields, you need to use setPickListFields with the reference to those fields.
You don't need to call fetch() on the DataSource itself. This is done automatically for you when you use DataBound components like ComoBoxItem.
You can add additional empty values using setSpecialValues() without modifying your DSResponse data (which is why you don't need to use fetch() directly).
EDIT
The problem you are having is that the ValueMap, which is just a Map (in other words, just a group of key/value pairs), that you are providing to the ComboBoxItem is not the same as the Record[] object provided directly by the DataSource, which in essence is just a List made of several Maps, each representing a field name and its value. This way, besides the value field, you can provide several fields to the ComboBoxItem for display purposes, like Name and Description, in your particular case.
From looking at the API, it looks to me that you can't provide a Record[] manually to the ComboBoxItem, so either you get the data via DMI (which for me is the easiest) or other method that allows you to modify and return the required response from the server automatically to the ComboBoxItem by using the data binding capabilities, or you stick to showing just the "values" (which is what you are getting right now, but off course you could format the data better).
What I mean with formatting is that if you choose to go with your original approach of using setValueMap(), you need to provide a Map where each entry in the Map is just a value on the ComboBoxItem and its respective display "text", which can be any String combining the values of several other fields, and formatted as desired using String concatenation (for instance, you could make it
nameField + ": " + descriptionField
But this is as good as it gets with this approach.
Now, via a DMI you would need to define the server class that would provide the properly-formatted data in you datasource descriptor (ds.xml file):
<operationBindings>
<operationBinding operationType="fetch" serverMethod="fetchComboBoxData">
<serverObject lookupStyle="new" className="com.myApp.ComboBoxDMI"/>
</operationBinding>
</operationBindings>
And then create the class and method to provide what you need:
public class ComboBoxDMI {
public DSResponse fetchComboBoxData(DSRequest dsRequest) throws Exception {
DSResponse response = dsRequest.execute();
if (response.statusIsSuccess()) {
#SuppressWarnings("unchecked")
List<Map<String, Object>> recList = response.getRecords();
List<Map<String, Object>> comboBoxList = new ArrayList<Map<String,Object>>();
// Add here the new record... for each field in your DataSource, you need to set a Map
// with the key being the field name and the value being the field value. So you need
// 1 Map entry per field. All your Map entries form 1 record, and that's what you add
// to your List of Maps
return constructDSResponse(comboBoxList);
}
return response;
}
private DSResponse constructDSResponse(List<Map<String, Object>> comboBoxList) {
DSResponse response = new DSResponse();
int totalRows = comboBoxList.size();
response.setStartRow(totalRows > 0 ? 1 : 0);
response.setEndRow(totalRows);
response.setTotalRows(totalRows);
response.setData(comboBoxList);
return response;
}
}
Finally, you can follow the original approach I suggest in my original answer, but now you don't need to use the setSpecialValues API, which your version doesn't support.
I'm working on a Java project that uses a big class of constants like:
public final class Settings {
public static final int PORT_1 = 8888;
public static final int PORT_2 = 8889;
...
}
Now, some of the value of those constants are not available at compile time anymore so I need a way to "initialize" them at application starts (e.g. from the args[]). Once initialized there should be no way to change them. I'm not very skilled in java, how do I do this in an acceptable way?
I thought of using a singleton with something like a "one shot" set method that throws an exception if called more than one time but it seams too hacky...
You can use a static initializer like this:
public final class Settings {
public static final int PORT_1;
public static final int PORT_2;
...
static {
// create the value for PORT_1:
PORT_1 = ...;
// create the value for PORT_2:
PORT_2 = ...;
}
}
The static initializer is executed during class loading. The final keywords on PORT_1 and PORT_2 protects them to be changed afterwards.
Well, using system properties is a way of doing it unless there is a huge amount of constants.
private static final String CONSTANT1 = System.getProperty("my.system.property");
private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));
System properties are passed on the command line when starting the application using the -D flag.
If there are too many variables a static initializer can be used where a property file or similar can be read that holds the properties:
public class Constants {
private static final String CONSTANT1 = System.getProperty("my.system.property");
private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));
private static final String CONSTANT3;
private static final String CONSTANT4;
static {
try {
final Properties props = new Properties();
props.load(
new FileInputStream(
System.getProperty("app.properties.url", "app.properties")));
CONSTANT3 = props.getProperty("my.constant.3");
CONSTANT4 = props.getProperty("my.constant.3");
} catch (IOException e) {
throw new IllegalStateException("Unable to initialize constants", e);
}
}
}
Note that if you are using some external framework such as Spring Framework or similar there is usually a built-in mechanism for this. E.g. - Spring Framework can inject properties from a property file via the #Value annotation.
There is no simple way to do this in Java. One way to simulate this is to use a builder which returns an internal type (so it can write the private fields) but the internal type only has getters.
See this answer: https://stackoverflow.com/a/1953567/34088
I have lots of values in properties files, which are read in my app to setup values (DB connections, email servers, etc.).
db.properties:
db.user=admin
db.pwd=secret1234
Now in my DatabaseService class, I have something like this:
private static final String DB_USER = "db.user";
private static final String DB_PWD = "db.pwd";
private Properties dbProps = new Properties();
// read db.properties values into dbProps
String user = dbProps.getProperty(DB_USER);
Then in my DatabaseServiceTest class, I have repeated code:
private static final String DB_USER = "db.user";
private static final String DB_PWD = "db.pwd";
private Properties dbProps = new Properties();
// read db.properties values into dbProps
String user = dbProps.getProperty(DB_USER);
So I have repeated code. So instead I have put the static String values into a StaticVars class that hosts all of the Strings so the DatabaseService and DatabaseServiceTest now look like this (I could also put the Properties in the utility class, but there are scores of this example, so I haven't so far):
private Properties dbProps = new Properties();
// read db.properties values into dbProps
String user = dbProps.getProperty(StaticVars.DB_USER);
Is there a better way to share the static Strings across multiple class files? My current StaticVars class has about 150 static String values, and growing. It seems like I am going down the wrong path.
Thanks,
Sean
I think your general approach - using public static final String members of a public class - is a fine way to share strings across an application.
However don't underestimate the importance of naming. When you come back to this code in 6 months will you remember that the names of your properties are stored in a class called StaticVars? If you are truly only storing property names, then perhaps the class should be called PropertyNames. Now you have bounded the scope of the class and will be less likely mix in strings for error messages or regular expressions or whatever. (Those should go into different classes with meaningful names to help you remember what kind of values they store.)
Taking this a step further, since these are property names, they are likely to be used in getProperty calls. So why not rename the class PropertyUtils or ConfigUtils, and have matching static methods which use the property names. Then you can add default property values if certain properties are optional.
public static final String DB_HOST = "db.host";
public static final String DB_USER = "db.user";
public static final String DB_PWD = "db.pwd";
public static String getDbHost(Properties props)
{
return props.getProperty(DB_HOST, "localhost");
}
public static String getDbUser(Properties props)
{
return props.getProperty(DB_USER, "admin");
}
public static String getDbPwd(Properties props)
{
return props.getProperty(DB_PWD);
}