tag:blogger.com,1999:blog-25460383682492633712024-02-08T04:04:46.175+01:00Matej Tymes's WeblogMatej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.comBlogger21125tag:blogger.com,1999:blog-2546038368249263371.post-2961274001899533152016-11-11T02:34:00.001+01:002016-11-13T01:24:13.847+01:00Cassandra ZStandrad CompressorFacebook has released it's new open sourced compression algorithm which is quite efficient and fast at the same time. To read more about it please look <a href="https://code.facebook.com/posts/1658392934479273/smaller-and-faster-data-compression-with-zstandard/">here</a> or <a href="https://quixdb.github.io/squash-benchmark/unstable/">here</a>.<br />
<br />
It's actually so efficient that it seems to be a great candidate for Cassandra table compression.<br />
<br />
So to save you all the required work, here I've implemented an Cassandra Zstandard compressor that is ready to use: <a href="https://github.com/MatejTymes/cassandra-zstd">https://github.com/MatejTymes/cassandra-zstd</a>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-24062313067479421682016-06-15T20:56:00.003+02:002016-06-16T00:01:45.220+02:00Missing matched documents on searches and updates reproduction<a href="https://engineering.meteor.com/mongodb-queries-dont-always-return-all-matching-documents-654b6594a827#.lhm75n88b">This blog</a> recently exposed an interesting concurrency caveat related to MongoDB where matching documents won't be found (or updated) if they are being reindexes.<br />
<br />
The only part in the entry I was missing is a way how to reproduce this issue. So I decided to create a test which you can test against your version of MongoDB to check if it is still a problem.<br />
<br />
Here it is:<br />
<br />
<pre class="brush:java">package co.uk.matejtymes.mongodb;
import com.mongodb.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import static com.mongodb.BasicDBObjectBuilder.start;
import static java.util.Arrays.asList;
import static java.util.UUID.randomUUID;
import static java.util.concurrent.Executors.newFixedThreadPool;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertThat;
public class ReindexFailureTest {
private static final String STATE_FIELD = "state";
private static final Random RANDOM = new Random();
private DBCollection coll;
@Before
public void setUp() throws Exception {
// todo: provide connection details for your mongoDB instance
MongoClient mongo = new MongoClient("localhost", 27017);
DB db = mongo.getDB("testDb");
coll = db.getCollection("indexTest");
}
@After
public void tearDown() throws Exception {
coll.drop();
}
@Test
public void shouldFindAllMatchingItemsEvenWhenRecalculatingIndex()throws Exception {
int docCount = 250;
int concurrentUpdates = 40;
int attemptsCount = 1_000;
List<String> stateValues = asList("Active", "Inactive");
coll.createIndex(new BasicDBObject(STATE_FIELD, 1));
List<String> allIds = createNDocumentsWithState(docCount, stateValues);
ExecutorService executor = newFixedThreadPool(concurrentUpdates);
for (int attempt = 1; attempt <= attemptsCount; attempt++) {
System.out.println(attempt + ". attempt");
CountDownLatch beginLatch = new CountDownLatch(concurrentUpdates + 1);
CountDownLatch endLatch = new CountDownLatch(concurrentUpdates + 1);
for (int update = 0; update < concurrentUpdates; update++) {
executor.submit(() -> updateState(pickRandomItem(allIds), stateValues, beginLatch, endLatch));
}
List<String> foundIds = findDocumentsInState(stateValues, beginLatch, endLatch);
Set<String> uniqueIds = new HashSet<>();
Set<String> duplicateIds = new HashSet<>();
Set<String> missingIds = new HashSet<>(allIds);
for (String foundId : foundIds) {
if (uniqueIds.contains(foundId)) {
duplicateIds.add(foundId);
}
uniqueIds.add(foundId);
missingIds.remove(foundId);
}
if (!missingIds.isEmpty()) {
System.err.println(missingIds.size() + ". missingIds: " + missingIds);
}
if (!duplicateIds.isEmpty()) {
System.err.println(duplicateIds.size() + ". duplicateIds: " + duplicateIds);
}
assertThat(foundIds, hasSize(allIds.size()));
assertThat(missingIds, hasSize(0));
assertThat(duplicateIds, hasSize(0));
}
executor.shutdown();
executor.awaitTermination(3, SECONDS);
}
@Test
public void shouldUpdateAllMatchingItemsEvenWhenRecalculatingIndex()throws Exception {
int docCount = 250;
int concurrentUpdates = 40;
int attemptsCount = 1_000;
List<String> stateValues = asList("Active", "Inactive");
coll.createIndex(new BasicDBObject(STATE_FIELD, 1));
List<String> allIds = createNDocumentsWithState(docCount, stateValues);
ExecutorService executor = newFixedThreadPool(concurrentUpdates);
for (int attempt = 1; attempt <= attemptsCount; attempt++) {
System.out.println(attempt + ". attempt");
String fieldToUpdate = "field" + attempt;
Object valueToSet = true;
CountDownLatch beginLatch = new CountDownLatch(concurrentUpdates + 1);
CountDownLatch endLatch = new CountDownLatch(concurrentUpdates + 1);
for (int update = 0; update < concurrentUpdates; update++) {
executor.submit(() -> updateState(pickRandomItem(allIds), stateValues, beginLatch, endLatch));
}
BasicDBObject query = new BasicDBObject(STATE_FIELD, new BasicDBObject("$in", stateValues));
BasicDBObject update = new BasicDBObject("$set", new BasicDBObject(fieldToUpdate, valueToSet));
beginLatch.countDown();
int n = coll.updateMulti(query, update).getN();
endLatch.countDown();
List<String> updatedIds = new ArrayList<>();
coll.find(new BasicDBObject(fieldToUpdate, valueToSet)).forEach(
dbObject -> updatedIds.add((String) dbObject.get("_id"))
);
Set<String> missingIds = new HashSet<>(allIds);
missingIds.removeAll(updatedIds);
if (!missingIds.isEmpty()) {
System.err.println(missingIds.size() + ". missingIds: " + missingIds);
}
if (n != allIds.size()) {
System.err.println("n = " + n);
}
if (updatedIds.size() != allIds.size()) {
System.err.println("updateIds = " + updatedIds.size());
}
assertThat(n, equalTo(allIds.size()));
assertThat(updatedIds, hasSize(allIds.size()));
assertThat(missingIds, hasSize(0));
}
executor.shutdown();
executor.awaitTermination(3, SECONDS);
}
/* ====================== */
/* --- helper methods --- */
/* ====================== */
private List<String> createNDocumentsWithState(int docCount, List<String> stateValues) {
List<String> ids = new ArrayList<>();
for (int i = 0; i < docCount; i++) {
String id = randomUUID().toString();
String state = stateValues.get(i % stateValues.size());
DBObject dbObject = start()
.add("_id", id)
.add(STATE_FIELD, state)
.get();
coll.insert(dbObject);
ids.add(id);
}
return ids;
}
private void updateState(String id, List<String> stateValues, CountDownLatch beginLatch, CountDownLatch endLatch) {
BasicDBObject query = new BasicDBObject("_id", id);
String oldStateValue = (String) coll.find(query).next().get(STATE_FIELD);
String newStateValue = stateValues.stream().filter(state -> !state.equals(oldStateValue)).findFirst().get();
BasicDBObject update = new BasicDBObject("$set", new BasicDBObject(STATE_FIELD, newStateValue));
beginLatch.countDown();
coll.update(query, update);
endLatch.countDown();
}
private List<String> findDocumentsInState(List<String> stateValues, CountDownLatch beginLatch, CountDownLatch endLatch) {
BasicDBObject query = new BasicDBObject(STATE_FIELD, new BasicDBObject("$in", stateValues));
Iterator<DBObject> dbObjects = coll.find(query).iterator();
List<String> foundIds = new ArrayList<>();
beginLatch.countDown();;
while (dbObjects.hasNext()) {
foundIds.add((String) dbObjects.next().get("_id"));
}
endLatch.countDown();
return foundIds;
}
private static <T> T pickRandomItem(List<T> values) {
return values.get(RANDOM.nextInt(values.size()));
}
}</pre>
Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-33634038810064353892016-04-29T03:13:00.004+02:002016-05-15T01:27:43.226+02:00Executor that notifies you when task finishJava Executors don't let you know when all tasks are finished or to be more precise, don't block you until the tasks are finished. You could call shutdown() on them and then awaitTermination(), but this way you can't reuse the executor anymore, which is not great. This is why I create a class Runner that can accomplish this. It's used like this:<br />
<br />
<pre class="brush:java">Runner runner = Runner.runner(10);
runner.runIn(2, SECONDS, runnable);
runner.run(runnable);
runner.waitTillDone(); // blocks until all tasks are finished (or failed)
// and reuse it
runner.runIn(500, MILLISECONDS, callable);
runner.waitTillDone();
runner.shutdownAndAwaitTermination();
</pre>
<br />
The code for it can be found here:<br />
<br />
<a href="https://github.com/MatejTymes/JavaFixes">https://github.com/MatejTymes/JavaFixes</a><br />
<br />
Hope this will helpMatej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-77900757726015083672016-04-19T05:19:00.001+02:002016-05-08T03:12:44.198+02:00End of BigDecimal BStingBigDecimal is so close to being great until you face few things which make you just scream.<br />
<ul>
<li>values are sometimes not equal (although you would like them to be):</li>
</ul>
<pre class="brush:java"> // yes: -1.2 is not equal to -1.20
assertThat(new BigDecimal("-1.2").equals(new BigDecimal("-1.20")), is(false));
</pre>
<br />
maybe this seems harmless but once your test will start to fail because the actual and expected domain object (using BigDecimal) are not equal although they are, you will just ask your self: why do we have to go trough this?<br />
<br />
Also have you ever been paring with somebody on an interview, where the candidate told you that we should use BigDecimal for this interest rate calculation, but in the end you both decided not to do it - as the interview is not long enough - really BigDecimal adds aditional 10 to 30 minutes to inteview excercise as you have to deal with the equals method - and THIS is the STANDARD!!!<br />
<ul>
<li>equals is bad, but hashCode is even worse</li>
</ul>
<pre class="brush:java"> // yes: hashCodes for -1.2 and -1.20 are not the same as well
assertThat(new BigDecimal("-1.2").hashCode(), is(not(new BigDecimal("-1.20").hashCode())));
</pre>
<br />
but why would that even matter? Well, if you ever decide to make BigDecimal key for a HashMap, than a situation might occur where you won't find any value for your number, as their hashCode won't match.<br />
<ul>
<li>the ways how to create BigDecimal are not unified at all (depending on your originating value there are few different ways how you create it)</li>
</ul>
<pre class="brush:java"> new BigDecimal("-1.20"); // from string
BigDecimal.valueOf(-120L, 2); // from long
new BigDecimal(BigInteger.valueOf(-120L), 2); // from BigInteger
// you should not create BigDecimal from float or double as you might get really weird value (because of transition from binary to decimal form)
</pre>
<br />
I assumed that this is going to be fixed as this problems were present for many years, but it seems this is the design we'll have to live with.<br />
<br />
This is why I decided to create a rewrite of BigDecimal called Decimal which you can find on this page (currently I'm finalizing the implementation):<br />
<br />
<a href="https://github.com/MatejTymes/JavaFixes">https://github.com/MatejTymes/JavaFixes</a><br />
<br />
It provides few advantages over BigDecimal<br />
<ul>
<li>unified creation using two possible factory methods (one more readable decimal(...), one shorter d(...))</li>
<li>fixed equals</li>
<li>fixed hashCode:</li>
</ul>
<pre class="brush:java"> // equals now works
assertThat(decimal("-1.2").equals(decimal("-1.200")), is(true));
assertThat(d("-1.2").equals(d("-1.200")), is(true));
// and surprisingly hashCode as well
assertThat(d("-1.2").hashCode(), is(d("-1.20").hashCode()));</pre>
<ul>
<li>also the creation approach is always the same</li>
</ul>
<pre class="brush:java"> decimal("-1.20"); // from string
decimal(-120L, 2); // from long
decimal(BigInteger.valueOf(-120L), 2); // from BigInteger
// you are not able to create Decimal from float or double but have to transform them into string first - otherwise you might get surprising values
</pre>
<ul>
<li>you can use underscores in the numbers to make them more readable</li>
</ul>
<pre class="brush:java"> Decimal value = d("-125_550_00.00"); // using underscores as you can use in java numbers
</pre>
<br />
The Decimal is an abstract class currently extended by two implementations: LongDecimal - if value can be backed by long and HugeDecimal - backed by BigInteger (for all other numbers). You can't address them directly, but the library handles the transition between these types seamlessly while you're calling arithmetic operations.<br />
<br />
And that's it. Please let me know if you can think of any other improvements, or just what you feel about this. I would be happy to hear your thoughts.Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com1tag:blogger.com,1999:blog-2546038368249263371.post-61088164425434309112014-10-15T00:30:00.001+02:002014-11-05T20:59:08.544+01:00WebDriver fix for UnreachableBrowserExceptionThis page is a summary of <b>3 different fixes</b> for <b>3 different</b> ways how to get <b>UnreachableBrowserException</b>-s while using WebDrivers
<br /><br/>
<h2>1. Caused by: java.net.SocketException: Software caused connection abort: recv failed</h2>
<br />
To fix <a href="https://github.com/detro/ghostdriver/issues/140">this issue</a> just <b><i>replace</i></b> usage of <b><i>PhantomJSDriver</i></b> with following <b><i>FixedPhantomJSDriver</i></b>:<br />
<br />
<pre class="brush:java">public class FixedPhantomJSDriver extends PhantomJSDriver {
private final int retryCount = 2;
public FixedPhantomJSDriver() {
}
public FixedPhantomJSDriver(Capabilities desiredCapabilities) {
super(desiredCapabilities);
}
public FixedPhantomJSDriver(PhantomJSDriverService service, Capabilities desiredCapabilities) {
super(service, desiredCapabilities);
}
@Override
protected Response execute(String driverCommand, Map<String, ?> parameters) {
int retryAttempt = 0;
while (true) {
try {
return super.execute(driverCommand, parameters);
} catch (UnreachableBrowserException e) {
retryAttempt++;
if (retryAttempt > retryCount) {
throw e;
}
}
}
}
}
</pre>
So in summary:
<br />
<pre style="background-color: #f7f7f7; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; box-sizing: border-box; color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; line-height: 1.45; margin-bottom: 10px; overflow: auto; padding: 10px; word-wrap: normal;"><code style="background: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: inherit; margin: 0px; padding: 0px; word-break: normal; word-wrap: normal;">org.openqa.selenium.remote.UnreachableBrowserException: Error communicating with the remote browser. It may have died.</code></pre>
caused by:
<pre style="background-color: #f7f7f7; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; box-sizing: border-box; color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; line-height: 1.45; margin-bottom: 10px; overflow: auto; padding: 10px; word-wrap: normal;"><code style="background: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: inherit; margin: 0px; padding: 0px; word-break: normal; word-wrap: normal;">Caused by: java.net.SocketException: Software caused connection abort: recv failed</code></pre>
(the recv failed is important)
<br />
happens (from my investigations) when connection is closed prematurely. In our case it seemed to be caused by ssl certificate handling in java (I'm still investigating this) and is extremely random. Luckily <b>all http traffic</b> is <b>handled by</b> the <b>execute method</b>. So by overriding it and <b>adding</b> simple <b>retry functionality</b> you provide a working workaround solution (it helped us on our project as we never had a failing/flaky tests again).
<br />
Although this is a specific implementation for PhantomJS driver, the <b>same approach</b> should <b>work for other drivers</b> as well.
<br/><br/>
<h2>2. Caused by: java.net.SocketTimeoutException: Read timed out</h2>
<br/>
But there is still chance that you have <b>different symptoms</b> and your browser just hangs for some time until it throws:
<pre style="background-color: #f7f7f7; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; box-sizing: border-box; color: #333333; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; font-size: 12px; line-height: 1.45; margin-bottom: 10px; overflow: auto; padding: 10px; word-wrap: normal;"><code style="background: transparent; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 0px; box-sizing: border-box; display: inline; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: inherit; margin: 0px; padding: 0px; word-break: normal; word-wrap: normal;">Caused by: java.net.SocketTimeoutException: Read timed out</code></pre>
If you're working on a Windows machine, the chances are that you've <b>reached limit of possible open connections</b>. This normally happens, because Selenium creates a lot of connections and Windows keeps them opened/cached even when java triggered a close connection command.
To <b>fix</b> this issue you need to <b>change Windows registry values</b> under:
<pre>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters</pre>
you need to set/create two DWORD values:
<pre>MaxUserPort = 32768
TcpTimedWaitDelay = 30</pre>
<b>MaxUserPort</b> will increase the <b>limit of possible open connections</b> (you can select any value between 5000-65534, the higher the better). <b>TcpTimedWaitDelay</b> makes sure that <b>windows will close stale connections</b> (already closed by java) <b>after 30 seconds</b> (can't be set to lower, but without setting this the default value is 4 minutes !!!). In most cases this should fix your "hang" issue.
<br/><br/>
<h2>3. When your test still hangs for 3 hours until it fails</h2>
<br/>
Unfortunately there is still a small chance that you have issues where your test will get stuck for 3 hours !!! The reason for this is that the <b>HttpClientFactory</b> in selenium <b>has hardcoded socket timeout to 3 hours</b>, and although there is a proposed fix to the selenium core code, until it will be accepted there is no way how to change it using normal means.
For those who unfortunatelly must bear the pain, here is how you use my hacky, but <b>working workaround</b> to this problem:
<pre class="brush:java">public class FixExample {
public static void main(String[] args) {
// this is my custom workaround
HttpParamsSetter.setSoTimeout(60 * 1000); // set socket timeout to 1 minute
// and here goes your custom code
WebDriver driver = new PhantomJSDriver();
...
driver.quit();
}
}</pre>
Here you can find the code that does the magic (this works because fields <b>HttpCommandExecutor.httpClientFactory</b> and <b>HttpClientFactory.client</b> are <b>static fields</b> that are initialized only once):
<pre class="brush:java">import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.openqa.selenium.remote.HttpCommandExecutor;
import org.openqa.selenium.remote.internal.HttpClientFactory;
import java.lang.reflect.Field;
public class HttpParamsSetter {
@SuppressWarnings("deprecation")
public static void setSoTimeout(int soTimeout) {
HttpClientFactory factory = getStaticValue(HttpCommandExecutor.class, "httpClientFactory");
if (factory == null) {
factory = new HttpClientFactory();
}
DefaultHttpClient httpClient = (DefaultHttpClient) factory.getHttpClient();
HttpParams params = httpClient.getParams();
HttpConnectionParams.setSoTimeout(params, soTimeout);
httpClient.setParams(params);
setStaticValue(HttpCommandExecutor.class, "httpClientFactory", factory);
}
private static <T> T getStaticValue(Class<?> aClass, String fieldName) {
Field field = null;
Boolean isAccessible = null;
try {
field = aClass.getDeclaredField(fieldName);
isAccessible = field.isAccessible();
field.setAccessible(true);
return (T) field.get(null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} finally {
if (field != null && isAccessible != null) {
field.setAccessible(isAccessible);
}
}
}
private static void setStaticValue(Class<HttpCommandExecutor> aClass, String fieldName, Object value) {
Field field = null;
Boolean isAccessible = null;
try {
field = aClass.getDeclaredField(fieldName);
isAccessible = field.isAccessible();
field.setAccessible(true);
field.set(null, value);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} finally {
if (field != null && isAccessible != null) {
field.setAccessible(isAccessible);
}
}
}
}</pre>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com20tag:blogger.com,1999:blog-2546038368249263371.post-65321100742976061762014-04-17T00:40:00.003+02:002014-05-02T02:53:06.913+02:00Trading: Call Options - introduction<h3>
Investment oportunity</h3>
<br />
Imagine that you would meet somebody who is from the future.<br />
<br />
And that somebody would tell you that each gold bar which<br />
now costs 100$<br />
will cost 115$ in a years time.<br />
<br />
And imagine that for some reason you would believe him (as for example he looks exactly like you just 10 years older).<br />
<br />
Now stop for a moment and think: What would you do regarding this gold situation?<br />
<br />
<h3>
Possible solution</h3>
<br />
It seems that a wise choice would be to invest all your money into gold and then sell it in a year.<br />
<br />
You have 5,000$ in savings and the bank is willing to lend you maximum amount of 250,000$ with a generous interest rate of 5% per year.<br />
<br />
So, let's do some calculations.<br />
<br />
How much money do we actually have? This is a simple formula:<br />
our savings + borrowed money = 5,000 + 250,000 = 255,000 $<br />
<br />
So, now the question is how many gold bars can we buy for this amount. To get this value we just divide our total amount by today's price of one gold bar:<br />
amount we have / today's price of gold bar = 255,000 / 100 = 2,550 gold bars<br />
<br />
2,550 gold bars seems like a quite nice amount. But it would be good to actually know how much money we'll get for it in a year. To find it out just use this formula:<br />
amount of gold bars * price of gold bar in a year = 2,550 * 115 = 293,250 $<br />
<br />
It seems that we have earned something. But how much actually is it?<br />
<br />
First find out how much we have gained above our invested amount:<br />
earned amount - invested amount = 293,250 - 255,000 = 38,250 $<br />
<br />
This seems like a quite nice sum but this is still not our net/clear income as we still need to pay the interest for the borrowed money which is:<br />
borrowed amount * interest rate percentage / 100 = 250,000 * 5 / 100 = 12,500 $<br />
<br />
Once we pay this amount as well we'll get the clear income:<br />
investment return - interest for borrowed money = 38,250 - 12,500 = 25,750 $<br />
<br />
So with our savings (5,000$) and borrowed money (250,000$) we've been able to earn 25,750$. As we return the borrowed money but keep the savings we now have:<br />
clear income + savings = 25,750 + 5,000 = 30,750$<br />
<br />
This seems to be quite a nice year, don't you think?<br />
<br />
<h3>
Return percentage</h3>
<br />
Lets do some deeper analysis of how good we actually did.<br />
<br />
At first we'll look what was the best/maximum percentage of return we could have achieved using this method.<br />
<br />
To get this we'll find the difference of future gold price and its current price value:<br />
price in a year - price today = 115 - 100 = 15 $<br />
<br />
And from this we find how big increase it actually is:<br />
(price difference / price today) * 100 = (15 / 100) * 100 = 15%<br />
<br />
So 15% is the maximum we could have gained if we would use only our savings (and there would be no need to pay interest for borrowed money).<br />
Which means that for each 100 $ we would earn 15% which is 15 $.<br />
<br />
So if we would use just our savings we would gain: 5,000 * 15 / 100 = 750 $.<br />
<br />
But as we were borrowing some money and we paid some interest rate, lets find out how good we actually did. For this purposes we use this calculation:<br />
(net income / invested amount) * 100 = (25,750 / 255,000) * 100 = 10.098 %<br />
<br />
So lets summarize:<br />
With using just our savings (5,000 $), we would have the highest return percentage (15%) but as only small amount would be invested, we would gained lower total income as well = 750 $.<br />
<br />
With the borrowed money our total investment has increased rapidly (255,000 $) and although the return percentage was lower (10.098% - which is still regarded as good investment) our total income was much higher = 25,750 $.<br />
<br />
It seems that it was a good decision to use not just our own savings but to borrow some money from bank as well.<br />
<br />
Congratulations.<br />
<br />
<h3>
Other options - Call option</h3>
<br />
Now the question is: Can we do better than this?<br />
<br />
Imagine that during our application for the bank loan, we would state that: I would like to buy gold bars because I expect their value to rise in a years time.<br />
<br />
Our friendly banker would for some reason stop for a while. After a moment of deep thoughts he would tell us:<br />
"I would have a proposal for you. Of course we will lend you the money. You're a good client as you already have 5,000 $ of your own, so I'm sure you'll be able to handle additional 250,000 $.<br />
But why would you buy all of this gold right now, when instead I would offer you something better.<br />
<br />
I can offer you the possibility to buy gold bars from us in a year for the same price as they costs today. So if the price of gold will be higher then today you'll earn some money. And the best thing is that if the price is lower there is no obligation for you to buy the gold bars from us, so you'll lose no more money at all. This seems really promising doesn't it? And to do so, all you have to do is sign this contract and pay the initial fee of 8 $ for each gold bar you'll be able to buy.""<br />
<br />
Ok, so our friendly banker has offered us the possibility (but not the obligation) to buy something in the future for an agreed price. This is a contract which is normally known as 'Call Option'. The fee you have to pay for signing/entering this contract is known as 'Option Premium'.<br />
<br />
So we have a new possibility now. But is it worth using this possibility? Let's do some math again to find it out.<br />
<br />
First we'll find out how many 'Call options' can we actually buy:<br />
amount we have / option premium = 255,000 / 8 = 31,875 pieces<br />
<br />
Now we have some amount of 'Options'. Lets imagine, that instead of buying the gold in years time for 100 $ and selling it back for 115 $ there are people who would buy our contracts for the price difference 15 $. So how much would we gain in the end:<br />
count of option calls * difference in gold price = 31,875 * 15 = 478,125 $<br />
<br />
OMG, this is a horrendous sum. But remember we still need to subtract our invested amount and interest we need to pay for borrowed money to get the net/real income:<br />
investment return - invested amount - interest for borrowed money = 478,125 - 255,000 - 12,500 = 210,625 $<br />
<br />
Holy macarony. This is way better than the previous 25,750 $. Lets see the actual percentage of return:<br />
(net income / invested amount) * 100 = (210,625 / 255,000) * 100 = 82.598 %<br />
<br />
This seems like the best possible approach. We received much more money (210,625 $ instead of 25,750 $) as well as higher return percentage (82.598 % instead of 10.098 %).<br />
<br />
The funny thing is that we could never achieve something like this with just buying gold on its own. Instead we were buying something that was derived from buying the gold. An option to buy the gold. Because of that we say that 'Call Option' is a 'Derivative'. There are many more 'Derivatives' that are being used, but for now just remember that 'Call Option' is one of them.<br />
<br />
<h3>
Negative scenario - possible losses explained</h3>
<br />
You might ask yourself. Why would the friendly banker ever provide us such an excellent opportunity? Aren't they only smiling on the outside but are rotten inside?<br />
<br />
Well actually our friendly banker is nor good nor bad. All what he did is provided us the possibility to "multiply" the possible gain, but also (and this is important) the possible loss.<br />
<br />
Let's imagine that all of the future traveling would be just a prank and the price of gold bar in years time would instead of rising by 15 $ to 115 $ actually fell by (only) 5 $ to 95 $.<br />
<br />
How much would we actually loose?<br />
<br />
We've already discussed few scenarios and each of them would have different losses:<br />
<br />
a) if we would use only our savings<br />
<br />
For 5,000 $ we could buy only 50 gold bars. In one years time the price of those gold bars would be:<br />
amount of gold bars * price of gold bar in a year = 50 * 95 = 4,750 $<br />
<br />
So we would loose:<br />
earned amount - invested amount = 4,750 - 5,000 = -250 $<br />
<br />
Which in percentages is a loss of:<br />
(clear loss / invested amount) * 100 = (250 / 5,000) * 100 = 5 %<br />
<br />
b) if we would use savings and borrowed money<br />
<br />
The actual price we would receive once selling the gold bars would be:<br />
amount of gold bars * price of gold bar in a year = 2,550 * 95 = 242,250 $<br />
<br />
So the loss would be:<br />
earned amount - invested amount = 242,250 - 255,000 = -12,750 $<br />
<br />
As we still need to pay the interest for borrowed money, our clear loss would be:<br />
investment difference + interest = 12,750 + 12,500 = 25,250 $<br />
<br />
Which in percentages is a loss of:<br />
(clear loss / invested amount) * 100 = (25,250 / 255,000) * 100 = 9.902 %<br />
<br />
This means that we would not even loose all of our savings, but beside this we would still need to pay back the dept of 20,250 $.<br />
<br />
c) if we would call options with our savings and borrowed money<br />
<br />
in this case we would actually loose everything. And by losing everything I mean we would loose our savings, borrowed money and would still need to return it plus the interest. The problem is that we used all the money to buy a promise which is now worthless. In situations before we at least had gold which was cheaper but still had SOME value and people would be willing to pay this smaller value for it. But now we only hold many papers which allow us to buy gold for higher value than we would get on the market. And nobody (with properly functioning mind) would pay us for this disadvantageous option. So in the end we would be in debt of:<br />
borrowed money + interest = 250,000 + 12,500 = 262,500 $<br />
<br />
But remember, to get the net/real loss we still need to add our lost savings:<br />
262,500 + 5,000 = 267,500 $<br />
<br />
Which in percentages is a loss of:<br />
(clear loss / invested amount) * 100 = (267,500 / 255,000) * 100 = 104.902 %<br />
<br />
So as you can see, if this would be "only" a prank, it could be a very costly prank indeed.<br />
<div>
<br /></div>
Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-84544676406877649192014-03-30T03:57:00.001+02:002014-03-30T03:57:29.282+02:00How paying the Highest market rates can Ruin Your CompanyAlthough it may sound absurd, paying the highest market rates can actually ruin your company or project. And this is how it goes:<br />
<br />
You have loads of money, starting a new project and want to attract the best guys on the market.<br />
<br />
You will make sure your interview process is tough and will offer market rates nobody else can beat (let say 200 - 300 % of market average).<br />
<br />
You hire the best guys, plus some guys that are not the best but can talk the talk.<br />
<br />
Project started, there is a constructive progress and everybody is happy.<br />
<br />
People realize more and more their high (money) status and know that they could never earn that same amount anywhere else.<br />
<br />
People start spending their money and get used to their status. Project is still constructive.<br />
<br />
After some time somebody will realize, that he would afraid of loosing this great job and he starts to be less constructively critical (as this could maybe threaten his position).<br />
<br />
More and more people are less and less critical as they value the earnings more than the need to say what they think should be right.<br />
<br />
After some time the project lead/manager/main architect will get used to situation where nobody (or just few) people express concerns regarding his ideas and will actually start to believe that he is always right (also he is earning a lot of money so he must be very good as well).<br />
<br />
After some time he will believe so much in his true that he will be annoyed when somebody will oppose him.<br />
<br />
The leading guy will have a bad idea (everybody can have a bad idea), but nobody or just a small amount of people will point to flaws of this suggestion.<br />
<br />
The bad idea will be implemented. And the guys that will still have the will to point out the bad concepts behind the idea are seen as a threat (to the leading guys ego and to everybody else as being befriended with them could be regarded as risk to current position).<br />
<br />
This repeats itself few times.<br />
<br />
Now comes the point when the guys who actually still care are becoming frustrated because they realize they can't change things if they are heading the wrong directions.<br />
<br />
The guys that care are leaving the project as they can't handle the frustration anymore.<br />
The guys that don't care stay as the money is good.<br />
<br />
You're left with a main guy (or guys) with boosted ego and no self control and bunch of people that don't want to oppose bad ideas, just want to do their work, get the check and go home.<br />
<br />
More and more bad ideas are being implemented without any constructive conversation.<br />
<br />
Project starts to have difficulties. You hire more guys.<br />
<br />
Those that criticize the current state are seen as threat and its slowly taken care of them (they have some bad reviews and are being fired or are assimilated and became "silent workers").<br />
<br />
After some time you just realize that you have a project that is extremely expensive and is producing (if ever) product of questionable quality and it seemed to get wrong on many levels.<br />
<br />
And all of this because you provided rates nobody else could beat (so only those who cared about money stayed and the reasonable guys left).<br />
<br />
End of the story.Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com1tag:blogger.com,1999:blog-2546038368249263371.post-71688797860112580282013-11-07T01:27:00.000+01:002013-11-07T17:38:30.598+01:00Testing multithreaded codeSometimes you needed to test that your code is thread-safe and can be run from multiple threads at the same time. To help with doing this I wrote an utility class that can make your test code simpler and more readable.<br />
<br />
Here is the usage of it. I was testing that <i>SimpleDateFormat</i> is thread safe (actually it is not so this test will fail):<br />
<pre class="brush:java">public class SimpleDateFormatTest {
// this test will fail as SimpleDateFormat is not thread safe
@Test
public void shouldWorkConcurrently() throws ExecutionException, InterruptedException {
// Given
final DateFormat dateFormat = new SimpleDateFormat();
final Date date1 = newRandomDate();
final Date date2 = newRandomDate();
final Date date3 = newRandomDate();
ConcurrentExecutor executor = new ConcurrentExecutor();
Future<String> result1 = executor.addAction(new Callable<String>() {
public String call() {
return dateFormat.format(date1);
}
});
Future<String> result2 = executor.addAction(new Callable<String>() {
public String call() {
return dateFormat.format(date2);
}
});
Future<String> result3 = executor.addAction(new Callable<String>() {
public String call() {
return dateFormat.format(date3);
}
});
// When
executor.executeAtTheSameTime(); // this will block until all actions are finished
// Then
assertThat(result1.get(), is(new SimpleDateFormat().format(date1)));
assertThat(result2.get(), is(new SimpleDateFormat().format(date2)));
assertThat(result3.get(), is(new SimpleDateFormat().format(date3)));
}
private Date newRandomDate() {
return new Date(new Random().nextLong());
}
}</pre>
In this test I created a <i>ConcurrentExecutor</i> to which I added 3 actions. Each of those actions calls the <i>format</i> method for one of 3 dates. Then I call the method <i>executeAtTheSameTime</i> which will make sure that all actions will start at the same time. Once the execution is finished I retrieve each result and verify that it is the same as proper single threaded conversion.<br />
<br />
One nice thing about this utility is that it is extremely simple to retry the execution. Just rerun the <i>executeAtTheSameTime</i> and the <i>Future</i> results will hold a new value. This way you can put the When and Then section info a for loop and test the execution thread safety multiple times. This is sometimes needed as some multi-threaded issues are not always visible during the first run.<br />
<br />
If you would like to add this utility into your project here is the actual implementation of <i>ConcurrentExecutor</i> (please note that it is in java 1.7 so if you're using some older version you might miss some generics definitions):<br />
<pre class="brush:java">public class ConcurrentExecutor {
private List<Callable<?>> actions = new ArrayList<>();
private List<Object> results = new ArrayList<>();
private volatile boolean finished = false;
public <T> Future<T> addAction(Callable<T> callable) {
actions.add(callable);
int actionIndex = actions.size() - 1;
return new FutureResult<>(actionIndex);
}
public void executeAtTheSameTime() {
try {
finished = false;
results.clear();
ExecutorService executor = Executors.newFixedThreadPool(actions.size());
CyclicBarrier barrier = new CyclicBarrier(actions.size());
CountDownLatch doneCountDown = new CountDownLatch(actions.size());
List<Future<?>> futureResults = new ArrayList<>();
for (Callable<?> action : actions)
{
futureResults.add(executor.submit(new ActionCallable<>(action, barrier, doneCountDown)));
}
doneCountDown.await();
for (Future<?> futureResult : futureResults)
{
try
{
results.add(futureResult.get(500, TimeUnit.MILLISECONDS));
}
catch (Exception e)
{
throw new RuntimeException("not able to retrieve result from thread execution", e);
}
}
executor.shutdownNow();
finished = true;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
class ActionCallable<T> implements Callable<T> {
private final Callable<T> action;
private final CyclicBarrier startBarrier;
private final CountDownLatch doneCountDown;
ActionCallable(Callable<T> action, CyclicBarrier startBarrier, CountDownLatch doneCountDown) {
this.action = action;
this.startBarrier = startBarrier;
this.doneCountDown = doneCountDown;
}
@Override
public T call() throws Exception {
T result;
startBarrier.await();
result = action.call();
doneCountDown.countDown();
return result;
}
}
class FutureResult<T> implements Future<T> {
private final int resultIndex;
public FutureResult(int resultIndex) {
this.resultIndex = resultIndex;
}
@Override
public boolean isDone() {
return finished;
}
@Override
@SuppressWarnings("unchecked")
public T get() throws InterruptedException, ExecutionException {
if (!isDone()) {
throw new IllegalStateException("execution has not yet finished");
}
return (T) results.get(resultIndex);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
throw new RuntimeException("not implemented by intention");
}
@Override
public boolean isCancelled() {
throw new RuntimeException("not implemented by intention");
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
throw new RuntimeException("not implemented by intention");
}
}
}</pre>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-75286734580488629552013-10-31T21:09:00.000+01:002013-11-01T03:20:25.485+01:00Testing QuickFix/J messagesSome short time ago we've been using QuickFix/J for sending and receiving messages. The library was ok, but to write tests for it was kind of painfull. For example our verification of created messages looked like this:
<br />
<pre class="brush:java">assertThat(message, instanceOf(AllocationInstruction.class));
assertThat(message.getHeader().getString(SenderSubID.FIELD), is("senderSubId-123"));
assertThat(message.getString(AllocID.FIELD), is("allocId-123"));
assertThat(message.getInt(AllocType.FIELD), is(AllocType.CALCULATED));
assertThat(message.getChar(AllocTransType.FIELD), is(AllocTransType.NEW));
assertThat(message.getDecimal(Shares.FIELD), is(new BigDecimal("1000000")));
assertThat(message.getInt(NoAllocs.FIELD), is(2));
AllocationInstruction.NoAllocs allocationGroup;
allocationGroup = (AllocationInstruction.NoAllocs) message.getGroup(1, new AllocationInstruction.NoAllocs());
assertThat(allocationGroup.getString(IndividualAllocID.FIELD), is("1"));
assertThat(allocationGroup.getString(AllocAccount.FIELD), is("acc-01"));
assertThat(allocationGroup.getDecimal(AllocShares.FIELD), is(new BigDecimal("250000")));
allocationGroup = (AllocationInstruction.NoAllocs) message.getGroup(2, new AllocationInstruction.NoAllocs());
assertThat(allocationGroup.getString(IndividualAllocID.FIELD), is("2"));
assertThat(allocationGroup.getString(AllocAccount.FIELD), is("acc-02"));
assertThat(allocationGroup.getDecimal(AllocShares.FIELD), is(new BigDecimal("750000")));
</pre>
The code was not very readable and we also had to catch FieldNotFound exception each time we were doing this. To resolve this inconvenience I've created my own hamcrest matcher that can be used like this:
<br />
<pre class="brush:java">assertThat(message, isFIXMessage()
.ofType(AllocationInstruction.class)
.with(header().with(SenderSubID.FIELD, "senderSubId-123"))
.with(AllocID.FIELD, "allocId-123")
.with(AllocType.FIELD, AllocType.CALCULATED)
.with(AllocTransType.FIELD, AllocTransType.NEW)
.with(Shares.FIELD, "1000000")
.with(NoAllocs.FIELD, 2)
.with(group(1, NoAllocs.FIELD)
.with(IndividualAllocID.FIELD, 1)
.with(AllocAccount.FIELD, "acc-01")
.with(AllocShares.FIELD, new BigDecimal("250000"))
)
.with(group(2, NoAllocs.FIELD)
.with(IndividualAllocID.FIELD, 2)
.with(AllocAccount.FIELD, "acc-02")
.with(AllocShares.FIELD, new BigDecimal("750000"))
)
);
</pre>
The source code can be found/is stored under my project: <a href="https://github.com/MatejTymes/QuickFixUtils">QuickFixUtils</a>.Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-12746855333959143712012-03-12T00:22:00.008+01:002012-03-12T00:51:12.707+01:00Parsing JBehave tablesThis is a simple sample how to parse JBehave tables.<br /><br />In our project we often have to parse JBehave tables that might look like this one:<br /><pre><br />Given currencies are truncated to these decimal places<br />| Currency | Decimal places |<br />| USD | 2 |<br />| JPY | 0 |<br />| LYD | 3 |<br /></pre><br />The standard approach is to write something like this<br /><pre class="brush:java"><br /> @Given("currencies are truncated to these decimal places $currencyDecimalPlaces")<br /> public void currencyDecimalPlaces(ExamplesTable currencyDecimalPlacesTable) {<br /> ...<br /> for (Parameters row : currencyDecimalPlacesTable.getRowsAsParameters()) {<br /> // read row data <br /> }<br /> ...<br /> } <br /></pre><br />where reading row data may look like this:<br /><pre class="brush:java"><br /> CurrencyCode currencyCode = row.valueAs("Currency", CurrencyCode.class);<br /> Integer decimalPlaces = row.valueAs("Decimal places", Integer.class);<br /></pre><br />or we can make it a little more readable like this:<br /><pre class="brush:java"><br /> CurrencyCode currencyCode = Currency.in(row);<br /> Integer decimalPlaces = DecimalPlaces.in(row);<br /></pre><br />To make the readable code we define a new Column class:<br /><pre class="brush:java"><br />public class Column<T> {<br /><br /> private final String label;<br /> private final Class<T> defaultType;<br /><br /><br /> public Column(String label, Class<T> defaultType) {<br /> this.label = label;<br /> this.defaultType = defaultType;<br /> }<br /><br /><br /> public <T> T in(Parameters row, Class<T> type) {<br /> return row.valueAs(label, type);<br /> }<br /><br /><br /> public T in(Parameters row) {<br /> return in(row, defaultType);<br /> }<br /><br /><br /> public String asString(Parameters row) {<br /> return in(row, String.class);<br /> }<br /><br /><br /> public static <T> Column<T> column(String label, Class<T> defaultType) {<br /> return new Column<T>(label, defaultType);<br /> }<br /><br /><br /> public static Column<String> column(String label) {<br /> return new Column<String>(label, String.class);<br /> }<br />}<br /></pre><br />and registering custom columns:<br /><pre class="brush:java"><br />public interface Columns {<br /><br /> ...<br /> <br /> public static final Column<CurrencyCode> Currency = column("Currency", CurrencyCode.class);<br /> public static final Column<Integer> DecimalPlaces = column("Decimal places", Integer.class);<br /> <br /> ...<br />} <br /></pre><br />now we can convert the column to the default registered class<br /><pre class="brush:java"><br /> CurrencyCode currencyCode = Currency.in(row);<br /></pre><br />or to a specified class:<br /><pre class="brush:java"><br /> String currencyCode = Currency.in(row, String.class);<br /></pre>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-66119638394199758302011-09-01T03:31:00.000+02:002013-11-01T03:36:32.400+01:00The irony of not testing<div>
Some time ago I wanted to do something with jQuery. But our customer was still using a really obsolete version: 1.1.3.1 that has been released in 2008. At that time the last stable version was 1.6 and the functionality I needed was introduced in jQuery 1.3. As I had no intention to write something that has been already written (and fixed multiple times over its existence) I asked the the leading programmer of this project if there would be a slight chance of replacing it (jQuery) with a newer version.</div>
<div>
<br /></div>
<div>
The answer was simple: "NO". As strange it may sound I kind of undestood him and it wasn't a suprise for me. It was just a sad realization of the truth. To change the jQuery library I needed to upload it into the core module, that has then been referenced by 9 other big modules (that depended on him). And as NONE of these 9 modules had automated tests of their behavior, no one would know if this change would introduce some bugs or not (they already had workaround and fixes for the old jQuery version and god knows how it would interact with the latest version). So if you look at it: my small task/requirement could have killed all 9 modules that have been nicely working for at least 3 years (and it would require regression tests for EVERYTHING which was extremely costly).</div>
<div>
<br /></div>
<div>
The irony was that the lack of useful tests might forced me into introducing some custom code that was not mature enough (from performance and memory consumption view) and probably buggy (even with some testing) instead of just using some standard code that has been proven with time and repaired/maintained constantly.</div>
<div>
<br /></div>
<div>
The lesson learned from this is: test your code as without it your code might become so big that the task of testing will take a whole year for you (because you need to learn all the business logic behind it to see that this strange behavior is the correct one). As <b><i>without tests</i></b> : you might be <b><i>unable to upgrade</i></b> your <b><i>existing libraries</i></b> and will be <b><i>forced to</i></b> create custom <i><b>workarounds</b></i> for something that has been already done by somebody else. And in comparison to your code the <b><i>existing</i></b><i><b> one</b></i><b><i> might be</i></b> <b><i>more performant</i></b>, <i><b>mature</b></i> AND <b><i>under active maintenance</i></b>.</div>
Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-54324916426108560892011-07-24T01:03:00.028+02:002011-07-24T22:19:53.313+02:00Spring proxy - calling methods within the same service<div>One of the bigest issues of <b><i>spring aop</i></b> is, that when you use its proxies for adding some aop functionality (like for example transactions or security), your <b><i>calls to a method withing</i></b> the same <b><i>bean won't trigger</i></b> the <b><i>advised</i></b> aop <b><i>functionality</i></b>. </div><div><br /></div><div>So if you have a Service, that has two methods, where method A() HAS NO @Transactional annotation and method B() HAS a @Transactional annotation and non transactional method A() calls during its execution transactional method B() then spring won't start any transaction. This is because the spring proxy will redirect its call for A() to the service object but the call to B() won't be executed on the proxy (that knows how and when to start the transaction) but instead on the actual service object that has not functional code to start the transaction (only a @Transactional annotation on the method B()).</div><div><br /></div><div>To overcome this problem I have implemented a <b><i>simple solution</i></b> that <b><i>injects</i></b> the <b><i>proxy of current bean</i></b> instance and then you execute advised methods calling this instance proxy variable. All you have use is a <b><i>@ThisInstance annotation</i></b> and register a custom BeanPostProcessor:</div><pre class="brush:xml"><bean class="sk.yourpackage.ThisInstanceBeanPostProcessor"/></pre><div>After this you <b><i>annotate</i></b> a <b><i>setter or field</i></b> with @ThisInstance annotation and spring will inject a proxy instance of this bean as a setter parameter or field value (if a proxy won't be created it will inject the actual unproxied service). Like for example:</div><pre class="brush:java">@ThisInstance<br />private MyService thisInstance;</pre><div>After this you change your code from:</div><pre class="brush:java">this.B();</pre><div>into</div><pre class="brush:java">thisInstance.B();</pre><div>And that is the whole configuration. It works for private, protected, ... method and fields and for beans with any scoping.</div><div> </div><div>This is the code of the annotation:</div><pre class="brush:java">@Retention(RetentionPolicy.RUNTIME)<br />@Target({ElementType.FIELD, ElementType.METHOD})<br />public @interface ThisInstance { }</pre><div>and this is the code for bean post processor:</div><pre class="brush:java">public class ThisInstanceBeanPostProcessor implements BeanPostProcessor, Ordered {<br /><br /> private final Set<Class<? extends Annotation>> annotationTypes = new LinkedHashSet<Class<? extends Annotation>>();<br /><br /> private int order = Ordered.LOWEST_PRECEDENCE;<br /><br /><br /> public ThisInstanceBeanPostProcessor() {<br /> this.annotationTypes.add(ThisInstance.class);<br /> }<br /><br /><br /> @Override<br /> public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {<br /> return bean;<br /> }<br /><br /><br /> @Override<br /> public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {<br /><br /> Object targetBean = getTargetBean(bean);<br /><br /> injectCurrentInstance(targetBean, bean);<br /><br /> return bean;<br /> }<br /><br /><br /> private Object getTargetBean(Object bean) {<br /> Object target = bean;<br /><br /> if (target instanceof Advised) {<br /> target = ((Advised) target).getTargetSource();<br /><br /> if (target instanceof TargetSource) {<br /> try {<br /> target = ((TargetSource) target).getTarget();<br /> } catch (Exception e) {<br /> throw new IllegalStateException(e);<br /> }<br /> }<br /> }<br /><br /> return target;<br /> }<br /><br /><br /> private void injectCurrentInstance(Object targetBean, Object thisInstance) {<br /><br /> if (annotationTypes.size() == 0) {<br /> return;<br /> }<br /><br /> Class<?> beanClass = targetBean.getClass();<br /><br /> do {<br /> // for each interface - look for injection annotations<br /> for (Class beanInterface : beanClass.getInterfaces()) {<br /> for (Method method : beanInterface.getMethods()) {<br /> for (Class<? extends Annotation> annotationType : annotationTypes) {<br /> if (method.getAnnotation(annotationType) != null) {<br /> invokeMethod(targetBean, method, thisInstance);<br /> break;<br /> }<br /> }<br /> }<br /> }<br /><br /> // for each method - look for injection annotations<br /> for (Method method : beanClass.getDeclaredMethods()) {<br /> for (Class<? extends Annotation> annotationType : annotationTypes) {<br /> if (method.getAnnotation(annotationType) != null) {<br /> invokeMethod(targetBean, method, thisInstance);<br /> break;<br /> }<br /> }<br /> }<br /><br /> // for each field - look for injection annotations<br /> for (Field field : beanClass.getDeclaredFields()) {<br /> for (Class<? extends Annotation> annotationType : annotationTypes) {<br /> if (field.getAnnotation(annotationType) != null) {<br /> setFieldValue(targetBean, field, thisInstance);<br /> break;<br /> }<br /> }<br /> }<br /><br /> beanClass = beanClass.getSuperclass();<br /><br /> } while (!Object.class.equals(beanClass));<br /> }<br /><br /><br /> private void invokeMethod(Object object, Method method, Object... values) {<br /> boolean isAccessible = method.isAccessible();<br /><br /> try {<br /> method.setAccessible(true);<br /><br /> method.invoke(object, values);<br /> } catch (Exception e) {<br /> throw new IllegalStateException(e);<br /> } finally {<br /> method.setAccessible(isAccessible);<br /> }<br /> }<br /><br /><br /> private void setFieldValue(Object object, Field field, Object value) {<br /> boolean isAccessible = field.isAccessible();<br /><br /> try {<br /> field.setAccessible(true);<br /><br /> field.set(object, value);<br /> } catch (Exception e) {<br /> throw new IllegalStateException(e);<br /> } finally {<br /> field.setAccessible(isAccessible);<br /> }<br /> }<br /><br /><br /> /**<br /> * allows you to add custom annotation type<br /> *<br /> * @param annotationType custom annotation type<br /> */<br /> public void setAnnotationType(Class<? extends Annotation> annotationType) {<br /> this.annotationTypes.add(annotationType);<br /> }<br /><br /><br /> /**<br /> * allows you to define custom annotation types<br /> *<br /> * @param annotationTypes custom annotation types<br /> */<br /> public void setAnnotationTypes(Set<Class<? extends Annotation>> annotationTypes) {<br /> this.annotationTypes.clear();<br /> this.annotationTypes.addAll(annotationTypes);<br /> }<br /><br /><br /> @Override<br /> public int getOrder() {<br /> return order;<br /> }<br /><br /><br /> public void setOrder(int order) {<br /> this.order = order;<br /> }<br />}</pre>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com5tag:blogger.com,1999:blog-2546038368249263371.post-54984591073769701362011-05-21T00:39:00.045+02:002011-05-23T14:33:04.915+02:00Some geecon 2011 notes<div>A week has passed and I have forced myself to put together some notes I have scribbled during my stay there:</div><div><br /></div><div><b><i>Java 7</i></b></div><div><br /></div><div>What have we found: the good part is that oracle is holding up to the schedule and there is a big chance that this year there will be finally a new java version. I am still curious if this will somehow change the so often said question: "java is dead", but otherwise this is good. So what interesting functionalities can we find in java 7:</div><div><ul><li> support for dynamic languages - so your groovy, scala, jruby will be once again faster better cooler</li><li>project coin : so you can write something like this: List<String><string> list = new ArrayList<>();</string></li><li>fork-join - concurrency library that is not bad, but I would maybe concentrate more on concurrency alias actors (for example akka). You could try this library long time ago as it was available as jsr166y.jar</li><li>after many many years, you'll be able to use strings in switch statement (although everybody knows that switch - case is bad and we should use strategy pattern instead :D)</li><li>automatical closing of Closable resources opened in the beginning of try statement</li><li>one catch for multiple exceptions</li></ul></div><div>For me it was a disappointment that the collection usage simplification won't make it into java 7 (although I knew this before I still had some hope).</div><div><br /></div><div>To see how this will work in action you can look at this <a href="http://code.joejag.com/2009/new-language-features-in-java-7/">quite old article</a>.</div><div><br /></div><div><br /></div><div><b><i>Spring</i></b></div><div><br /></div><div>Besides the presentation on the whole spring ecosystem we heard something about the long awaited spring 3.1. They added caching support (quite old information) as they had no more patience to wait for the never coming java standard. Also it should be fully compatible with java 7. It is possible that the execution and scheduling framework will use the new fork-join library. For me a it was quite interesting how easily (with few lines) you can integrate with many network and social solutions: email, XMPP, RSS/Atom, twitter. Twitter can be also used for one to one communication. Spring also checks if you haven't reached the twitter message limit (the limit check works even when you are writing messages from multiple devices/platforms). Also we learned that twitter is often making incompatible changes. And as Spring relies on 3rd party libraries in this integration they can't control the speed of change adaption. This is also why they announced "spring social" (also quite old information) where they will write and adapt all code by their internal team. So by using spring social you should be almost always up to date with current twitter api. The last interesting thing (which some people still don't know) is spring data that provides a simplification of jdbc, hibernate, jpa and noSql databases processing</div><div><br /></div><div><br /></div><div><b><i>Hadoop</i></b></div><div><br /></div><div>This was quite a good introduction into the whole hadoop ecosystem. From what I know Hadoop is based on some research papers from google. You can use it to execute tasks over massive amount of data where the task can be divided into smaller tasks and then the result can be combined to produce one global result. The heart of it all is HDFS (hadoop file system), that is based on GFS (google file system). It presents a distributed file system layer, that is positioned above your physical disks. Over it operates the MapReduce framework. To write your custom map reduce tasks/jobs you have to implement interfaces Mapper and Reducer and extend class MapReduceBase, plus define a job configuration (but you are not limited just to java - although it seems that java is one of the most perforant solutions). Another interesting module is HBase which lays over the HDFS and provides equivalent functionality to google's bigtable (from my point of view Hadoop is just one big loot stolen from google that is provided as open source - but this is good). Hbase can be used as input or output for MapReduce jobs. The good thing about Hadoop is that you can process tasks over massive new data without the risk that it will blow up, because it needs to be reindexed first (as you might see in standard database solutions). For me a new information was that cloudera provides a simplification of the installation process and a business (payed) support (which can be a good argument for your customers)</div><div><br /></div><div><br /></div><div><i><b>Code generation</b></i></div><div><br /></div><div>There was a presentation of some frameworks that provide you code generation from annotations. <a href="http://projectlombok.org/features/index.html">Lombok</a> provides annotations to simplify your work: @Getter, @Setter, @Synchronized (even when it seams stupid it does it better than simple synchronize), @EqualAndHashCode, ... The annotations will be replaced by code during compilation process (to make this work the process must be written for each platform: windows, linux, ...). Other solution was Spring Roo, that uses aspectj and *.aj files (which you probably already know). There were also some interesting annotations from Groovy: @Delegate - when used on variable, then the encapsulating class has all its methods and they refer to this object (quite cool - from what I know there is no such thing in java), @Lazy - lazy initialization, @Singleton - proper singleton implementation. For me quite new thing was contract driven programming where you can define a precondition that must be met so that a method can be executed without exception and an after condition - to be honest I am not sure what will happen if this after condition will be violated (as I don't expect it will do some rollback). The sad thing is that the ide support is sometimes not good (for example for Lombok in IDEA). I've also learned new java command: javap that will show you the skeleton of compiled class (everything except method implementation details)</div><div><br /></div><div><br /></div><div><i><b>Refactoring</b></i></div><div><br /></div><div>I'll just summarize some interesting and practices (different from the ones so often heard):</div><div><ul><li>set timer to 2 hours, start your work. review your work. is it better -> commit it. is it worst -> revert</li><li>do incremental changes</li><li>the code with most revisions is the best candidate for test coverage</li><li>if you want to use new libraries, but your project tech lead won't allow you to put them into production, put them into test code :D</li><li>have fun, for example give some small awards to those who are most active in code maintenance (like for example a picture on the wall "maintainer of the week", or badges "100% test code coverage guru")</li><li>good argument for unit tests: when you write them other colleagues can't *** up your code</li></ul></div><div><br /></div><div><br /></div><div><i><b>New J2EE</b></i></div><div><br /></div><div>I have seen only a small glimpse of it, but it seems very readable and simple. Although some spring guys have been making fun of j2ee as from they point of view (when we omit glassfish) nobody supports the whole standard (even when it is released longer than a year now).</div><div><br /></div><div><br /></div><div><b><i>GridGain</i></b></div><div><br /></div><div>After some live demos a left the presentation with impression that this is a cool framework for distributed programming in scala (at least from the maintenance point of view). The distribution of code is almost automatic (not much more than compilation of code is required), new nodes execute jobs instantly after they start and it seemed that there is no complex configuration. So the main goal you have to focus on is your business logic (MapReduce/Actors).</div><div><br /></div><div><div><br /></div><div><i><b>Neo4j</b></i></div><div><br /></div><div>This graph oriented database (yeees, you're right: NoSQL) was presented by the most charismatic presenter from the whole conference. From other NoSQL databases it has some interesting features. It supports transactions. As it is a graph oriented database it stores objects and maps relations between them. Also the querying has a language defined for it, so you don't have to figure out how to retrieve the data from your key value store and also there is no need to write map-reduce functions. It is good for data retrieval in situations where normal database would blow up, because of the required joins (also the speed of querying seems to be unaffected by the data amount, which is quite a opposite situation as you can see it on relational databases). So for example work with hierarchies is a great candidate for Neo4j (you can also define how deep would you like to go, to prevent the out of memory error once you would retrieve too much data). There is heavy use of caching (in this point I am not sure how the first query (before anything is cached) would perform). But there are also some things you must be aware of. The neo4j doesn't scale well (between multiple nodes) when your data is heavily interconnected. As in this case there is a great traffic between nodes that slows the whole query process. To assure great scalability you should be able to split your data into separate data buckets, where each node would host one to many complete data bucket.</div><div><br /></div><div><br /></div><div><i><b>Hibernate</b></i></div><div><br /></div><div>From my point of view, it is really hard to say something new about hibernate. But there has been a good summarization presentation about the common pitfalls/mistakes.</div><div><br /></div><div><br /></div><div><i><b>Node.js</b></i></div><div><br /></div><div>This was a part of the workshop day. It contained a small introduction to node.js (if you review <a href="http://www.youtube.com/watch?feature=player_embedded&v=jo_B4LTHi3I">the presentation</a> on the node.js page you'll get the almost same information), plus we heard a small but clear explanation of javascript prototyping. After this we downloaded some great practical example (mortal combat client/server written in node.js - code can be found <a href="https://github.com/ktoso/mk-javascript">here</a>). Once we got it running we were asked to implement the login screen. Oh and yes, for those who would like to try it out, I would like to cite one advice that has been told to me: "are you trying to run it on cygwin? forget it! Its no good."</div></div><div><br /></div><div>my notes:</div><div>- if you hate javascript I advice you to see <a href="http://www.youtube.com/watch?v=hQVTIJBZook">this presentation</a> (that has nothing to do with geecon :))</div><div>- node.js has good integration with html 5 web sockets (if you don't know why they are the new hype, read <a href="http://soa.sys-con.com/node/1315473">this article</a>)</div><div><br /></div><div><br /></div><div>So in the end I would like to do some summarization. Once I left geecon I had some mixed feelings. From my point of view it is hard to say something new about java as most of the interesting stuff is now being processed in different languages: groovy, scala, ruby, javascript. But somehow they have managed to make it a quite interesting conference. The only thing I was maybe missing were some electric plugs (as they are normally not present in multi-cinemas) (yeah I know, I should have bought a mac :() but otherwise it was ok.</div><div><br /></div><div>notes for me:</div><div>- never sit down. you'll fall asleep</div><div>- don't eat polish eggs</div>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-58862254370266636002011-05-16T23:19:00.008+02:002014-01-02T03:32:17.004+01:00Some hints I have learned about ActiveVOS (BPMS solution) so farIf you expect many long running processes (or what is worse human tasks) <b>set</b> the <b>system logging level to none</b> and define the <b>logging for each of your process separately</b><br />
<blockquote>
<i><b>Process logs</b></i> can take a <i><b>lot of your database</b></i> storage. Especialy the logs for human tasks.<br />
Not that they wouldn't be big if we would take a look at them separately. One log will take only few bytes. The problem is that there are many of them.<br />
From our measurement we had approximately around 5500 log records for <b><i>one human task</i></b> which could consume almost <b><i>1MB of logical datastorage</i></b>. As the system process are using the default logging level then by setting it to none you'll be rid of this problem (as you'll almost never need the logs from system processes - event the support never wants them).</blockquote>
<br />
Try to <b>avoid</b> architectural design based on <b>manual process fixing</b> in case of failure<br />
<blockquote>
One of the biggest advantages of ActiveVOS is that you can set the logging to full<br />
level for any of your processes and once there is a failure you can return to any<br />
point of it, fix some data and continue from there as if nothing happened.<br />
The disadvantage is that <b style="font-style: italic;">once you overuse this </b>"pattern", the <b><i>maintenance cost</i></b> of your solution <b><i>will heavily increase</i></b> as you'll always need some additional guys that will fix your failed processes from the point they arived into the office<br />
(but that of course depends on the number of processes you're dealing with).<br />
I am not saying that you should absolutely omit this pattern but I suggest that<br />
you should <b><i>always look if there is no</i></b> additional <b><i>automatic solution</i></b>.</blockquote>
<div>
<br /></div>
Be prepared that <b><i>some things won't work</i></b><br />
<blockquote>
Nothing is perfect and ActiveVOS (even when it provides a cool solution) has some of it flaws. So be prepared that in some cases you'll end up with a workaround. To just name some that we needed here is a short list:<br />
<ul>
<li>XQuery methods "instance of" and "typeswitch" are not working properly in once you want to check an element type that might come from a hierarchy of types. The problem is that the saxon library which is underneath is not providing this functionality. The same goes for method like "rename".</li>
<li>If you're using asynch calls with WS-Addressing than the timeout policy is not working if you'll define it on reply activity.</li>
<li>If multiple wsdl files in one process are using the same namespace then they must be merged. I don't know how it is in newer versions (we lastly used this feature in version 6.2), but the eventing can show some unexpected behavior once used in clustered environment (but as said this might be already fixed).</li>
</ul>
But don't be afraid as these are some minor issues for which you can surely find a workaround and might be fixed over the time.</blockquote>
<br />
<b>Don't use human tasks as</b> mere <b>notifications for FINAL task</b> that needs to be done<br />
<blockquote>
From collegues project I have the experience that the <b><i>people forget to close </i></b>these <i><b>FINAL TASK "notifications"</b></i> once the work is finished as for them its not as important (because they have already a new notification/task that needs to be done).</blockquote>
<blockquote>
Even the management doesn't care as everything has been done/finished, so (from the bussiness point of view) who cares that the process was not closed.</blockquote>
<blockquote>
This creates not only confusion about which tasks are finished and which not (sure the user knows), but also makes a heavy usage of the database as unfinished system processes that run the human task <b><i>can consume</i></b> much of the <i><b>database storage</b></i> and <i><b>can't be deleted</b></i> if we wan't to decrease the database size.</blockquote>
<br />
Try to get all your <b>important business data</b> out of your processes and <b>store</b> them <b>into<br />separate database</b> before the process ends<br />
<blockquote>
<b><i>Currently</i></b> there is <b><i>no archivation mechanism</i></b> (of what I would know) that would allow you <b><i>to archive only a part of your processes</i></b> (for example completed ones) and leave other in tact. </blockquote>
<blockquote>
The <b><i>only thing</i></b> you can do is <b><i>archive</i></b> your <b><i>ActiveVOS database as a whole</i></b>.<br />
And than <b><i>recover it as a whole</i></b>. </blockquote>
<blockquote>
This can be very tricky as it <b><i>prohibits any incremental recoveries or storages</i></b> (you can write it on your own, but than you'll loose the support - and which customer would be willing to loose that) and would work create a backup when you have a great mix of completed and running processes. You'll probably get to this problem once you'll have many finished processes as they can cost a lot of database memory (in one big project we're already over 600GB !!! of Oracle database). The best thing how you can prepare yourself for this is to a create some persisting activities in your processes, that will gather all meaningfull informations and store them into separate database. This way once you'll encounter some problems with database space, you can easily delete some of your old completed processes.</blockquote>
<blockquote>
Oh and don't forget to put them in the first version of your processes as your deletes can be only used for processes "older than" (so forget defining time range from - to) and you wouldn't be happy if the oldest processes without persistence mechanism would be deleted/sacrificed for the sake of the database (as you must delete the newer versions).</blockquote>
Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-84824012178692864672011-05-06T10:36:00.021+02:002011-05-21T00:47:18.609+02:00Javascript localization hellNot long time ago I visited a hell. The hell was called javascript localization. And the headache started when I wanted to sort some strings. To do so I needed to compare them first. This would seem as a simple task if you would be an english gentleman without the fear of diacritiques. But as I live and work in a country where diacritique is ever present I needed to process it properly. Method <b><i>localeCompare</i></b> was working <i><b>differently on every browser</b></i>. Also if method <i><b>indexOf</b></i> was used for searching trough <i style="font-weight: bold; "><span class="Apple-style-span" style="font-style: normal; font-weight: normal; "><i style="font-weight: bold; ">text with</i></span> diacritique</i> using <i><b>search</b></i> expression <b><i>without diacritique</i></b> it was <b><i>not working</i></b> properly.<div><br /></div><div>So I once again ended with something I don't like very much. I wrote my custom javascript util that could be used like this:</div><div><pre class="brush: javascript">localeHelper.diacritiqueComparison('čerešne', 'citron'); // = 1<br />localeHelper.diacritiqueComparison('čerešne', 'hrozno'); // = -1<br /><br />// this is case insensitive<br />localeHelper.containsDiacritiqueText('štart', 'st'); // = true<br />localeHelper.containsDiacritiqueText('štart', 'št'); // = true<br />localeHelper.containsDiacritiqueText('štart', 'tar'); // = true<br />localeHelper.containsDiacritiqueText('trend', 'tr'); // = true<br />localeHelper.containsDiacritiqueText('trend', 'ťr'); // = false (as we explicitly want the symbol 'ť' and not 't')<br />localeHelper.containsDiacritiqueText('trend', 'b'); // = false (symbol 'b' is not present)<br /></pre></div><div>The final code looked like this:<br /></div><pre class="brush: javascript">function LocaleHelper() { // constructor<br /><br /> var i;<br /><br /> // source: http://en.wikipedia.org/wiki/Alphabets_derived_from_the_Latin<br /> // supported languages :<br /> // austro-bavarian, belarusian, croatian, czech, dutch, estonian, finish, french, german, hungarian, irish, italian,<br /> // latvian, lithuanian, polish, portuquese, romanian, slovak, sorbian, spanish, swedish, turkish<br /> //<br /> // todo: not sure how to or if I should process german letter 'ß'<br /> var localUpperVowelList = "ÁÀÂÄĂĀÃÅĄÆÉÈĖÊËĚĒĘÍÌİÎÏĪĮÓÒÔÖÕŐŒÚÙÛÜŬŪŰŮŲÝŸ";<br /> var latinUpperVowelList = "AAAAAAAAAAEEEEEEEEIIIIIIIOOOOOOOUUUUUUUUUYY";<br /><br /> var localLowerVowelList = "áàâäăāãåąæéèėêëěēęıíìîïīįóòôöõőœúùûüŭūűůųýÿ";<br /> var latinLowerVowelList = "aaaaaaaaaaeeeeeeeeiiiiiiiooooooouuuuuuuuuyy";<br /><br /> var localUpperConsonantList = "ĆČÇĎĐĞĢĶĹĻŁĽŃŇÑŅŔŘŚŠŞȘŤŢṬŹŻŽ";<br /> var latinUpperConsonantList = "CCCDDGGKLLLLNNNNRRSSSSTTTZZZ";<br /><br /> var localLowerConsonantList = "ćčçďđğģķĺļłľńňñņŕřśšşșťţṭźżž";<br /> var latinLowerConsonantList = "cccddggkllllnnnnrrsssstttzzz";<br /><br /> this.charMap = [];<br /> for (i = 0; i < localUpperVowelList.length; i++) {<br /> this.charMap[localUpperVowelList.charAt(i)] = latinUpperVowelList.charAt(i);<br /> }<br /> for (i = 0; i < localLowerVowelList.length; i++) {<br /> this.charMap[localLowerVowelList.charAt(i)] = latinLowerVowelList.charAt(i);<br /> }<br /> for (i = 0; i < localUpperConsonantList.length; i++) {<br /> this.charMap[localUpperConsonantList.charAt(i)] = latinUpperConsonantList.charAt(i);<br /> }<br /> for (i = 0; i < localLowerConsonantList.length; i++) {<br /> this.charMap[localLowerConsonantList.charAt(i)] = latinLowerConsonantList.charAt(i);<br /> }<br />}<br /><br /><br />LocaleHelper.prototype = {<br /><br /> removeCharDiacritique : function(charToProcess) {<br /><br /> var result = this.charMap[charToProcess];<br /> if ((result == undefined) || (result == null)) {<br /> result = charToProcess;<br /> }<br /><br /> return result;<br /> },<br /><br /> localeCharCompare : function(charA, charB) {<br /><br /> var newCharA = this.removeCharDiacritique(charA);<br /> var newCharB = this.removeCharDiacritique(charB);<br /><br /> return (newCharA == newCharB) ? 0 : ((newCharA < newCharB) ? -1 : 1);<br /><br /> // removed: doesn't work on every browser<br />// return charA.localeCompare(charB);<br /> },<br /><br /> isLatinLetter : function(character) {<br /> return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z');<br /> },<br /><br /><br /> // case sensitivity is used only when the words have the same letters (they are read the same way)<br /> diacritiqueComparison : function(textA, textB) {<br /> // todo: note: in Lithuanian alphabet the 'y' character is just before before 'j' - so this algorithm won't work properly<br /><br /> var result = 0;<br /><br /> var caseDiff = 0; // difference in case sensitiveness<br /> var minLength = Math.min(textA.length, textB.length);<br /> for (var i = 0; i < minLength; i++) {<br /> var charA = textA.charAt(i);<br /> var charB = textB.charAt(i);<br /> var lowerA = charA.toLocaleLowerCase();<br /> var lowerB = charB.toLocaleLowerCase();<br /><br /> result = this.localeCharCompare(lowerA, lowerB);<br /> if (result == 0 && lowerA != lowerB) {<br /> result = (lowerA < lowerB) ? -1 : 1;<br /> }<br /><br /> if (result == 0) {<br /> if (caseDiff == 0 && charA != charB) { // first most left difference in case is the only important one<br /> caseDiff = (charA < charB) ? -1 : 1;<br /> }<br /> } else {<br /> break;<br /> }<br /> }<br /><br /> if (result == 0) {<br /> if (textA.length != textB.length) {<br /> result = (textA.length < textB.length) ? -1 : 1;<br /> } else {<br /> result = caseDiff; // if the strings are identical let the case sensitive difference decide<br /> }<br /> }<br /><br /> return result;<br /> },<br /><br /> containsDiacritiqueText : function (fullText, searchText) {<br /><br /> var textA = fullText;<br /> var textB = searchText;<br /><br /> var result = false;<br /><br /> if (textB.length == 0) {<br /> result = true;<br /> } else if (textA.length >= textB.length) {<br /> for (var i = 0 ; i < textA.length - textB.length + 1; i++) {<br /> var found = true;<br /><br /> for (var j = 0; j < textB.length; j++) {<br /> var charA = textA.charAt(i + j).toLocaleLowerCase();<br /> var charB = textB.charAt(j).toLocaleLowerCase();<br /><br /> if (charA != charB) {<br /> if (this.localeCharCompare(charA, charB) != 0) {<br /> found = false;<br /> break;<br /> } else if (!this.isLatinLetter(charB)) {<br /> found = false;<br /> break;<br /> }<br /> }<br /> }<br /><br /> if (found === true) {<br /> result = true;<br /> break;<br /> }<br /> }<br /> }<br /><br /> return result;<br /> }<br />};<br /><br /><br />var localeHelper = new LocaleHelper();<br /></pre>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com1tag:blogger.com,1999:blog-2546038368249263371.post-39804355503468714442011-05-04T21:24:00.017+02:002011-05-06T00:47:51.541+02:00Porovnanie stringov s diakritikou v javascripte<div>Dnes som od zákazníka dostal zadanie napísať v javascripte metodu pre zotriedenie textov. Čo by bola celkom jednoduchá úloha, ak by išlo anglické texty. No bohužiaľ, naša slovenská (a takisto česká) abeceda má jednu nechutnú vec: diakritiku.</div><div><br /></div><div>Ak sa teda snažíte zotriediť tieto texty: </div><div><ul><li>"cudzí", "čučoriedka", "ťava", "tŕň", "trstina"</li></ul></div><div>dostanete pri štandartnom porovnávaní textov túto postupnosť:</div><div><ul><li>"cudzí", "trstina", "tŕň", "čučoriedka", "ťava"</li></ul>A to nie je práve najlepšie usporiadanie (štandartne su prvé veľké písmená bez diakritiky, nasledované malými písmenami bez diakritiky, veľké písmená s diakritikou a na záver malé písmená s diakritikou).</div><div><br /></div><div>Klasické porovnanie teda nie je dostačujúce. Skúšal som teda nájsť niečo vhodnejšie a narazil som na celkom peknú metódu:</div><div><ul><li>textA.localeCompare(textB)</li></ul></div><div>S jej použitím som dosiahol o trošku lepší, nie však dostačujúci výsledok:</div><div><ul><li>"čučoriedka", "cudzí", "ťava", "tŕň", "trstina"</li></ul></div><div>Problém tejto metódy je že diakritiku úplne odignoruje a teda nikdy nedá znak s diakritikou za znak bez diakritiky, ale tieto znaky majú v porovnaní identické postavenie (čo spôsobilo, že znak 'č' sa ocitol pred znakom 'c', znak 'ť' pred znakom 't' a znak 'ŕ' pred znakom 'r').</div><div><br /></div><div>K tomu sa ešte pridali problémy s kapitálkami. Skúšal som nájsť nejaké vhodné riešenie na internete bohužiaľ žiadne nebolo dostačujúce (plus nemal som chuť vymenovávať všetky znaky s diakritikou - som detailista a chcel som aby to fungovalo pre všetky jazyky odvodené z latinskej abecedy).</div><div><br /></div><div>A tak som dospel k niečomu, čo normálne robia všetci nadšený programátori (a čo sa štandartne považuje za chybu). Napíšem si túto metódu sám. A toto je čo nakoniec vzniklo:</div><pre class="brush: javascript"> function diacritiqueComparison(textA, textB) {<br /> var result = 0;<br /><br /> var caseDiff = 0; // difference in case sensitiveness<br /> var minLength = Math.min(textA.length, textB.length);<br /> for (var i = 0; i < minLength; i++) {<br /> var charA = textA.charAt(i);<br /> var charB = textB.charAt(i);<br /> var lowerA = charA.toLocaleLowerCase();<br /> var lowerB = charB.toLocaleLowerCase();<br /> <br /> result = lowerA.localeCompare(lowerB);<br /> if (result == 0 && lowerA != lowerB) {<br /> result = (lowerA < lowerB) ? -1 : 1;<br /> }<br /> <br /> if (result == 0) {<br /> if (caseDiff == 0 && charA != charB) { // first most left difference in case is the only important one<br /> caseDiff = (charA < charB) ? -1 : 1;<br /> }<br /> } else {<br /> break;<br /> }<br /> }<br /> <br /> if (result == 0) {<br /> if (textA.length != textB.length) {<br /> result = (textA.length < textB.length) ? -1 : 1;<br /> } else {<br /> result = caseDiff; // if the strings are identical let the case sensitive difference decide<br /> }<br /> }<br /> <br /> return result;<br /> } </pre><div>Po jej použití som už dosahoval celkom uspokojivé výsledky:</div><ul><li>"cudzí", "čučoriedka", "trstina", "tŕň", "ťava"</li></ul><div>Dodatočná poznámka z nasledujúceho dňa: bodaj by porazilo celý ten javascript. Metóda <b><i>localeCompare</i></b> funguje na každom prehliadači inak, dokonca sa mi zdá, že je aj rozdiel medzi IE verziami (vďaka čomu som si ešte aj túto metódu musel naprogramovať sám - a áno, nakoniec som vymenovával znaky s diakritikou :(). Kde toto všetko skončí.</div>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-68719059343210305802011-04-28T15:15:00.012+02:002011-05-04T22:27:55.128+02:00XSS - prevent path manipulationFew days ago I have been asked by a customer to add validation for file download functionality.<br /><br />They needed to verify that the file requested from web page is in working directory or in some of its sub directories and log once it isn't. And this is the final code (if you know of some better/existing solution, just let me know):<br /><pre name="code" class="brush: java"><br /> public static String correctFilePath(String filePath, boolean allowSubDirAccess) {<br /> String newFilePath = filePath;<br /><br /> if (StringUtils.isNotBlank(newFilePath)) {<br /> String normalizedFilePath = new File(newFilePath).getPath();<br /><br /> if (StringUtils.startsWith(normalizedFilePath, File.separator)) {<br /> log.error("attempt to access the root directory: " + filePath);<br /> }<br /><br /> List<String> allowedPathElements = new ArrayList<String>();<br /> for (String pathElement : StringUtils.split(normalizedFilePath, File.separator)) {<br /> pathElement = pathElement.trim();<br /> if (!".".equals(pathElement)) {<br /> if (pathElement.contains(":")) {<br /> log.error("attempt to access the root directory: " + filePath);<br /> } else if ("..".equals(pathElement)) {<br /> if (allowedPathElements.size() > 0) {<br /> allowedPathElements.remove(allowedPathElements.size() - 1);<br /> } else {<br /> log.error("attempt to access parent of working directory: " + filePath);<br /> }<br /> } else {<br /> allowedPathElements.add(pathElement);<br /> }<br /> }<br /> }<br /><br /> if (!allowSubDirAccess) {<br /> if (allowedPathElements.size() > 1) {<br /> log.error("attempt to access files not located in working directory: " + filePath);<br /> }<br /><br /> newFilePath = allowedPathElements.get(allowedPathElements.size() - 1);<br /> } else {<br /><br /> newFilePath = StringUtils.join(allowedPathElements, File.separator);<br /> }<br /> }<br /><br /> return newFilePath;<br /> }<br /></pre>Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-54053048631770439022011-01-26T01:31:00.029+01:002011-05-04T22:28:50.439+02:00Trapenie s Hibernate proxyV nasledujucom clanku popisem jeden z pripadov, kedy sa vam moze vytvorit problematicka hibernate proxy.<br /><br />V podstate to je celkom jednoduche. Ked mate velku stromovu strukturu, nie stale chcete aby sa vam nacitali vsetky objekty, ktore sa v nej nachadzaju. Chcete vsak aby sa niektore casti dotahovali len ak ich treba, pripadne sa dotiahnu v specifickych selectoch (napriklad pouzitim LEFT JOIN FETCH).<br />Aby sa toho dosiahlo definuje sa na niektore vazby/fieldy lazy loading = FetchType.LAZY (teda sa z databazy nacitaju az ked ich skutocne treba). No a niekedy sa stane, ze niekto toto urobi aj na fieldy ktorych trieda predstavuje hierarchicku strukturu (toto je zakladny kamen urazu). Teda napriklad je takto oznaceny field typu Animal od ktoreho je oddena trieda Cat, Dog, a Mouse.<br /><br />Ak si teda nacitate triedu, ktora ma lazy loaded field, hibernate pre neho vytvori proxy (hibernate to takto robi pre vsetky lazy loading fieldy). A az ked sa na tej proxine zavola nejaky getter/setter tak hibernate spusti mechanizmus, ktory z databazy dotaha vsetky informacie a tento (doteraz nedotiahnuty) objekt vytvori. Takze teraz mame field ktory odkazuje na proxinu a ta sa dalej odkazuje (v sebe zaobaluje) skutocne nacitany (nami ocakavany objekt). Vsetky dotazy na gettre a settre sa potom cez proxinu zavolaju na tomto objekte.<br /><br />No a teraz prichadza ta zaujimava cast: dovodom preco sa dava lazy loading je aby sa nerobilo zbytocne vela selectov do tabuliek, ktorych data sa vobec nevyuzivaju. Co znamena, ze sa teda nerobi ani select do tabuliek pre dany lazy loading field. A tu je ta dolezita vec. Hibernate teda v case vytvarania proxy nepozna skutocny typ objektu, ktory pod proxinou bude, nakolko nevykonal selekt ktory by mu povedal, ze to je napriklad Dog. Nakolko teda nevie, ci to bude Dog, Cat, Mouse alebo Animal, vytvori hibernate proxinu ktora ma ten isty typ ako je zadefinovany na fielde. V nasom pripade teda vytvori proxinu typu Animal. A aj ked sa pod nou neskor dotiahne objekt typu Dog, tak ked na tejto proxine urobime instanceof Dog, tak nam java vyhodi false. Dovodom je, ze aj ked proxina pod sebou ma ulozeny objekt typu Dog ona sama je typu Animal (a nas field sa odkazuje na tuto proxinu a nie na konkretny objekt pod nou). Inak len tak pre zaujimavost: v pripade ze by Animal, Dog, Mouse a Cat boli interfacy, potom by proxina implementovala VSETKY (teda instanceof by vratila true na lubovolny z nich).<br /><br />Takze, uz len jedna vec a mame to hotove :D.<br /><br />p.s.: tato chyba je viac menej sposobena zlym navrhom<br />p.s.2: plus toto je specificka vec, ktora sa prikladom neda ukazat, nakolko z prikladu nevidno ze vobec nejaky problem existuje.<br /><br />Takze, ale aby sme pokracovali dalej. Dufam, ze dovodu, preco hibernate take proxiny vytvara ste teda pochopil?<br /><br />A teraz prichadza na radu dalsia taka zaujimava vec: hibernate ma jedno klucove pravidlo. V pripade, ze bola entita nacitana do sessiony (stane sa manazovanou) musi byt tato entita (v danej sessione) reprezentovana len jednym jedinym objektom. Co v skratke znamena ze ak by ste v jednej sessione urobil dvakrat za sebou select na jednu a tu istu entitu, dostanete stale identicky objekt. Nie teda objekt s rovnakymi fieldami ale UPLNE TEN ISTY objekt. Teda zmena v objekte z prveho selektu sa premietne v objekte z druheho (nakolko ide o jeden objekt). To vsak znamena i to, ze ked je entity reprezentovana proxinou, tak lubovolny selekt na nu vrati tiez (v danej sessione) proxinu. Co uz vsak trosicku zavana problemom.<br /><br />Moze sa totiz stat situacia ze, niekto si dotiahne objekt, ktory obsahuje odkaz na danu entitu typu Animal. A nakolko tento odkaz/field je urceny na lazy loading vytvori sa pre danu entitu proxina. Dajme tomu ze toto urobila metoda A. Nasledne sa zavola niekde inde v kode (ale stale v tej istej transakcii/sessione) metoda B. A v metode B (ktoru pisal niekto iny ako metodu A) sa dotazeme na tuto Animal entitu. Hibernate nam vsak vrati proxinu, pod ktoru dotiahne uz konkretny objekt typu Dog (nakolko aj pre metodu A aj pre metodu B islo o tu istu entitu a MUSI to teba byt ten isty objekt). Bohuzial vsak tato proxina je typu Animal a nakolko my potrebujeme upravovat fieldy Dog-a, dostali sme sa do slepej ulicky. Najhorsie vsak je, ze v tomto pripade uz nepomoze ani LEFT JOIN FETCH na dane Animal.<br /><br />Toto je asi najcastejsie ako sa tato chyba prejavuje (hlavne ak sa na pisani kodu podiela viac ludi). Clovek si dotiahne objekt o ktorom vie ze je urciteho typu ale miesto toho mu pride len proxina typu superclass. A fakt netusi preco tomu tak je. A mozno len nahodou pride na to, ze predtym sa volal nejaky (mozno validacny kod) niekoho ineho, ktory sposobil, ze tato entita je do hibernate sessiony ulozena ako proxy a hibernate pri kazdom dalsom dotaze nic ine ako proxy nevrati.<br /><br />Hotovo :D<br /><br />No teda, skoro. Zostava tu totiz este jeden problem. Nakolko s tymto objektom potrebujeme nejako realne robit, tak nam ta proxina trosku vadi. Casto sa takato chyba odhali vo faze projektu, kedy nejaka rapidnejsia upravu designu predstavuje problem a tento problem je teda potrebne riesit nejak inak.<br /><br />V uvahu pripadaju take tri zakladne riesenia, pricom prve dve z nich su skoro az hnusne:<br /><ol><li>nacitat a spracovat entitu v novej sessione/transakcii (pre vacsinu pripadov nerealne riesenie)</li><li>nacitanu manazovanu entitu vyhodit zo session cache</li><ul><li>vyhodenim vsetkych objektov zo session cache (trosku drasticke riesenie)</li><li>nacitat problematicky objekt, vyhodit ho zo session cache a znovu ho nacitat (trosku hlupe riesenie)</li></ul><li>pouzit util triedu ktora sa o to postara (mnou preferovana varianta):</li></ol><pre name="code" class="brush: java"><br />public class ProxyUtil {<br /><br /> public static <T> T deproxy(final T object) {<br /> // deproxy object<br /> if (object == null) {<br /> return null;<br /> }<br /> return (object instanceof HibernateProxy) ?<br /> (T) ((HibernateProxy) object).getHibernateLazyInitializer().getImplementation() :<br /> object;<br /> }<br />}<br /></pre><br />Tato utilka zoberie ako vstup lubovolny objekt. Ak je tento objekt hibernate proxy, vrati objekt pod nou ulozeny, inak vrati nezmeneny objekt.<br /><br />Tak a teraz je to uz fakt vsetkoMatej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com5tag:blogger.com,1999:blog-2546038368249263371.post-77025954269642958892010-11-05T22:49:00.027+01:002014-01-02T03:22:43.998+01:00Optimistic locking on iBatisI know that it's little outdated, but I needed this recently on one project<br />
and couldn't find it anywhere in a Ctrl+C / Ctrl+V form.<br />
How to implement optimistic locking under iBatis when there is no standard support for it.<br />
<div>
<br /></div>
<div>
It's quite simple. Implement it by your self.</div>
<div>
<ul>
<li>implement custom OptimisticLockException or reuse the one from JPA package: javax.persistence</li>
<li>add new db field named 'version' into entity's table. it should be numerical non-null field with default value defined as 1</li>
<li>add new field into Entity's class: int version</li>
<li>Update DAO interface to throw OptimisticLockException (so anybody using this DAO can change his code accordingly)</li>
</ul>
<pre class="brush: java" name="code">
public interface EntityDao {
void createEntity(Entity entity);
Entity selectEntryById(Long id);
void updateEntity(Entity entity) throws OptimisticLockException;
void deleteEntity(Entity entity);
}</pre>
</div>
<ul>
<li>Update DAO class to process version fields</li>
</ul>
<pre class="brush: java" name="code">public class EntityDaoImpl extends SqlMapClientDaoSupport implements EntityDao {
@Override
public void createEntity(final Entity entity) {
getSqlMapClientTemplate().insert("insertEntity", entity);
entity.setVersion(1);
}
@Override
public Entity selectEntryById(final Long id) {
return (Entity) getSqlMapClientTemplate().queryForObject("findEntityById", id);
}
@Override
public void updateEntity(final Entity entity) throws OptimisticLockException {
final int oldVersion = entity.getVersion();
final int newVersion = oldVersion + 1;
final Map<String, Object> params = new HashMap<String, Object>();
params.put("entity", entity);
params.put("oldVersion", oldVersion);
params.put("newVersion", newVersion);
int updateCount = getSqlMapClientTemplate().update("updateEntity", params);
if (updateCount == 0) {
throw new OptimisticLockException("trying to update database with obsolete Entity");
}
entity.setVersion(newVersion);
}
@Override
public void deleteEntity(Entity entity) {
getSqlMapClientTemplate().delete("deleteEntity", entity);
}
}</pre>
<ul>
<li>Update statements file</li>
</ul>
<pre class="brush: xml" name="code">
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap>
<typeAlias alias="Entity" type="yourPackage.domain.Entity"/>
<resultMap id="EntityMap" class="Entity">
<result column="id" property="id"/>
<result column="value" property="value"/>
<result column="version" property="version"/>
</resultMap>
<insert id="insertEntity" parameterClass="Entity">
INSERT INTO entity (id, value, version) VALUES (#id#, #value#, 1)
</insert>
<select id="findEntityById" parameterClass="java.lang.Long" resultClass="Entity" resultMap="EntityMap">
SELECT id, value, version FROM entity WHERE id=#value#
</select>
<update id="updateEntity" parameterClass="java.util.Map">
UPDATE entity
SET
value=#entity.value#
version=#newVersion#
WHERE
id=#entity.id#
AND version=#oldVersion#
</update>
<delete id="deleteEntity" parameterClass="Entity">
DELETE FROM entity WHERE id=#id#
</delete>
</sqlMap></pre>
Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com2tag:blogger.com,1999:blog-2546038368249263371.post-13720460317672478122010-05-31T00:06:00.002+02:002010-05-31T00:14:15.120+02:00YES - easy Excel table processing in JavaNot long time ago I was looking for some library that would allow me to easily process data from Excel tables (although I like Apache POI, for my needs it was a little low level API).<br />But as I was not lucky while searching for a suitable solution I decided to implement a small but effective project (it was a nice facade over POI) that was based on heavy use of annotations.<br />Thanks to the usage of annotations, the complete code for simple table processing was about 10 to 20 lines long.<br /><br />You can find some more technical description on <a href="http://code.google.com/p/yava-excel-stripper/wiki/FAQ">this site</a>.Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com0tag:blogger.com,1999:blog-2546038368249263371.post-27989697020156848232010-05-09T01:00:00.047+02:002013-10-31T20:18:28.878+01:00LinkedArrayQueueNot long time ago I needed an implementation of a really <b>fast</b> (constant speed) and <b>small</b> (with as small memory footprint as posible) <b>queue</b> for storing multiple objects (<b>thread safety was not required</b>). For me there were two usable options where each of them fullfilled only one of these two requirements. ArrayList with its size and LinkedList with its speed.<br />
<div>
<br /></div>
<div>
<b>ArrayList</b> was good (from the memory point of view) because it <b>used array as</b> a mean of element <b>storage</b>. Its <b>disadvantage</b> however was that it was not a queue, so the <b>removal of its first element</b> always moves all elements (with the first one excluded) to a lower index. And as the speed of such operation is <b>O(n)</b> this was not acceptable for cases where you had too many elements (my case).</div>
<div>
<br /></div>
<div>
<table border="1"><tbody>
<tr><th><br /></th><th>remove first</th><th>insert last</th></tr>
<tr><th>ArrayList</th><td><b>O(n)</b></td><td>O(1) or <b>O(n)</b> when resizing</td></tr>
<tr><th>LinkedList</th><td>O(1)</td><td>O(1)</td></tr>
</tbody></table>
<br /></div>
<div>
The disadvantage of <b>LinkedList</b> (besides its slower insert) was that it <b>wrapped each inserted element</b> into a new wrapper and thus<b> increased the memory usage</b>.</div>
<div>
<br /></div>
<div>
As i needed something better I decided to implement my own queue. And so LinkedArrayQueue was born. </div>
<div>
<br /></div>
<div>
The design was quite simple: to <b>reuse</b> the principle of <b>LinkedList</b> (as this was more closely copying my expectations) and "fix" its memory issues. So <b style="font-weight: bold;">instead</b><span style="font-weight: bold;"> of</span> creating a <b>wrapper for each</b> new <b>element</b> I was <b>wrapping an array</b> that was suposed to hold the queue elements. When the array was fully written a new wrapper with array was created = O(1). When all elements from an array were <b>poll</b>ed/removed (<b>O(1)</b>) the array with its wrapper was thrown away = O(1). This way we'll get an <b>insert</b> (<b>O(1)<span class="Apple-style-span" style="font-weight: normal;">) </span><span class="Apple-style-span" style="font-weight: normal;">almost as fast as an insert into an array </span></b>and also <b>m-times less element wrappers</b> as in the LinkedList (where <b>m means the size of the wrapped array</b>). Also the creation of a new array with its wrapper takes a constant amount of time = O(1).</div>
<div>
<br /></div>
<div>
Here is a graphical representation of this concept:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwNha1EUlDeQmhRuRH2Aukv0rxBUsE48Cz9uFQMm0148PYP0dfoQXD1Upke7wzbvct4Ef6-zY-p-bgIFV78TRHWCRiYYtIjL4yDaGGUNIk07FHwLBHVaIvEfHlw3EY2ocaJzjg8Mf7l8Ob/s1600/LinkedArrayQueue.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img alt="" border="0" id="BLOGGER_PHOTO_ID_5469262212563075890" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwNha1EUlDeQmhRuRH2Aukv0rxBUsE48Cz9uFQMm0148PYP0dfoQXD1Upke7wzbvct4Ef6-zY-p-bgIFV78TRHWCRiYYtIjL4yDaGGUNIk07FHwLBHVaIvEfHlw3EY2ocaJzjg8Mf7l8Ob/s320/LinkedArrayQueue.png" style="cursor: pointer; height: 173px; width: 320px;" /></a><br />
<br />
And here are some interesting results I got on my machine (-Xmx64m, java 1.6.0_16):<br />
<br />
<table border="1"><tbody>
<tr><th><br /></th><th>inserts before OutOfMemory Error</th><th>insert last speed ns/element (of 1 milion inserts) - aprox. average</th><th>remove first speed ns/element (of 500 000 elements) - aprox. average</th></tr>
<tr><th>ArrayList</th><td>7 634 068</td><td>60 nanoseconds</td><td>220 092 nanoseconds</td></tr>
<tr><th>LinkedList</th><td>2 771 293</td><td>290 nanoseconds</td><td>22 nanoseconds</td></tr>
<tr><th>LinkedArrayQueue<br />
<span style="font-weight: normal;">(with array size 128)</span></th><td>15 202 048</td><td>35 nanoseconds</td><td>20 nanoseconds</td></tr>
</tbody></table>
<br />
As you can see with the same provided memory I was able to <span style="font-weight: bold;">store more elements</span> (in my case I was storing the same Integer n-times) and <span style="font-weight: bold;">much faster</span> that it was possible in LinkedList or ArrayList. Also the <span style="font-weight: bold;">poll/remove operation</span> was much faster than in ArrayList and only slightly <span style="font-weight: bold;">faster</span> than in LinkedList. And that is what I exactly wanted.</div>
<div>
<br />
So this is the design of my implementation. But <b>if you know of some better</b> (possibly existing) <b>solution just let me know</b>.</div>
Matej Tymeshttp://www.blogger.com/profile/01374039014087672059noreply@blogger.com2