Skip to content

Instantly share code, notes, and snippets.

@juliofalbo
Last active October 7, 2019 10:53
Show Gist options
  • Save juliofalbo/0fb76615139b89c5331e9f33cd4ce859 to your computer and use it in GitHub Desktop.
Save juliofalbo/0fb76615139b89c5331e9f33cd4ce859 to your computer and use it in GitHub Desktop.
Custom Bean Utils
import java.beans.FeatureDescriptor;
import java.util.Objects;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
public class CustomBeanUtils {
private static final Logger log = LoggerFactory.getLogger(CustomBeanUtils.class);
protected static String[] getNullPropertyNames(Object source) {
final BeanWrapper wrappedSource = new BeanWrapperImpl(source);
return Stream.of(wrappedSource.getPropertyDescriptors())
.map(FeatureDescriptor::getName)
.filter(propertyName -> {
boolean aNull = true;
try {
aNull = Objects.isNull(wrappedSource.getPropertyValue(propertyName));
} catch (Exception e) {
log.warn("{}. Ignoring this property", e.getMessage());
}
return aNull;
})
.toArray(String[]::new);
}
public static <T> T copyProperties(Object source, Class<T> target) throws Exception {
T copied;
try {
copied = target.getConstructor().newInstance();
} catch (NoSuchMethodException e) {
throw new NoSuchMethodException("No default constructor defined for class " + target.getSimpleName());
}
BeanUtils.copyProperties(source, copied, getNullPropertyNames(source));
return copied;
}
}
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.Assert;
import org.junit.Test;
public class CustomBeanUtilsTest {
@Test
public void should_copy_all_fields() throws Exception {
AnotherObjectForTest anotherObjectForTest = getAnotherObjectForTest();
AnotherObjectForTest copied = CustomBeanUtils.copyProperties(anotherObjectForTest, AnotherObjectForTest.class);
Assert.assertEquals(anotherObjectForTest.getFirst(), copied.getFirst());
Assert.assertEquals(anotherObjectForTest.getSecond(), copied.getSecond());
}
@Test(expected = NoSuchMethodException.class)
public void should_return_an_exception_cause_the_target_class_has_no_def_constructor() throws Exception {
AnotherObjectForTestWithoutDefaultConstructor anotherObjectForTest = new AnotherObjectForTestWithoutDefaultConstructor("a", "b");
CustomBeanUtils.copyProperties(anotherObjectForTest, AnotherObjectForTestWithoutDefaultConstructor.class);
}
@Test
public void should_copy_only_filled_fields_with_one_invalid_attribute() throws Exception {
final TestAppender appender = new TestAppender();
final Logger logger = Logger.getRootLogger();
logger.addAppender(appender);
try {
AnotherObjectForTest anotherObjectForTest = getAnotherObjectForTest();
ObjectForTest info = getObjectForTest(anotherObjectForTest);
ObjectForTest copied = CustomBeanUtils.copyProperties(info, ObjectForTest.class);
Assert.assertEquals(info.getFirstString(), copied.getFirstString());
Assert.assertEquals(info.getSecondString(), copied.getSecondString());
Assert.assertEquals(info.getObjectType(), copied.getObjectType());
Assert.assertEquals(info.getSomeInteger(), info.getSomeInteger());
} finally {
logger.removeAppender(appender);
}
final List<LoggingEvent> log = appender.getLog();
final LoggingEvent firstLogEntry = log.get(0);
assertThat(firstLogEntry.getLevel(), is(Level.WARN));
assertThat(firstLogEntry.getMessage(), is("Invalid property 'withoutGetter' of bean class [com.juliofalbo.ObjectForTest]: Bean property 'withoutGetter' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?. Ignoring this property"));
assertThat(firstLogEntry.getLoggerName(), is("com.juliofalbo.CustomBeanUtils"));
}
private ObjectForTest getObjectForTest(AnotherObjectForTest anotherObjectForTest) {
ObjectForTest info = new ObjectForTest();
info.setFirstString(UUID.randomUUID().toString());
info.setSecondString(UUID.randomUUID().toString());
info.setObjectType(anotherObjectForTest);
info.setWithoutGetter("ccc");
return info;
}
private AnotherObjectForTest getAnotherObjectForTest() {
AnotherObjectForTest anotherObjectForTest = new AnotherObjectForTest();
anotherObjectForTest.setFirst("aaa");
anotherObjectForTest.setSecond("bb");
return anotherObjectForTest;
}
}
class TestAppender extends AppenderSkeleton {
private final List<LoggingEvent> log = new ArrayList<>();
@Override
public boolean requiresLayout() {
return false;
}
@Override
protected void append(final LoggingEvent loggingEvent) {
log.add(loggingEvent);
}
@Override
public void close() {
}
public List<LoggingEvent> getLog() {
return new ArrayList<>(log);
}
}
class ObjectForTest {
@SuppressWarnings("checkstyle:RedundantModifier")
public ObjectForTest() {
}
private String firstString;
private String secondString;
private AnotherObjectForTest objectType;
private String withoutGetter;
private Integer someInteger;
public String getFirstString() {
return firstString;
}
public void setFirstString(String firstString) {
this.firstString = firstString;
}
public String getSecondString() {
return secondString;
}
public void setSecondString(String secondString) {
this.secondString = secondString;
}
public AnotherObjectForTest getObjectType() {
return objectType;
}
public void setObjectType(AnotherObjectForTest objectType) {
this.objectType = objectType;
}
public void setWithoutGetter(String withoutGetter) {
this.withoutGetter = withoutGetter;
}
public Integer getSomeInteger() {
return someInteger;
}
public void setSomeInteger(Integer someInteger) {
this.someInteger = someInteger;
}
}
class AnotherObjectForTest {
private String first;
private String second;
@SuppressWarnings("checkstyle:RedundantModifier")
public AnotherObjectForTest() {
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
}
}
class AnotherObjectForTestWithoutDefaultConstructor {
private String first;
private String second;
@SuppressWarnings("checkstyle:RedundantModifier")
public AnotherObjectForTestWithoutDefaultConstructor(String first, String second) {
this.first = first;
this.second = second;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment