I have lots of legacy code, which to a large extent consists of classes with following structure:
public interface MyFunctionalBlock
{
// Setters for the inputs
void setInput1(final int aInput1);
void setInput2(final Object aInput2);
// Inside the method run inputs are converted into results
void run();
// If this building block needs functionality from some other building blocks,
// it gets a reference to them from the Google Guice injector.
void setInjector(final Injector aInjector);
// Getters for the results
long getResult1();
Object getResult2();
Map<String,String> getResult3();
}
public class MyFunctionalBlockFactory implements Factory<MyFunctionalBlock>
{
public MyFunctionalBlock create()
{
return new DefaultMyFunctionalBlock();
}
}
class DefaultMyFunctionalBlock implements MyFunctionalBlock
{
private int input1;
private Object input2;
private long result1;
private long result2;
private Map<String,String> result3;
private Injector injector;
@Override
public void run()
{
// Here the calculations are performed.
// If this functional block needs another one, it gets a reference to it using the injector.
// AnotherFunctionalBlock is the public interface. Implementations of the interface are
// intentionally hidden using injector and package-private declaration.
final AnotherFunctionalBlock fb = injector.getInstance(AnotherFunctionalBlock.class);
// First, we set the inputs
fb.setInput1(...);
fb.setInput2(...);
[...]
fb.setInputN(...);
// Now we run the calculation
fb.run();
// Now we can use the results
fb.getResult1();
fb.getResult2();
[...]
fb.getResultN();
}
// Implementation of getters and setters omitted
}
Basically, the entire application consists of such building blocks, which use each other.
Up to now, the application was used in a single-threaded mode. Now I need to modify it such that
- the building blocks are thread safe and
- the changes to the code, which uses them are minimal (ideally, I would change only the inner workings of the building blocks without touching the public interfaces and calling routines).
How can I do this?
I thought about putting the code from setting the first input to reading the last result into synchronized
block (something like the code example below), but it would require rewriting the entire application.
final AnotherFunctionalBlock fb = injector.getInstance(AnotherFunctionalBlock.class);
synchronized(fb)
{
fb.setInput1(...);
fb.setInput2(...);
[...]
fb.setInputN(...);
fb.run();
fb.getResult1();
fb.getResult2();
[...]
fb.getResultN();
}
Update 1 (09.06.2013 21:57 MSK): A potentially important note - the concurrency stems from the fact that there are N web services, which receive a request, then use the old code to make calculations based on that request and return the results to the web service client.
A potential solution would be to add some sort of queue between web services and the old code.
Update 2:
I thought about how to make my code thread-safe with minimum possible effort and found following solution (currently, I don't care about performance).
There are several web service classes, which all have a backend property and access it concurrently.
public class WebService1
{
private Backend backend;
public Response processRequest(SomeRequest1 request)
{
return wrapResultIntoResponse(backend.doSomeThreadUnsafeStuff1(request.getParameter1(), request.getParameter2()));
}
}
public class WebService2
{
private Backend backend;
public Response processRequest(SomeRequest2 request)
{
return wrapResultIntoResponse(backend.doSomeThreadUnsafeStuff2(request.getParameter1(), request.getParameter2(), request.getParameter3()));
}
}
All calls to the non-threadsafe code go via the Backend
class (all web services reference one and the same Backend
instance).
If I ensure that the backend processes one request after another (and never processes two requests simultaneously), I can achieve the desired result without re-writing the entire application.
Here's my implementation of Backend class:
public class Backend
{
private synchronized boolean busy = false;
public Object doSomeThreadUnsafeStuff1(Long aParameter1, String aParameter2)
{
waitUntilIdle();
synchronized (this)
{
busy=true;
// Here comes the non-thread safe stuff 1
busy=false;
notifyAll();
}
}
public Object doSomeThreadUnsafeStuff2(Long aParameter1, String aParameter2, Map<String,String> aParameter3)
{
waitUntilIdle();
synchronized (this)
{
busy=true;
// Here comes the non-thread safe stuff 2
busy=false;
notifyAll();
}
}
private void waitUntilIdle()
{
while (busy)
{
wait();
}
}
}
Can this solution work?