Adding custom attribute (HTML5) support to JSF 2.0 UIInput component - html

I am trying to write a renderer which would process the placeholder attribute on an <h:inputText> component.
I headed to this path after reading JSF 2.0 strips out needed HTML5 attributes and it seems correct. Here's my custom renderer
public class InputRenderer extends com.sun.faces.renderkit.html_basic.TextRenderer{
#Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
System.out.println("Rendering :"+component.getClientId());
String placeholder = (String)component.getAttributes().get("placeholder");
if(placeholder != null) {
ResponseWriter writer = context.getResponseWriter();
writer.writeAttribute("placeholder", placeholder, "placeholder");
}
super.encodeBegin(context, component);
}
#Override
public void decode(FacesContext context, UIComponent component) {
super.decode(context, component);
}
#Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
super.encodeEnd(context, component);
}
}
And this renderer is registered in faces config as
<render-kit>
<renderer>
<component-family>javax.faces.Input</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>com.example.renderer.InputRenderer</renderer-class>
</renderer>
</render-kit>
This gets registered fine, no issues there.
My intention is to process the placeholder attribute, insert it, and then delegate the processing to super. My above code doesn't work because I'm inserting the attribute at a wrong place. It must be inserted after writer.startElement('input') has executed. However, the startElement must be happening somewhere in the super's encodeBegin() method. So how do I insert a custom attribute ('placeholder' in this case) and then continue the execution flow?
NB: The above code does add a placeholder attribute but not to the input component that I intend to, It writes it to the parent of the Input (since I'm trying to write an attribute before the component itself is actually written in the stream, it applies the attribute to the current component)

This is my way. I added placeholder and data-theme attributes. If you want to add more attributes, you should just add its name to attributes array.
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.sun.faces.renderkit.html_basic.TextRenderer;
public class InputRender extends TextRenderer {
#Override
protected void getEndTextToRender(FacesContext context,
UIComponent component,
String currentValue)
throws java.io.IOException{
String [] attributes = {"placeholder","data-theme"};
ResponseWriter writer = context.getResponseWriter();
for(String attribute : attributes)
{
String value = (String)component.getAttributes().get(attribute);
if(value != null) {
writer.writeAttribute(attribute, value, attribute);
}
}
super.getEndTextToRender(context, component, currentValue);
}
}
You should add this to faces-config.xml file.
<render-kit>
<renderer>
<component-family>javax.faces.Input</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>your.package.InputRenderer</renderer-class>
</renderer>
</render-kit>

You can just override ResponseWriters startElement method, that method is only called once and then you can restore to the original responsewriter object.
import javax.faces.context.*;
import java.io.IOException;
public class InputRenderer extends com.sun.faces.renderkit.html_basic.TextRenderer{
// Put all of the attributes you want to render here...
private static final String[] ATTRIBUTES = {"required","placeholder"};
#Override
protected void getEndTextToRender(FacesContext context,
UIComponent component, String currentValue) throws IOException {
final ResponseWriter originalResponseWriter = context.getResponseWriter();
context.setResponseWriter(new ResponseWriterWrapper() {
#Override
// As of JSF 1.2 this method is now public.
public ResponseWriter getWrapped() {
return originalResponseWriter;
}
#Override
public void startElement(String name, UIComponent component)
throws IOException {
super.startElement(name, component);
if ("input".equals(name)) {
for (String attribute : ATTRIBUTES)
{
Object value = component.getAttributes().get(attribute);
if (value != null)
{
super.writeAttribute(attribute,value,attribute);
}
}
}
});
super.getEndTextToRender(context, component, currentValue);
context.setResponseWriter(originalResponseWriter); // Restore original writer.
}
}

And to override for MyFaces 2.0.8+
package com.hsop.abc.eld;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.apache.myfaces.renderkit.html.HtmlTextRenderer;
public class InputRenderer extends HtmlTextRenderer
{
#Override
protected void renderInputBegin(FacesContext context, UIComponent component)
throws IOException
{
// TODO Auto-generated method stub
super.renderInputBegin(context, component);
Object placeholder = component.getAttributes().get("placeholder");
if(placeholder != null) {
ResponseWriter writer = context.getResponseWriter();
writer.writeAttribute("placeholder", placeholder, "placeholder");
}
}
}

Related

Index of an invalid object within a list

Is there an equivalent to the getPropertyPath method in the Oval validation framework?
The example code below prints the properties of all nested invalid values of an object. I'd like to also print the index of the invalid object within the list but I'm not sure how this can be done in Oval.
With javax.validation, I can call ConstraintViolation#getPropertyPath but there doesn't seem to be an equivalent in Oval. Am I missing something?
The output is
list[].value: example.ValidationDemo$Child.value cannot be null
list[].value: example.ValidationDemo$Child.value cannot be null
Here's the code:
package example;
import java.util.List;
import net.sf.oval.ConstraintViolation;
import net.sf.oval.Validator;
import net.sf.oval.constraint.AssertValid;
import net.sf.oval.constraint.NotNull;
import net.sf.oval.context.FieldContext;
public class ValidationDemo {
public static void main(String[] args) {
Validator validator = new Validator();
validator.validate(new Parent())
.forEach(ValidationDemo::printViolation);
}
private static void printViolation(ConstraintViolation violation) {
printViolation(violation, "");
}
private static void printViolation(ConstraintViolation violation, String property) {
FieldContext fieldContext = (FieldContext) violation.getContext();
if (!property.isEmpty()) {
property += ".";
}
property += fieldContext.getField().getName();
if (List.class.isAssignableFrom(fieldContext.getField().getType())) {
property += "[]"; // How do I find the index of violation.getInvalidValue() within the list?
}
if (violation.getCauses() == null) {
System.out.format("%s: %s\n", property, violation.getMessage());
} else {
for (ConstraintViolation cause : violation.getCauses()) {
printViolation(cause, property);
}
}
}
public static class Parent {
#AssertValid
public final List<Child> list = List.of(new Child("value"),
new Child(null), new Child(null));
}
public static class Child {
#NotNull
public final String value;
public Child(String value) {
this.value = value;
}
}
}
This is currently not possible. This code part would need to be extended to keep track of the index.
ConstraintViolation.getContextPath() was added in 3.1.0

Unit test WCMUsePOJO class

I am writing unit test cases for following class which extends WCMUsePOJO. Now, this class is using a getSlingScriptHelper method shown below.
public class ConstantsServiceProvider extends WCMUsePojo {
private static final Logger logger = LoggerFactory.getLogger(ConstantsServiceProvider.class);
private String var1;
#Override
public void activate() throws Exception {
ConstantsService constantsService = getSlingScriptHelper().getService(ConstantsService.class);
if(constantsService != null) {
var1 = constantsService.getVar1();
}
}
public string getVar1() { return var1; }
}
The question is how do I mock getSlingScriptHelper method? Following is my unit test code.
public class ConstantsServiceProviderTest {
#Rule
public final SlingContext context = new SlingContext(ResourceResolverType.JCR_MOCK);
#Mock
public SlingScriptHelper scriptHelper;
public ConstantsServiceProviderTest() throws Exception {
}
#Before
public void setUp() throws Exception {
ConstantsService service = new ConstantsService();
scriptHelper = context.slingScriptHelper();
provider = new ConstantsServiceProvider();
provider.activate();
}
#Test
public void testGetvar1() throws Exception {
String testvar1 = "";
String var1 = provider.getVar1();
assertEquals(testvar1, var1);
}
}
The only thing that you should "have to"* mock is the SlingScriptHelper instance itself, so that it will mimic the dependency injection of the declared service.
Everything else (e.g. the Bindings instance) can be a concrete implementation, for example:
import org.apache.sling.api.scripting.SlingBindings;
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.junit.Test;
import javax.script.Bindings;
import javax.script.SimpleBindings;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ConstantsServiceProviderTest {
private SlingScriptHelper mockSling = mock(SlingScriptHelper.class);
private ConstantsServiceProvider constantsServiceProvider = new ConstantsServiceProvider();
private Bindings bindings = new SimpleBindings();
#Test
public void testFoo() throws Exception {
//Arrange
final String expected = "Hello world";
final ConstantsService testConstantsService = new TestConstantsService(expected);
when(mockSling.getService(ConstantsService.class)).thenReturn(testConstantsService);
bindings.put(SlingBindings.SLING, mockSling);
//Act
constantsServiceProvider.init(bindings);
//Assert
final String actual = constantsServiceProvider.getVar1();
assertThat(actual, is(equalTo(expected)));
}
class TestConstantsService extends ConstantsService {
String var1 = "";
TestConstantsService(String var1) {
this.var1 = var1;
}
#Override
String getVar1() {
return var1;
}
}
}
The entry point here, as you said above, is via the init() method of the WCMUsePojo superclass (as this method is an implementation of the Use.class interface, this test structure also works for testing that via that interface, even if you don't use WCMUsePojo directly.)
*this could be any type of test-double, not necessarily a mock.
You shouldn't create a mock for ConstantsServiceProvider.class if you want to unit-test it. Instead, you should create mocks of its internal objects. So:
Create real instance of ConstantsServiceProvider with new
Mock objects that are returned by getSlingScriptHelper().getService(.) methods. Usually, dependencies are provided (injected) to classes by some container like Spring or simply provided by other classes of your app using setters. In both cases mocks creation is easy.
If your current implementation doesn't allow this - consider refactoring.
You are testing void activate() method which doesn't return anything. So, you should verify calling constantsService.getVar1() method.
I strongly advice you to study Vogella unit-testing tutorial
Here one of possible solution.
The main idea is to have a real object of your class but with overridden getSlingScriptHelper() to return mocked scriptHelper.
I mocked the ConstantsService as well but may be not needed, I don't know your code.
public class ConstantsServiceProviderTest {
#Mock
public SlingScriptHelper scriptHelper;
#Test
public void getVar1ReturnsActivatedValue() throws Exception {
// setup
final String expectedResult = "some value";
// Have a mocked ConstantsService, but if possible have a real instance.
final ConstantsService mockedConstantsService =
Mockito.mock(ConstantsService.class);
Mockito.when(
mockedConstantsService.getVar1())
.thenReturn(expectedResult);
Mockito.when(
scriptHelper.getService(ConstantsService.class))
.thenReturn(mockedConstantsService);
// Have a real instance of your class under testing but with overridden getSlingScriptHelper()
final ConstantsServiceProvider providerWithMockedHelper =
new ConstantsServiceProvider() {
#Override
SlingScriptHelper getSlingScriptHelper() {
return scriptHelper;
}
};
// when
String actualResult = providerWithMockedHelper.getVar1();
// then
assertEquals(expectedResult, actualResult);
}
}

Override Primefaces Renderer

I'm developing a dynamic menu using Primefaces and JSF 2.2.
The problem is that it's not inserting the menu id. Looking primefaces' code it hits a code that will always be false:
BaseMenuRenderer:
protected boolean shouldRenderId(MenuElement element) {
if(element instanceof UIComponent)
return shouldWriteId((UIComponent) element);
else
return false;
}
TieredMenuRenderer:
writer.startElement("li", null);
if(shouldRenderId(submenu)) {
writer.writeAttribute("id", submenu.getClientId(), null);
}
So, I decided to override primefaces' TieredMenuRenderer, but my override constructor classe is called but the override method is never called.
Here's how I set my facesconfig.xml
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.TieredMenuRenderer</renderer-type>
<renderer-class>ui.jsf.TieredMenuRenderer</renderer-class>
</renderer>
</render-kit>
My override class:
public class TieredMenuRenderer extends org.primefaces.component.tieredmenu.TieredMenuRenderer {
#Override
protected void encodeElements(FacesContext context, AbstractMenu menu, List<MenuElement> elements) throws IOException {
System.out.println("----------- TEST --------------");
super.encodeElements(context, menu, elements);
}
Sysout is never print.
Does anyone know what i'm doing wrong?
Thanks!
Edit:
Add ID to DefaultMenuItem:
DefaultMenuItem item = new DefaultMenuItem();
item.setId(menuItem.getMenuId());// just return a string value.
Adding menu xhtml, the "menucontroller.model" is a primefaces MenuModel which I use a DefaultMenuModel :
Iterating over renderer kit, When I execute the following command, returns the correct renderer-type org.primefaces.component.TieredMenuRenderer
Iterator<String> renderKit = kit.getRendererTypes("org.primefaces.component");
When I execute the following code returns my qualified classname ui.jsf.TieredMenuRenderer#64baec0e:
Renderer renderer = kit.getRenderer("org.primefaces.component", "org.primefaces.component.TieredMenuRenderer");
First of all, My renderer was not overriding the correct renderer. I was overriding org.primefaces.component.tieredmenu.TieredMenuRenderer when I should override org.primefaces.component.MenubarRenderer.
Then, to correct the primefaces id problem I did the following in my rendered class:
#Override
protected boolean shouldRenderId(MenuElement element) {
return true;
}
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
AbstractMenu menu = (AbstractMenu) component;
encodeMarkup(context, menu);
encodeScript(context, menu);
}
EncodedEnd was calling "generateIds()" from primefaces which override my ids.
Full rendered class:
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.primefaces.component.menu.AbstractMenu;
import org.primefaces.model.menu.MenuElement;
public class MenuRenderer extends org.primefaces.component.menubar.MenubarRenderer {
#Override
protected boolean shouldRenderId(MenuElement element) {
return true;
}
#Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
AbstractMenu menu = (AbstractMenu) component;
encodeMarkup(context, menu);
encodeScript(context, menu);
}
}

How can I set a pattern in a DBAppender?

I using logback and put a pettern into a dbappender, but it doesn´t work.
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource
class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>net.sourceforge.jtds.jdbc.Driver</driverClass>
<url>jdbc:jtds:sqlserver://xxx.xxx.xxx.xx:1433/granica</url>
<user>java</user>
<password>java</password>
</connectionSource>
<encoder>
<pattern>%d{HH:mm:ss.SSS} - %msg%n</pattern>
</encoder>
</appender>
someone know how to fix that?
thanks in advance!
You can't put a pattern in a DBAppender:
"The DBAppender inserts logging events into three database tables in a format independent of the Java programming language.
*These three tables are logging_event, logging_event_property and logging_event_exception. They must exist before DBAppender can be used. Logback ships with SQL scripts that will create the tables."*
Alternative solution: If you realy need this pattern, then you should create an extra dataBbase table/view/sql that generaties that output for you, based on the existing tables. This can be done with a simple SQL, View or Triggers. Use standard SQL to create the output you need.
Good luck!
I managed to customize the formatted message easily enough by defining a couple of custom classes, one extending the DBAppender and the other implementing the ILoggingEvent interface:
package com.mypackage;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Map;
import org.slf4j.Marker;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.db.DBAppender;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.LoggerContextVO;
/**
*
* #author PJ_Finnegan
*
* To override the default DBAppender with a custom FORMATTED_MESSAGE
*
*/
public class MdcDbAppender extends DBAppender {
#Override
protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement)
throws Throwable {
// use a special event with custom formatted message
MdcLoggingEvent customEvent = new MdcLoggingEvent(event);
super.subAppend(customEvent, connection, insertStatement);
}
}
/**
*
* #author PJ_Finnegan
*
* To override the getFormattedMessage() method prepending MDC info
*
*/
class MdcLoggingEvent implements ILoggingEvent {
private ILoggingEvent mOrigEvent;
public MdcLoggingEvent(ILoggingEvent origEvent) {
mOrigEvent = origEvent;
}
#Override
public String getThreadName() {
return mOrigEvent.getThreadName();
}
#Override
public Level getLevel() {
return mOrigEvent.getLevel();
}
#Override
public String getMessage() {
return mOrigEvent.getMessage();
}
#Override
public Object[] getArgumentArray() {
return mOrigEvent.getArgumentArray();
}
#Override
public String getFormattedMessage() {
// add my MDC info as a prefix
String mdcVals[] = new String[] { "NA", "NA", "NA" };
Map<String, String> mpm = mOrigEvent.getMDCPropertyMap();
if (mpm != null) {
int i = 0;
for (String key : new String[] { "jobName", "jobInstanceId", "jobExecutionId" }) {
if (mpm.containsKey(key)) {
mdcVals[i] = mpm.get(key);
}
i++;
}
}
return String.format("[%s-%s-%s] ", mdcVals[0], mdcVals[1], mdcVals[2]) + mOrigEvent.getFormattedMessage();
}
#Override
public String getLoggerName() {
return mOrigEvent.getLoggerName();
}
#Override
public LoggerContextVO getLoggerContextVO() {
return mOrigEvent.getLoggerContextVO();
}
#Override
public IThrowableProxy getThrowableProxy() {
return mOrigEvent.getThrowableProxy();
}
#Override
public StackTraceElement[] getCallerData() {
return mOrigEvent.getCallerData();
}
#Override
public boolean hasCallerData() {
return mOrigEvent.hasCallerData();
}
#Override
public Marker getMarker() {
return mOrigEvent.getMarker();
}
#Override
public Map<String, String> getMDCPropertyMap() {
return mOrigEvent.getMDCPropertyMap();
}
#Override
public Map<String, String> getMdc() {
return mOrigEvent.getMdc();
}
#Override
public long getTimeStamp() {
return mOrigEvent.getTimeStamp();
}
#Override
public void prepareForDeferredProcessing() {
mOrigEvent.prepareForDeferredProcessing();
}
}
This way the LOGGING_EVENT.FORMATTED_MESSAGE column values on the DB will have the string [jobName-jobInstanceId-jobExecutionId] (whose value I had previously put in MDC) prepended:
TIMESTMP
FORMATTED_MESSAGE
1635255763784
[test-2760-3980] HikariPool-1 - Shutdown completed.
Of course you want to use your custom class in your logback.xml file instead of the stock DBAppender:
<!-- DB appender -->
<!-- use our custom class to override the FORMATTED_MESSAGE default pattern -->
<!-- <appender name="ORADB" class="ch.qos.logback.classic.db.DBAppender"> -->
<appender name="ORADB" class="com.mypackage.MdcDbAppender">
<connectionSource
...
Nothing prevents from overriding the ILoggingEvent.getFormattedMessage() method with more elaborated manipulations.

documentFilter.insert never called

I'm trying to set a documentFilter for my JTextArea. Having overriden the insert(...) method I admitted that it is never called. What's wrong? A piece of code:
package jaba;
import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
public class Main extends JFrame {
public Main() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(640, 480);
setLayout(new FlowLayout());
add(txt);
Document doc = txt.getDocument();
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).setDocumentFilter(new DocumentFilter() {
#Override
public void insertString(DocumentFilter.FilterBypass fb,
int offset, String string, AttributeSet att)
throws BadLocationException {
if (string.toLowerCase().contains("ass")) {
super.insertString(fb, offset, "###", att);
} else {
super.insertString(fb, offset, string, att);
}
}
});
} else {
txt.setText("error setting filter");
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Main().setVisible(true);
}
});
}
private JTextArea txt = new JTextArea(40, 40);
}
Having overriden the insert(...) method I admitted that it is never called.
Changes to the text in Swing components ultimately invoke the replace(...) method of the DocumentFilter.
The insertString(...) method is only invoked when you update the Document directly by using code like:
textField.getDocument().insertString(...);
So you need to make sure that you also override the replace() method in the DocumentFilter.