Springbatch FlatfileItemReader problem with & in line - spring-batch

I created an application using Springbatch. The batch read a csv file and do some stuff after. Everything works fine except when a line in the file contains the character &.
For example:
"BB1222";"Myexample & blabla";"tayoo"
I don't understand why and how to fix it, but the batch fail and can not convert the line in my object. It throw java.lang.IndexOutOfBoundsException: start ....
I defined my reader like this:
#Bean
public FlatFileItemReader<Bank> bankReader() {
FlatFileItemReader<Bank> reader = new FlatFileItemReader<Bank>();
reader.setLinesToSkip(1);
reader.setStrict(false);
reader.setEncoding("UTF-8");
reader.setLineMapper(new DefaultLineMapper<Bank>() {
{
setLineTokenizer(new DelimitedLineTokenizer() {
{
setNames(new String[]{
...
});
setDelimiter(";");
}
});
setFieldSetMapper(new BeanWrapperFieldSetMapper<Bank>() {
{
setTargetType(Bank.class);
}
});
}
});
return reader;
}
Can you help me ?
Thanks in advance !

Your issue does not seem to be related to the presence of &. Here is a passing test with your example:
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.core.io.ByteArrayResource;
class DemoApplicationTests {
#Test
void testFlatFileItemReaderWithAmpersandInInput() throws Exception {
// given
String line = "\"BB1222\";\"Myexample & blabla\";\"tayoo\"";
Charset encoding = StandardCharsets.UTF_8;
FlatFileItemReader<Bank> reader = new FlatFileItemReader<>();
reader.setStrict(false);
reader.setEncoding(encoding.name());
reader.setLineMapper(new DefaultLineMapper<Bank>() {
{
setLineTokenizer(new DelimitedLineTokenizer() {
{
setNames("code", "name", "address");
setDelimiter(";");
}
});
setFieldSetMapper(new BeanWrapperFieldSetMapper<Bank>() {
{
setTargetType(Bank.class);
}
});
}
});
reader.setResource(new ByteArrayResource(line.getBytes(encoding)));
// when
reader.open(new ExecutionContext());
Bank item1 = reader.read();
Bank item2 = reader.read();
reader.close();
// then
Assertions.assertNotNull(item1);
Assertions.assertEquals("BB1222", item1.getCode());
Assertions.assertEquals("Myexample & blabla", item1.getName());
Assertions.assertEquals("tayoo", item1.getAddress());
Assertions.assertNull(item2);
}
public static class Bank {
String code;
String name;
String address;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
}
You didn't share the full stacktrace, but you probably have a line with less or more tokens than expected. Moreover, since you configured the reader with UTF-8, you need to make sure the input file is encoded in UTF-8.

Related

TP-Link Omada Software Controller password cipher

user_name:"test"
password:"NhNq8N6pbpj/GXiTYVP41A=="
The plaintext password is "lala". These are properties from JSON document, from mongoDB instance of TP-Link Omada Software Controller.
If anyone has information on the cipher used to decrypt "lala", it would solve my problem. The UI for the software already has a bulk import/export user feature, but it has a bug and is defunct. Has been defunct for many prior versions, not sure since which, and is still defunct in current version 4.4.6.
I can automate localuser addition by directly storing user JSON into mongoDB, but the password must be stored using the specific cipher which is not documented and is unknown.
Any help would be appreciated.
public void setPassword(String password) {
this.password = com.tplink.omada.common.util.b.a.c(password);
}
Blockquote
package com.tplink.omada.common.util.b;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class a {
private static final Logger a = LoggerFactory.getLogger(a.class);
private static final byte[] b = new byte[]{73, 30, 95, 35, -84, -65, -98, -76};
public static byte[] a(String var0) {
try {
return com.tplink.omada.common.util.b.b.b(var0);
} catch (Exception var2) {
a.error(var2.toString(), var2);
return null;
}
}
public static String a(byte[] var0) {
try {
return com.tplink.omada.common.util.b.b.c(var0);
} catch (Exception var2) {
a.error(var2.toString(), var2);
return null;
}
}
public static String b(byte[] var0) {
return new String(k.b(var0, 0), StandardCharsets.UTF_8);
}
public static byte[] b(String var0) throws NoSuchAlgorithmException {
Object var1 = null;
KeyGenerator var2 = KeyGenerator.getInstance("AES");
SecureRandom var3 = SecureRandom.getInstance("SHA1PRNG");
var3.setSeed(var0.getBytes(StandardCharsets.UTF_8));
var2.init(128, var3);
SecretKey var4 = var2.generateKey();
byte[] var5 = var4.getEncoded();
return var5;
}
public static byte[] a(String var0, String var1) {
try {
SecretKeySpec var2 = new SecretKeySpec(b(var1), "AES");
Cipher var3 = Cipher.getInstance("AES");
byte[] var4 = var0.getBytes("UTF-8");
var3.init(1, var2);
return var3.doFinal(var4);
} catch (Exception var5) {
a.warn(var5.toString(), var5);
return null;
}
}
public static byte[] a(byte[] var0, String var1) {
try {
SecretKeySpec var2 = new SecretKeySpec(b(var1), "AES");
Cipher var3 = Cipher.getInstance("AES");
var3.init(2, var2);
return var3.doFinal(var0);
} catch (Exception var4) {
a.warn(var4.toString(), var4);
return null;
}
}
// ========== begin password fiasco =============
public static String c(String var0) {
if (var0 == null) {
return null;
} else {
byte[] var1 = a(var0, b(b));
return a(var1);
}
}
// ============ end password fiasco =============
public static String d(String var0) {
if (var0 == null) {
return null;
} else {
byte[] var1 = a(var0);
byte[] var2 = a(var1, b(b));
try {
return new String(var2, "UTF-8");
} catch (UnsupportedEncodingException var4) {
a.warn(var4.toString(), var4);
return new String(var2);
}
}
}
}

ListView validate edit and prevent commit

I'm using an editable ListView containing Patterns.
The user can see and edit the regexs in the list, and I'd like to validate whether the regex is syntactically correct before committing the value (and give feedback like a red border to the user).
Is there a way to do so?
patternList.setCellFactory(TextFieldListCell.forListView(new StringConverter<Pattern>() {
#Override
public String toString(Pattern pattern) {
return pattern.toString();
}
#Override
public Pattern fromString(String string) {
try {
return Pattern.compile(string);
} catch (PatternSyntaxException e) {
return null;
}
}
}));
patternList.setOnEditCommit(e -> {
if (e.getNewValue() == null) {
// TODO pattern syntax error, prevent commit and stay in edit mode
} else {
patternList.getItems().set(e.getIndex(), e.getNewValue());
}
});
I would do this by creating a TableCell implementation. E.g.:
import java.util.function.Predicate;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.css.PseudoClass;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
public class ValidatingEditingCell<S> extends TableCell<S, String> {
private final TextField textField ;
private static final PseudoClass INVALID = PseudoClass.getPseudoClass("invalid");
private BooleanProperty valid = new SimpleBooleanProperty();
public ValidatingEditingCell(Predicate<String> validator) {
this.textField = new TextField();
valid.bind(Bindings.createBooleanBinding(() -> textField.getText() != null && validator.test(textField.getText()),
textField.textProperty()));
valid.addListener((obs, wasValid, isValid) -> {
pseudoClassStateChanged(INVALID, ! isValid);
});
pseudoClassStateChanged(INVALID, ! valid.get());
textField.addEventHandler(KeyEvent.KEY_PRESSED, e -> {
if (e.getCode() == KeyCode.ENTER && valid.get()) {
commitEdit(textField.getText());
}
if (e.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
});
setGraphic(textField);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
textField.setText(empty ? null : item);
setContentDisplay(isEditing() ? ContentDisplay.GRAPHIC_ONLY : ContentDisplay.TEXT_ONLY);
}
#Override
public void cancelEdit() {
super.cancelEdit();
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void commitEdit(String newValue) {
super.commitEdit(newValue);
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void startEdit() {
super.startEdit();
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.selectAll();
textField.requestFocus();
}
}
This takes a predicate as an argument; the predicate returns true for valid text and false for invalid text. It sets a CSS pseudoclass on the cell, so you can use CSS to style the text field (or cell itself, if needed).
Here's a simple example which validates three different columns differently:
import java.util.function.Function;
import java.util.function.Predicate;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ValidatingTableExample extends Application {
private static <S> TableColumn<S, String> column(String title, Function<S, StringProperty> property,
Predicate<String> validator) {
TableColumn<S, String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(tc -> new ValidatingEditingCell<>(validator));
col.setPrefWidth(150);
return col ;
}
#Override
public void start(Stage primaryStage) {
TableView<Address> table = new TableView<>();
table.setEditable(true);
table.getColumns().add(column("City", Address::cityProperty, s -> ! s.isEmpty()));
table.getColumns().add(column("State", Address::stateProperty, s -> s.length()==2));
table.getColumns().add(column("Zip", Address::zipProperty, s -> s.matches("\\d{5}")));
Button newAddress = new Button("Add");
newAddress.setOnAction(e -> {
table.getItems().add(new Address("City", "State", "Zip"));
});
Button debug = new Button("Debug");
debug.setOnAction(e ->
table.getItems().stream()
.map(address -> String.format("%s, %s %s", address.getCity(), address.getState(), address.getZip()))
.forEach(System.out::println));
HBox buttons = new HBox(5, newAddress, debug);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(new Insets(5));
BorderPane root = new BorderPane(table, null, null, buttons, null);
Scene scene = new Scene(root, 600, 600);
scene.getStylesheets().add(getClass().getResource("validating-cell.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public static class Address {
private final StringProperty city = new SimpleStringProperty();
private final StringProperty state = new SimpleStringProperty();
private final StringProperty zip = new SimpleStringProperty();
public Address(String city, String state, String zip) {
setCity(city);
setState(state);
setZip(zip);
}
public final StringProperty cityProperty() {
return this.city;
}
public final String getCity() {
return this.cityProperty().get();
}
public final void setCity(final String city) {
this.cityProperty().set(city);
}
public final StringProperty stateProperty() {
return this.state;
}
public final String getState() {
return this.stateProperty().get();
}
public final void setState(final String state) {
this.stateProperty().set(state);
}
public final StringProperty zipProperty() {
return this.zip;
}
public final String getZip() {
return this.zipProperty().get();
}
public final void setZip(final String zip) {
this.zipProperty().set(zip);
}
}
public static void main(String[] args) {
launch(args);
}
}
and some sample CSS:
.table-cell:invalid .text-field {
-fx-focus-color: red ;
-fx-control-inner-background: #ffc0c0 ;
-fx-accent: red ;
}
I finally found a way, by overriding the commitEdit() method of TextFieldListCell:
patternList.setCellFactory(l -> new TextFieldListCell<Pattern>(new StringConverter<Pattern>() {
#Override
public String toString(Pattern pattern) {
return pattern.toString();
}
#Override
public Pattern fromString(String string) {
try {
return Pattern.compile(string);
} catch (PatternSyntaxException e) {
return null;
}
}
}) {
#Override
public void commitEdit(Pattern pattern) {
if (!isEditing()) return;
PseudoClass errorClass = PseudoClass.getPseudoClass("error");
pseudoClassStateChanged(errorClass, pattern == null);
if (pattern != null) {
super.commitEdit(pattern);
}
}
});
patternList.setOnEditCommit(e -> patternList.getItems().set(e.getIndex(), e.getNewValue()));

Dynamic styling with IceFaces: how can I change the color of the outputText at the time of rendering?

I'm new to ICEFaces and I have to maintain someone else's code. I'm working on a web chat where the user can send and receive messages. I would like the messages to have different colors depending on whether they were sent by the user or by someone else.
I currently have the following code in my xhtml file:
<h:dataTable id="history" value="#{chatBean.messages}" var="message" border="0" align="left" style="width: 100%" >
<h:column width="590" height="25" align="left" valign="bottom" >
<h:outputText value="#{message}" styleClass="#{chatBean.messageColor}" />
</h:column>
</h:dataTable>
This shows all messages sent and received, but all with the same color, even though the messageColor property of the chat bean changes: I did an experiment and appended the result of getMessageColor() at the end of each message and it does change, but the text is still rendered in the same color.
The CSS has the following classes (among others):
.class1
{
color:#818181;
width: 100%;
font-size: 15px;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
}
.class2
{
color:#00657c;
width: 100%;
font-size: 15px;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
}
Here's the code for the ChatBean class:
#ManagedBean(name = "chatBean")
#SessionScoped
public class ChatBean implements Serializable {
private static final long serialVersionUID = -12636320254821872L;
private static final String PUSH_GROUP = "chatPage";
private PortableRenderer renderer;
private String message;
private String lastMessageSent = "";
private Date lastMessageTime = new Date();
private String isDown = "false";
private List<String> messages = new ArrayList<String>();
private String buttonDisabled = "true";
private String buttonCancelDisabled = "true";
private String pollDisabled = "false";
private String id = "";
private ChatClient chat;
private Timer timer = new Timer();
private String messageColor;
public class ChatThread extends Thread implements Serializable {
private static final long serialVersionUID = -7636532554421738019L;
private Map<String, String> data;
private String mail;
public ChatThread(final Map<String, String> data, final String mail) {
this.data = data;
this.mail = mail;
}
#Override
public void run() {
chat = new ChatClient(new ChatClient.Event() {
#Override
public void handle(String msg) {
if(msg != null && msg.length() > 0)
pushMessage(msg);
}
#Override
public void agentConnected(String msg) {
buttonDisabled = "false";
buttonCancelDisabled = "false";
pushMessage(msg);
}
#Override
public void agentDisconnected(String msg) {
buttonDisabled = "true";
pushMessage(msg);
try {
timer.cancel();
} catch (Exception e) {
e.printStackTrace();
}
}
});
chat.login(mail, data);
chat.join(mail, data.get("partner"), data.get("business"));
timer.scheduleAtFixedRate(new RefreshTimerTask(), 0, 1000);
}
}
public class RefreshTimerTask extends TimerTask implements Serializable {
private static final long serialVersionUID = 1852678537009150141L;
public void run() {
chat.refresh();
}
}
public ChatBean() {
if(getSession() != null) {
id = getSession().getId();
PushRenderer.addCurrentSession(PUSH_GROUP + id);
renderer = PushRenderer.getPortableRenderer();
setMessageColor("class1");
Log.getLogger().debug("New chat bean.");
if(getData().containsKey("login_chat")) {
ChatThread chat = new ChatThread(getData(), getSessionAttribute(GenesysSingleton.getInstance().getConfigApp().getDisplayName(), "<mail>"));
chat.start();
}
}
}
private void pushMessage(String msg) {
if(msg != null && !msg.isEmpty()) {
ChatBean.this.isDown = "true";
messages.add(msg);//Acá se puede acceder a textColor.
try {
PushRenderer.render(PUSH_GROUP + id);
} catch (Exception e) {
renderer.render(PUSH_GROUP + id);
}
setMessageColor("class1");
}
}
private String getSessionAttribute(String key, String ref) {
Object value = getSession().getAttribute(key);
return value != null ? value.toString() : ref;
}
private Map<String, String> getData() {
Map<String, String> data = new HashMap<String, String>();
HttpSession session = getSession();
#SuppressWarnings("rawtypes")
Enumeration enums = session.getAttributeNames();
while(enums.hasMoreElements()) {
String key = enums.nextElement().toString();
if(!"com.sun.faces.application.view.activeViewMaps".equals(key)
&& !"com.sun.faces.renderkit.ServerSideStateHelper.LogicalViewMap".equals(key)
&& !"javax.faces.request.charset".equals(key))
data.put(key, session.getAttribute(key).toString());
}
return data;
}
public void sendMessage(ActionEvent event) {
sendMessage();
}
protected synchronized void sendMessage() {
if (message != null && !message.trim().isEmpty()){
Date now = new Date();
//No permito mandar el mismo mensaje 2 veces seguidas en un intervalo menor a un segundo.
message = message.trim();
if (message.equals(lastMessageSent)&&(now.getTime()<(1000+lastMessageTime.getTime()))){
message = null;
}
else{
setMessageColor("class2");
lastMessageSent = message;
message = null;
lastMessageTime = new Date();
chat.refresh(lastMessageSent);
}
}
}
public String disconnect() {
pollDisabled = "true";
return "login";
}
public void sendClose(ActionEvent event) {
}
public void receiveMessage() {
}
#PreDestroy
public void destroy() {
buttonDisabled = "true";
try {
//pushMessage(SISTEMA_3);
} catch (Exception e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
};
System.out.println(id + "- ssssssss");
try {
timer.cancel();
} catch (Exception e) {
}
chat.logout();
chat.close();
}
private HttpSession getSession() {
return (HttpSession) getContext().getSession(false);
}
private ExternalContext getContext() {
return getFacesContext().getExternalContext();
}
private FacesContext getFacesContext() {
return FacesContext.getCurrentInstance();
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getButtonDisabled() {
return buttonDisabled;
}
public void setButtonDisabled(String buttonDisabled) {
this.buttonDisabled = buttonDisabled;
}
public List<String> getMessages() {
try {
JavascriptContext.addJavascriptCall(getFacesContext(), "document.getElementById('scrollDataTable').scrollIntoView();");
} catch (Exception e) {
e.printStackTrace();
}
return messages;
}
public void setMessages(List<String> messages) {
this.messages = messages;
}
public String getPollDisabled() {
return pollDisabled;
}
public void setPollDisabled(String pollDisabled) {
this.pollDisabled = pollDisabled;
}
public String getButtonCancelDisabled() {
return buttonCancelDisabled;
}
public void setButtonCancelDisabled(String buttonCancelDisabled) {
this.buttonCancelDisabled = buttonCancelDisabled;
}
public String getIsDown() {
return isDown;
}
public void setIsDown(String isDown) {
this.isDown = isDown;
}
public String getMessageColor() {
return messageColor;
}
public void setMessageColor(String textColor) {
this.messageColor = textColor;
}
}
All help will be appreciated. Thank you in advance.
One possible way which I have changed css dynamically depending on a bean property is through using the styleClass attribute of the <h:outputText>.
In your css file define two varying classes such as
.class1
{
color:red; //Put your colour here (#818181)
}
.class2
{
color:green; //Put your colour here (#00657c)
}
Then in your java bean code you could declare a String field with getters and setters such as
private String messageColor;
Then in your code where you do
setTextColor(COLOR_AGENTE);
You can change this to the class which you would like to change the text to such as:
setMessageColor("class1");
Then on your <h:outputText> attach
styleClass="#{chatBean.messageColor}"
This should hopefully work;
Due to my original suggestion not working, you could try this.
Remove private String messageColor; from you chatBean and the getters/setters along with any calls to setMessageColor("class1");.
But keep the two classes in your css.
Now declare a boolean property with getters and setters in your chatBean:
private boolean colourAgente;
Declare a method:
public String setColor() {
if (colourAgente) {
return "class1";
} else {
return "class2";
}
}
Then in your xhtml change the styleClass attribute to:
styleClass="#{chatBean.setColor()}"
Finally, in your java code change:
setMessageColor("class1");
to either colourAgente = true; or colourAgente=false; depending on what colour you want to set.
I finally did it, but I had to use an ugly JavaScript workaround. That is, I'm now running this script every time the chat is refreshed:
function updateColors(){
var username = document.getElementById("form:username").value;
if (username.length > 0){
var x = document.getElementsByClassName("class1");
if (x != null){
for (i = 0; i < x.length; i++){
if (x[i].innerHTML.indexOf(username) === 0){
x[i].className = "class2";
}
}
}
}
}
Anyway, thanks for your help, LiamWilson94. I still don't know what part of the code I'm working with makes it so that your answers don't work, but you have given me a lot of insight which helped me arrive to this "solution", and I have learnt a few things about IceFaces in the process.
OK, I have found a better solution.
I created a TextModel class:
import java.io.Serializable;
public class TextModel implements Serializable {
private static final long serialVersionUID = -8470475291191399871L;
private String text;
private String clase;
public TextModel() {
}
public TextModel(String text, String clase) {
this.text = text;
this.clase = clase;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getClase() {
return clase;
}
public void setClase(String clase) {
this.clase = clase;
}
public String toString() {
return text;
}
}
Then I changed messages in ChatBean from List to List, and changed the following functions in ChatBean.java:
private void pushMessage(String msg) {
if(msg != null && !msg.isEmpty()) {
ChatBean.this.isDown = "true";
messages.add(new TextModel(msg,clase));
try {
PushRenderer.render(PUSH_GROUP + id);
} catch (Exception e) {
renderer.render(PUSH_GROUP + id);
}
clase = "class1";
}
}
protected synchronized void sendMessage() {
if (message != null && !message.trim().isEmpty()){
Date now = new Date();
message = message.trim();
if (message.equals(lastMessageSent)&&(now.getTime()<(1000+lastMessageTime.getTime()))){
message = null;
}
else{
clase = "class2";
lastMessageSent = message;
message = null;
lastMessageTime = new Date();
chat.refresh(lastMessageSent);
}
}
}
Where clase is either "class1" or "class2" (could be neater, but it works for now, I can always make it neater later).
Finally, on chat.xhtml, I changed the outputtext tag to:
<h:outputText value="#{message.text}" styleClass="#{message.clase}" />
That's it. No more messy JavaScript patches.
The trick was making the class a property of the message itself rather than the ChatBean.
I hope this helps someone else in the future.

Jenkins plugin development - persistence

I'm still learning plugin dev. This is my first one.
I would like to persist the configuration of my plugin, but it won't work.
Could you please tell me, what am I doing wrong?
I have tried debuging the process, starting from the addition of the plugin to the job 'til the saving of the job config.
I have found, that inside the load() method of the descriptor, no xml file is found!
The path it is looking for is something like: c:\users\Peter\workspace\r-script.\work\whatEverDir\xy.xml
I don't think that the .\ part is causing the config file not to be found, but since it is a Jenkins class generating this path, I would not bet on it. Although the system might have tried to create it here at the first place.
Thanks in advance!
Scriptrunner.jelly
<f:block>
<f:entry title="RScript" field="command">
<f:textarea style="width:99%" />
</f:entry>
</f:block>
<f:entry field="installedPackages" title="Installed packages">
<f:select style="width:40%" />
</f:entry>
<f:entry field="mirrors" title="Choose a mirror">
<f:select style="width:40%" />
</f:entry>
<f:entry>
<f:repeatableProperty field="availablePackages" minimum="1"/>
</f:entry>
AvailablePackage.jelly
<f:entry field="availablePackages">
<f:select style="width:40%" />
<f:repeatableDeleteButton />
</f:entry>
ScriptRunner.java
public class ScriptRunner extends Builder {
private static final String fileExtension = ".R";
private ArrayList<AvailablePackage> availablePackages;
private String command;
private String chosenMirror;
private List<String> mirrorList = new ArrayList<String>();
#DataBoundConstructor
public ScriptRunner(String command, ArrayList<String> installedPackages, ArrayList<String> mirrors, ArrayList<AvailablePackage> availablePackages) {
this.chosenMirror = mirrors.get(0);
this.availablePackages = availablePackages;
this.command = command;
}
public final String getCommand() {
return command;
}
#Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
BuildListener listener) throws InterruptedException {
return perform(build, launcher, (TaskListener) listener);
}
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
TaskListener listener) throws InterruptedException {
FilePath workSpace = build.getWorkspace();
FilePath rScript = null;
if (workSpace == null) {
try {
throw new NoSuchFileException("Workspace could not be set!");
} catch (NoSuchFileException e) {
e.printStackTrace();
}
}
try {
try {
String fullScript;
if (command.contains("options(repos=structure(")) {
fullScript = PackagesManager.singleton().createFullScript(availablePackages, "", command);
} else {
fullScript = PackagesManager.singleton().createFullScript(availablePackages, chosenMirror, command);
}
rScript = workSpace.createTextTempFile("RScriptTemp",
getFileExtension(), fullScript, false);
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_UnableToProduceScript()));
return false;
}
boolean successfullyRan = false;
try {
EnvVars envVars = build.getEnvironment(listener);
for (Map.Entry<String, String> e : build.getBuildVariables()
.entrySet()) {
envVars.put(e.getKey(), e.getValue());
}
if (launcher.launch().cmds(buildCommandLine(rScript))
.envs(envVars).stdout(listener).pwd(workSpace).join() == 1) {
successfullyRan = true;
}
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_CommandFailed()));
}
return successfullyRan;
} finally {
try {
if (rScript != null) {
rScript.delete();
}
} catch (IOException e) {
Util.displayIOException(e, listener);
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_UnableToDelete(rScript)));
} catch (Exception e) {
e.printStackTrace(listener.fatalError(Messages
.CommandInterpreter_UnableToDelete(rScript)));
}
}
}
public String[] buildCommandLine(FilePath script) {
return new String[] { "Rscript", script.getRemote() };
}
protected String getFileExtension() {
return fileExtension;
}
public List<String> getMirrorList() {
return mirrorList;
}
public void setMirrorList(List<String> mirrorList) {
this.mirrorList = mirrorList;
}
public String getChosenMirror() {
return chosenMirror;
}
public void setChosenMirror(String chosenMirror) {
this.chosenMirror = chosenMirror;
}
public ArrayList<AvailablePackage> getAvailablePackages() {
return availablePackages;
}
#Override
public ScriptBuildStepDescriptorImplementation getDescriptor() {
return (ScriptBuildStepDescriptorImplementation)super.getDescriptor();
}
#Extension
public static class ScriptBuildStepDescriptorImplementation extends
BuildStepDescriptor<Builder> {
private boolean showInstalled;
private String command;
private String chosenMirror;
private ArrayList<AvailablePackage> availablePackages;
public ScriptBuildStepDescriptorImplementation() {
load();
}
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
req.bindJSON(this, formData);
save();
return super.configure(req,formData);
}
#Override
public String getDisplayName() {
return "Advanced R script runner";
}
#Override
public boolean isApplicable(Class<? extends AbstractProject> jobType) {
return true;
}
public ListBoxModel doFillInstalledPackagesItems() {
ListBoxModel mirrors = new ListBoxModel();
Set<String> mirrorsList = PackagesManager.singleton()
.getInstalledPackages();
for (String entry : mirrorsList) {
mirrors.add(entry);
}
return mirrors;
}
public ListBoxModel doFillAvailablePackagesItems() {
ListBoxModel packages = new ListBoxModel();
List<String> packageList = PackagesManager.singleton().getAvailablePackages();
Set<String> alreadyInstalled = PackagesManager.singleton().getInstalledPackages();
for (String entry : packageList) {
if (!alreadyInstalled.contains(entry)) {
packages.add(entry);
}
}
return packages;
}
public ListBoxModel doFillMirrorsItems() {
ListBoxModel mirrors = new ListBoxModel();
String[] mirrorsList = MirrorManager.singleton().getMirrors();
int selected = 34;
for (int i = 0; i < mirrorsList.length; i++) {
String[] splitCurrent = mirrorsList[i].split(" - ");
if (chosenMirror != null && chosenMirror.equals(splitCurrent[1])) {
selected = i;
}
mirrors.add(splitCurrent[1], splitCurrent[0]);
}
mirrors.get(selected).selected = true;
return mirrors;
}
public boolean getShowInstalled() {
return showInstalled;
}
public void setShowInstalled(boolean showInstalled) {
this.showInstalled = showInstalled;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getChosenMirror() {
return chosenMirror;
}
public void setChosenMirror(String chosenMirror) {
this.chosenMirror = chosenMirror;
}
}
}
AvailablePackage.java
public class AvailablePackage extends AbstractDescribableImpl<AvailablePackage> {
private String name;
#DataBoundConstructor
public AvailablePackage(String availablePackages) {
this.name = availablePackages;
}
public String getName() {
return name;
}
#Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl)super.getDescriptor();
}
#Extension
public static class DescriptorImpl extends Descriptor<AvailablePackage> {
private String name;
public DescriptorImpl() {
load();
}
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
req.bindJSON(this, formData);
save();
return super.configure(req,formData);
}
public ListBoxModel doFillAvailablePackagesItems() {
return PackagesManager.singleton().getAvailablePackagesAsListBoxModel(name);
}
#Override
public String getDisplayName() {
return "";
}
public String getName() {
return name;
}
}
}
Sorry for the code formatting! First timer at stackoverflow code posting.
I think you may need to comment this line out
#Override
public boolean configure(StaplerRequest req, JSONObject formData) throws FormException {
req.bindJSON(this, formData);
save();
//return super.configure(req,formData);
return true;
}
as it will then save again but with no fields.
A good place to locate Jenkins plugin examples is in https://github.com/jenkinsci

Log request xml on error at OutFaultInterceptor for CXF Web Service

Is it possible to retrieve and log the request XML to a file at OutFaultInterceptor when I hit an error such as fail schema validation?
I have tried search the web but don't seems to be able to find much related to this.
Yest it is possible. I have wrote CxfOutInterceptor for getting XML of the message. Here is the code:
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CacheAndWriteOutputStream;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.io.CachedOutputStreamCallback;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CxfOutInterceptor extends AbstractPhaseInterceptor<Message> {
private static final Logger LOGGER = LoggerFactory.getLogger(CxfInInterceptor.class);
public CxfOutInterceptor() {
super(Phase.PRE_STREAM);
}
public static final String SINGLE_KEY = CxfOutInterceptor.class.getName() + ".Processed";
private static final int LIMIT = 10 * 1024 * 1024;
#Override
public void handleFault(Message message) {
LOGGER.trace("handleFault");
try {
internalHandleMessage(message);
} catch (Throwable ex) {
LOGGER.error("Exception thrown by internalHandleMessage: ", ex);
} finally {
LOGGER.trace("handleFault - end");
}
}
#Override
public void handleMessage(Message message) throws Fault {
LOGGER.trace("handleMessage");
try {
if (onceOnly(message)) {
LOGGER.debug("handled message previously");
return;
}
internalHandleMessage(message);
} finally {
LOGGER.trace("handleMessage - end");
}
}
private class LogCallback implements CachedOutputStreamCallback {
private final Message message;
private final OutputStream origStream;
public LogCallback(final Message msg, final OutputStream os) {
this.message = msg;
this.origStream = os;
}
#Override
public void onFlush(CachedOutputStream cos) {
}
#Override
public void onClose(CachedOutputStream cos) {
StringBuilder requestBuilder = new StringBuilder();
String encoding = (String) message.get(Message.ENCODING);
try {
writePayload(requestBuilder, cos, encoding);
//requestBuilder - is your actuall body of the message.
} catch (IOException ex) {
LOGGER.trace("Unable to write output stream to StringBuilder:\n" + ex.toString());
}
try {
cos.lockOutputStream();
cos.resetOut(null, false);
} catch (Exception ex) {
LOGGER.info("Ignoring exception");
}
message.setContent(OutputStream.class, origStream);
}
}
private void internalHandleMessage(Message message) {
final OutputStream os = message.getContent(OutputStream.class);
final Writer writer = message.getContent(Writer.class);
if (os == null && writer == null) {
return;
}
if (os == null) {
message.setContent(Writer.class, writer);
} else {
final CacheAndWriteOutputStream newOut = new CacheAndWriteOutputStream(os);
message.setContent(OutputStream.class, newOut);
newOut.registerCallback(new LogCallback(message, os));
}
}
private static boolean onceOnly(Message message) {
if (message.getExchange().containsKey(SINGLE_KEY)) {
return true;
} else {
message.getExchange().put(SINGLE_KEY, Boolean.TRUE);
return false;
}
}
private static void writePayload(StringBuilder builder, CachedOutputStream cos, String encoding)
throws IOException {
if (StringUtils.isEmpty(encoding)) {
cos.writeCacheTo(builder, LIMIT);
} else {
cos.writeCacheTo(builder, encoding, LIMIT);
}
}
}
You will get the XML of the message in onClose method. Refer to this comment: //requestBuilder - is your actuall XML of the message.