Implementing checksums in Java migrations - flyway

My Java migrations currently do not implement the MigrationChecksumProvider interface. I am wondering if they should.
I do not understand what role checksums play in Flyway. Would someone explain this to me, please? Moreover, if I implement MigrationChecksumProvider, how should a checksum be computed for a given migration?
Thank you.

Checksum are used for (accidental) change detection of migrations, which would invalidate the guarantee that the schema can be recreated exactly.
For Java migrations, the sky is the limit as to what you can do. It is therefore your call as to how you want to implement checksums to detect changes.

I think most of the time you want to calculate the checksum based upon the content of the class file in question. We are doing that like this....
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.CRC32;
import org.flywaydb.core.api.migration.jdbc.JdbcMigration;
import org.flywaydb.core.internal.util.FileCopyUtils;
import org.flywaydb.core.internal.util.logging.Log;
import org.flywaydb.core.internal.util.logging.LogFactory;
/**
* Utility class for checksum calculations.
*
*/
public final class Checksums {
private static final Log LOG = LogFactory.getLog(Checksums.class);
private Checksums() {
super(); // singleton
}
/**
* Calculates a checksum based on the given JdbcMigration. It builds the checksum from the byte content of the given
* migration class file using the same {#link CRC32} method as used by
* {#link org.flywaydb.core.internal.resolver.sql.SqlMigrationResolver}
*
* #param migration
* #return the checksum
*/
public static final Integer checksum(JdbcMigration migration) {
Integer checksum = null;
Class<?> migrationClass = migration.getClass();
String migrationClassFilePath = migrationClass.getName().replace(".", System.getProperty("file.separator")) + ".class";
InputStream inputStream = null;
try {
inputStream = ClassLoader.getSystemResourceAsStream(migrationClassFilePath);
byte[] classContent = FileCopyUtils.copyToByteArray(inputStream);
final CRC32 crc32 = new CRC32();
crc32.update(classContent);
checksum = (int) crc32.getValue();
} catch (IOException ioe) {
LOG.error("Problem calculating checksum for class " + migrationClass.getName(), ioe);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ioe) {
LOG.error("Problem closing input stream to " + migrationClassFilePath, ioe);
}
}
}
return checksum;
}
}
Then you can create a base class that looks something like
public abstract class JdbcMigrationWithChecksum implements JdbcMigration, MigrationChecksumProvider {
#Override
public Integer getChecksum() {
return Checksums.checksum(this);
}
}

Related

spring-kafka kafkaStreamsBuilder.getKafkaStreams() is null

Here is my code
The first bean is watching the messages on Topic.TRANSACTION_RAW and split one message into two and send them to Topic.TRANSACTION_INTERNAL
And the second bean is doing group and reducing and materialize it to the state store "StateStore.BALANCE".
The last one is to get the ReadOnlyKeyValueStore to read state from "ReadOnlyKeyValueStore".
#Configuration(proxyBeanMethods = false)
#EnableKafkaStreams
public class MyKafkaStreamsConfiguration {
#Bean
public KStream<String, BankTransaction> alphaBankKStream(StreamsBuilder streamsBuilder) {
JsonSerde<BankTransaction> valueSerde = new JsonSerde<>(BankTransaction.class);
KStream<String, BankTransaction> stream = streamsBuilder.stream(Topic.TRANSACTION_RAW,
Consumed.with(Serdes.String(), valueSerde));
stream.flatMap((k, v) -> {
List<BankTransactionInternal> txInternals = BankTransactionInternal.splitBankTransaction(v);
List<KeyValue<String, BankTransactionInternal>> result = new LinkedList<>();
result.add(KeyValue.pair(v.getFromAccount(), txInternals.get(0)));
result.add(KeyValue.pair(v.getToAccount(), txInternals.get(1)));
return result;
}).filter((k, v) -> !Constants.EXTERNAL_ACCOUNT.equalsIgnoreCase(k))
.to(Topic.TRANSACTION_INTERNAL, Produced.with(Serdes.String(), new JsonSerde<>()));
return stream;
}
#Bean
public KStream<String, BankTransactionInternal> alphaBankInternalKStream(StreamsBuilder streamsBuilder) {
JsonSerde<BankTransactionInternal> valueSerde = new JsonSerde<>(BankTransactionInternal.class);
KStream<String, BankTransactionInternal> stream = streamsBuilder.stream(Topic.TRANSACTION_INTERNAL,
Consumed.with(Serdes.String(), valueSerde));
KGroupedStream<String, Double> groupedByAccount = stream
.map((k,v) -> KeyValue.pair(k, v.getAmount()))
.groupBy((account, amount) -> account, Grouped.with(Serdes.String(), Serdes.Double()));
groupedByAccount.reduce(Double::sum,
Materialized.<String, Double, KeyValueStore<Bytes, byte[]>>as(StateStore.BALANCE)
.withValueSerde(Serdes.Double()));
return stream;
}
#Bean
public ReadOnlyKeyValueStore<String, Double> balanceStateStore(StreamsBuilderFactoryBean defaultKafkaStreamsBuilder) {
if (defaultKafkaStreamsBuilder == null) {
System.out.println("... defaultKafkaStreamsBuilder is null ...");
}
if (defaultKafkaStreamsBuilder.getKafkaStreams() == null) {
System.out.println("... defaultKafkaStreamsBuilder.getKafkaStreams() is null ...");
// this one got printed
}
ReadOnlyKeyValueStore<String, Double> store = defaultKafkaStreamsBuilder.getKafkaStreams().store(
StateStore.BALANCE,
QueryableStoreTypes.keyValueStore());
return store;
}
}
I always got NullPointException on defaultKafkaStreamsBuilder.getKafkaStreams().
Any idea what is wrong here? Thanks!
if (defaultKafkaStreamsBuilder.getKafkaStreams() == null) {
System.out.println("... defaultKafkaStreamsBuilder.getKafkaStreams() is null ...");
// this one got printed
}
This operation is not good to do during bean definition phase.
See its JavaDocs:
/**
* Get a managed by this {#link StreamsBuilderFactoryBean} {#link KafkaStreams} instance.
* #return KafkaStreams managed instance;
* may be null if this {#link StreamsBuilderFactoryBean} hasn't been started.
* #since 1.1.4
*/
public synchronized KafkaStreams getKafkaStreams() {
since you call this method far too early before a lifecycle start phase, you end-up with that error.
You should reconsider your logic in favor of SmartLifecycle.start() in the target service where you'd like to use that ReadOnlyKeyValueStore. So, you autowire over there this StreamsBuilderFactoryBean and call its getKafkaStreams() from the start() implementation.

How I get real time statistics in MOEA Framework?

I know there is the Instrumenter class, however this method outputs the data after the run finish. I would like to get (near) real-time data, like in the Symbolic Regression in the Demos.
Looking at its code, it seems I need to use the step method and try to imitate the runSingleSeed in Executor. Is there a better way? Some other class like Instrumenter but asynchronous. I cannot really find something similar online.
Just build a wrapper around the cycle (similar to the next one) and make it also a subject in an observer pattern.
import java.util.Properties;
import java.util.Arrays;
import java.text.DecimalFormat;
import org.moeaframework.core.Algorithm;
import org.moeaframework.core.Solution;
import org.moeaframework.core.Problem;
import org.moeaframework.core.Population;
import org.moeaframework.core.NondominatedPopulation;
import org.moeaframework.core.variable.EncodingUtils;
import org.moeaframework.core.spi.AlgorithmFactory;
import org.moeaframework.problem.misc.Kursawe;
public class Main{
public static void main(String[] args){
String algorithmName = "NSGAII";
Properties properties = new Properties();
properties.setProperty("populationSize", "100"); // to change properties
Problem problem = new Kursawe();
Algorithm algorithm = AlgorithmFactory.getInstance()
.getAlgorithm(algorithmName, properties, problem);
int maxGenerations = 100;
int generation = 0;
while( generation < maxGenerations ){
if( generation % 10 == 1 ){
System.out.println("Generation " + generation);
NondominatedPopulation paretoFront = algorithm.getResult();
// metrics
System.out.print("One of the pareto front: ");
System.out.println(toString(paretoFront.get(0)));
}
algorithm.step();
generation++;
}
algorithm.terminate();
System.out.println("Parento Front:");
for(Solution solution: algorithm.getResult()){
System.out.println(toString(solution));
}
export(algorithm.getResult());
}
private static String toString(Solution solution){
StringBuilder out = new StringBuilder();
double[] variables = EncodingUtils.getReal(solution);
double[] objectives = solution.getObjectives();
out.append("f");
out.append(doubleArrayToString(variables));
out.append(" = ");
out.append(doubleArrayToString(objectives));
return out.toString();
}
private static String doubleArrayToString(double[] array){
DecimalFormat format = new DecimalFormat("+#,##0.00;-#");
StringBuilder out = new StringBuilder();
out.append("[");
for(int i = 0; i < array.length-1; i++){
out.append(format.format(array[i]));
out.append(", ");
}
out.append(format.format(array[array.length-1]));
out.append("]");
return out.toString();
}
private static void export(Population population){
System.out.println();
for(Solution solution: population){
double[] objectives = solution.getObjectives();
System.out.println(String.format("%.3f,%.3f", objectives[0], objectives[1]));
}
}
}
Another option for the one indicated by Black Arrow, if you are using multithread, is to extentend AlgorithmFactory. For example:
public class MyAlgorithmFactory extends AlgorithmFactory {
private static Algorithm algorithm;
public Algorithm getGeneratedAlgorithm() {
return this.algorithm;
}
#Override
public Algorithm getAlgorithm(String name, Properties properties, Problem problem){
this.algorithm = super.getAlgorithm(name, properties, problem);
return algorithm;
}
}
Then you use this Factory on your Executor, for example:
MyAlgorithmFactory af = new MyAlgorithmFactory();
Executor executor = new Executor()
.usingAlgorithmFactory(af)
.withAlgorithm("NSGAII") //
.withProblem(yourProblemHere) //
.withMaxEvaluations(10000);
After this you can start the Executor on a separated thread, and call af.getGeneratedAlgorithm() to get the instance of Algorithm initialized by the Executor. From this Algorithm you can get, while the Executor is still running, the actual NondominatedPopulation to calc statistics.

AspectJ - Is is possible to extend an enum's value?

Say I have an enum
public enum E {A,B,C}
Is it possible to add another value, say D, by AspectJ?
After googling around, it seems that there used to be a way to hack the private static field $VALUES, then call the constructor(String, int) by reflection, but seems not working with 1.7 anymore.
Here are several links:
http://www.javaspecialists.eu/archive/Issue161.html (provided by #WimDeblauwe )
and this: http://www.jroller.com/VelkaVrana/entry/modify_enum_with_reflection
Actually, I recommend you to refactor the source code, maybe adding a collection of valid region IDs to each enumeration value. This should be straightforward enough for subsequent merging if you use Git and not some old-school SCM tool like SVN.
Maybe it would even make sense to use a dynamic data structure altogether instead of an enum if it is clear that in the future the list of commands is dynamic. But that should go into the upstream code base. I am sure the devs will accept a good patch or pull request if prepared cleanly.
Remember: Trying to avoid refactoring is usually a bad smell, a symptom of an illness, not a solution. I prefer solutions to symptomatic workarounds. Clean code rules and software craftsmanship attitude demand that.
Having said the above, now here is what you can do. It should work under JDK 7/8 and I found it on Jérôme Kehrli's blog (please be sure to add the bugfix mentioned in one of the comments below the article).
Enum extender utility:
package de.scrum_master.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;
public class DynamicEnumExtender {
private static ReflectionFactory reflectionFactory =
ReflectionFactory.getReflectionFactory();
private static void setFailsafeFieldValue(Field field, Object target, Object value)
throws NoSuchFieldException, IllegalAccessException
{
// let's make the field accessible
field.setAccessible(true);
// next we change the modifier in the Field instance to
// not be final anymore, thus tricking reflection into
// letting us modify the static final field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(field);
// blank out the final bit in the modifiers int
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(field, modifiers);
FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
fa.set(target, value);
}
private static void blankField(Class<?> enumClass, String fieldName)
throws NoSuchFieldException, IllegalAccessException
{
for (Field field : Class.class.getDeclaredFields()) {
if (field.getName().contains(fieldName)) {
AccessibleObject.setAccessible(new Field[] { field }, true);
setFailsafeFieldValue(field, enumClass, null);
break;
}
}
}
private static void cleanEnumCache(Class<?> enumClass)
throws NoSuchFieldException, IllegalAccessException
{
blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
blankField(enumClass, "enumConstants"); // IBM JDK
}
private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
throws NoSuchMethodException
{
Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
parameterTypes[0] = String.class;
parameterTypes[1] = int.class;
System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
return reflectionFactory.newConstructorAccessor(enumClass .getDeclaredConstructor(parameterTypes));
}
private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues)
throws Exception
{
Object[] parms = new Object[additionalValues.length + 2];
parms[0] = value;
parms[1] = Integer.valueOf(ordinal);
System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
}
/**
* Add an enum instance to the enum class given as argument
*
* #param <T> the type of the enum (implicit)
* #param enumType the class of the enum to be modified
* #param enumName the name of the new enum instance to be added to the class
*/
#SuppressWarnings("unchecked")
public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName) {
// 0. Sanity checks
if (!Enum.class.isAssignableFrom(enumType))
throw new RuntimeException("class " + enumType + " is not an instance of Enum");
// 1. Lookup "$VALUES" holder in enum class and get previous enum
// instances
Field valuesField = null;
Field[] fields = enumType.getDeclaredFields();
for (Field field : fields) {
if (field.getName().contains("$VALUES")) {
valuesField = field;
break;
}
}
AccessibleObject.setAccessible(new Field[] { valuesField }, true);
try {
// 2. Copy it
T[] previousValues = (T[]) valuesField.get(enumType);
List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
// 3. build new enum
T newValue = (T) makeEnum(
enumType, // The target enum class
enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
values.size(), new Class<?>[] {}, // could be used to pass values to the enum constuctor if needed
new Object[] {} // could be used to pass values to the enum constuctor if needed
);
// 4. add new value
values.add(newValue);
// 5. Set new values field
setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));
// 6. Clean enum cache
cleanEnumCache(enumType);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
}
Sample application & enum:
package de.scrum_master.app;
/** In honour of "The Secret of Monkey Island"... ;-) */
public enum Command {
OPEN, CLOSE, PUSH, PULL, WALK_TO, PICK_UP, TALK_TO, GIVE, USE, LOOK_AT, TURN_ON, TURN_OFF
}
package de.scrum_master.app;
public class Server {
public void executeCommand(Command command) {
System.out.println("Executing command " + command);
}
}
package de.scrum_master.app;
public class Client {
private Server server;
public Client(Server server) {
this.server = server;
}
public void issueCommand(String command) {
server.executeCommand(
Command.valueOf(
command.toUpperCase().replace(' ', '_')
)
);
}
public static void main(String[] args) {
Client client = new Client(new Server());
client.issueCommand("use");
client.issueCommand("walk to");
client.issueCommand("undress");
client.issueCommand("sleep");
}
}
Console output with original enum:
Executing command USE
Executing command WALK_TO
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant de.scrum_master.app.Command.UNDRESS
at java.lang.Enum.valueOf(Enum.java:236)
at de.scrum_master.app.Command.valueOf(Command.java:1)
at de.scrum_master.app.Client.issueCommand(Client.java:12)
at de.scrum_master.app.Client.main(Client.java:22)
Now you can either add an aspect with an advice executed after the enum class was loaded or just call this manually in your application before extended enum values are to be used for the first time. Here I am showing how it can be done in an aspect.
Enum extender aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Command;
import de.scrum_master.util.DynamicEnumExtender;
public aspect CommandExtender {
after() : staticinitialization(Command) {
System.out.println(thisJoinPoint);
DynamicEnumExtender.addEnum(Command.class, "UNDRESS");
DynamicEnumExtender.addEnum(Command.class, "SLEEP");
DynamicEnumExtender.addEnum(Command.class, "WAKE_UP");
DynamicEnumExtender.addEnum(Command.class, "DRESS");
}
}
Console output with extended enum:
staticinitialization(de.scrum_master.app.Command.<clinit>)
Executing command USE
Executing command WALK_TO
Executing command UNDRESS
Executing command SLEEP
Et voilà! ;-)

Doctrine OneToMany with aggregate field, how to keep it up to date?

In doctrine2 I have a OneToMany association: One Application <=> Many ApplicationCost
// Application.php
/**
* #ORM\OneToMany(targetEntity="ApplicationCost", mappedBy="application", orphanRemoval=true)
*/
protected $costs;
// ApplicationCost.php
/**
* #ORM\ManyToOne(targetEntity="Application", inversedBy="costs")
* #ORM\JoinColumn(name="application_id", referencedColumnName="id")
*/
protected $application;
In Application entity I have an agregate field sumCosts:
/**
* #ORM\Column(type="decimal", scale=2)
*/
protected $sumCosts;
Which is updated when addCost and removeCost are called:
// Application.php
public function addCost(ApplicationCost $cost)
{
if (!$this->costs->contains($cost)) {
$this->sumCosts += $cost->getBalance();
$this->costs[] = $cost;
$cost->setApplication($this);
}
return $this;
}
public function removeCost(ApplicationCost $cost)
{
if ($this->costs->contains($cost)) {
$this->sumCosts -= $cost->getBalance();
$this->costs->removeElement($cost);
}
}
Assuming User can edit already existing ApplicationCost's and can change it's parent Application, how do I make sure that this agregate field is up to date?
My approach is:
// ApplicationCost.php
public function setApplication(Application $application = null)
{
if ($this->application !== null) {
$this->application->removeCost($this);
}
if ($application !== null) {
$application->addCost($this);
}
$this->application = $application;
return $this;
}
Is that good? Or am I makeing here some huge mistake here and sumCosts may be out of sync?
EDIT: I've read Doctrine's Aggregate Fields cookbook and I have the versioning (and I use locking mechanism). My question is not about concurrency.
EDIT: I've created some tests
public function testSumCosts()
{
$app = new Application();
$costA = new ApplicationCost();
$costA->setBalance(150);
$costB = new ApplicationCost();
$costB->setBalance(100);
$costC = new ApplicationCost();
$costC->setBalance(50);
$app->addCost($costA);
$app->addCost($costB);
$app->addCost($costC);
$app->removeCost($costC);
$this->assertEquals(250, $app->sumCosts(), 'Costs are summed correctly');
}
public function testCostsChangeApplication()
{
$appA = new Application();
$appB = new Application();
$costA = new ApplicationCost();
$costA->setBalance(100);
$costB = new ApplicationCost();
$costB->setBalance(50);
$appA->addCost($costA);
$appB->addCost($costB);
$costA->setApplication($appB);
$costB->setApplication(null);
$this->assertEquals(0, $appA->sumCosts(), 'Costs are removed correctly');
$this->assertEquals(100, $appB->sumCosts(), 'Costs are added correctly');
}
And after adding $cost->setApplication($this); to addEntry both tests are green. Though I still wonder if I might have missed something.
Okay, I think I finally achieved desired result. I'll describe it for future reference and anyone who might have the same problem:
First of all correct the class
// Application.php
public function addCost(ApplicationCost $cost)
{
if (!$this->costs->contains($cost)) {
$this->sumCosts += $cost->getBalance();
}
$this->costs[] = $cost;
return $this;
}
public function removeCost(ApplicationCost $cost)
{
if ($this->costs->contains($cost)) {
$this->sumCosts -= $cost->getBalance();
}
$this->costs->removeElement($cost);
}
If you compare this to my original code you'll see that only updateing the agregate field is under condition. It does not hurt as collections can't hold duplicate elements and can't remove non existing elements.
Second of all, configure the cascade={all} option on inverse side of association (that is, on costs inside Application.php). So whenever you add/remove costs they are persisted too.
to be continued... (have to test what happens when i change application it from the owning side and persist only ApplicationCost -> will both old and new Application be updated?)

How can I check if a class belongs to Java JDK

I use an external library which return some List<?>.
I need to check if each object of this list is an Object of the JDK (String, int, Integer...).
Is this a proper solution?
List<?> list = externalLibrary.search(...);
for(clazz : list) {
if (clazz.getPackage().getName().startsWith("java.lang"))
// do something different
}
Is there a better one?
Depending on your definition of "object of the JDK" -- which could get quite fuzzy around the edges -- no, this isn't going to do it. The java.lang package is only a tiny part of all the classes included in the JDK.
You might check whether each object was loaded by the same ClassLoader that loaded java.lang.String -- i.e.,
if (theObject.getClass().getClassLoader() == "".getClass().getClassLoader()) ...
In general, a different ClassLoader will be used for system classes vs. application classes.
It is probably OK, just you have to check the following packages:
java
javax
com.sun
sun
probably others...
We use the below class to check if the classes belongs to JDK
public class JDKClass {
private static Set<String> CS = new HashSet<String>();
static {
try {
File file = new File(System.getProperty("java.home"),
"lib/classlist");
BufferedReader r = new BufferedReader(new FileReader(file));
String l;
while (true) {
l = r.readLine();
if (l == null) {
break;
} else {
CS.add(l.replace('/', '.'));
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static boolean contains(String o) {
return CS.contains(o) || o.startsWith("java") || o.startsWith("com.sun")
|| o.startsWith("sun") || o.startsWith("oracle")
|| o.startsWith("org.xml") || o.startsWith("com.oracle");
}
private JDKClass() {
}
}
You can use ClassLoader.getSystemResources and then check from what jar is the class loaded (f.g. if it comes from rt.jar).
You will get URL's such as:
jar:file:/C:/Users/user/.m2/repository/org/slf4j/slf4j-log4j12/1.6.1/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class
Example code taken from SLF4j:
private static String STATIC_LOGGER_BINDER_PATH =
"org/slf4j/impl/StaticLoggerBinder.class";
private static void singleImplementationSanityCheck() {
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class
.getClassLoader();
Enumeration paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader
.getResources(STATIC_LOGGER_BINDER_PATH);
}
List implementationList = new ArrayList();
while (paths.hasMoreElements()) {
URL path = (URL) paths.nextElement();
implementationList.add(path);
}
....
}
Personally I like class loader base answer. But it will return true also on StringBuilder. If you want to more narrow definition that is only "built-in" types, you can try to evaluate whether this is primitive type (such as int) or wrapper type (such as Integer) or String. You can write something like this:
import java.util.Map;
import java.util.TreeMap;
public class Utils {
private static Map<String, Class<?>> SUBST_MAP = new TreeMap<String, Class<?>>();
private static Map<String, Class<?>> SIMPLE_MAP = new TreeMap<String, Class<?>>();
static {
SUBST_MAP.put(Byte.class.getName(), Byte.TYPE);
SUBST_MAP.put(Short.class.getName(), Short.TYPE);
SUBST_MAP.put(Integer.class.getName(), Integer.TYPE);
SUBST_MAP.put(Long.class.getName(), Long.TYPE);
SUBST_MAP.put(Float.class.getName(), Float.TYPE);
SUBST_MAP.put(Double.class.getName(), Double.TYPE);
SUBST_MAP.put(Boolean.class.getName(), Boolean.TYPE);
SUBST_MAP.put(Character.class.getName(), Character.TYPE);
SIMPLE_MAP.put(String.class.getName(), Boolean.TRUE);
}
/**
* Gets the the class type of the types of the argument.
*
* if substPrimitiveWrapper is true,
* then if there is argument, that represent primitive type wrapper (such as Integer),
* then it will be substituted to primitive type (such as int).
* else no substitution will be done.
*
* #param arg object.
* #param substPrimitiveWrapper - wheteher to do primitive type substitution.
* #retrun class type.
*/
public static Class<?> getClassType(Object arg, boolean substPrimitiveWrapper){
Class<?> classType = null;
String className = null;
Class<?> substClass = null;
if(arg != null ){
//making default classType
classType = arg.getClass();
if(substPrimitiveWrapper){
className = classType.getName();
substClass = (Class<?>)SUBST_MAP.get(className);
if(substClass != null){
classType = substClass;
}
}
}
return classType;
}
/**
* This method consider JDK type any primitive type, wrapper class or String.
*
*
* #param arg object
* #return where arg is JDK type or now.
*/
public static boolean isJDKClass(Object arg){
Class<?> classType = getClassType(arg, true);
boolean isJDKClass = false;
if(classType!=null){
//if(String.class.equals(classType)){
// isJDKClass = true; //this is String, note that String is final
//}
assert classType!=null;
String className = classType.getName();
Boolean isFound = (Boolean)SIMPLE_MAP.get(className);
if(Boolean.TRUE.equals(isFound)){
isJDKClass = true; //this is predefined class
}
boolean isPrimitiveType = classType.isPrimitive();
if(isPrimitiveType){
isJDKClass = true; //this is primitive type or wrapper class
}
}
return isJDKClass;
}
}
You can also optionally add support for such classes like java.math.BigDecimal, java.util.Date, java.sql.Timestamp. Note, however, that they are not final, so I assumed that if somebody extended them even in the trivial way, it will not be considered as JDK class.
I think an easier solution is to thing of the problem this way:
write a method to identify all classes that are defined by you. In most cases, all user defined classes follow a pattern like com.something.something. Then if they do not belong to com.something.something, it is a JDK class

Resources