public class ServicesRunner {
public static final String SERVICE_ONE = "Service One";
public static final String SERVICE_TWO = "Service Two";
// ...many other similar strings...
public static final String SERVICE_N = "Umpteenth service";
public static final String UNKNOWN_SERVICE = "Unknown Service";
public String getServiceStatus(String serviceName) {
if (serviceName.equals(SERVICE_ONE) {
return getServiceOneStatus;
} else if (serviceName.equals(SERVICE_TWO) {
return getServiceTwoStatus;
}
// several more else ifs...
else {
return UNKNOWN_SERVICE;
}
}
private String getServiceOneStatus() {
if (ServiceOne.getRunner() != null && ServiceOne.getRunner().isActive()) }
return RUNNING;
} else {
return NOT_RUNNING;
}
}
// all other getServiceXStatus are similar (VERY similar... TOO MUCH similar)
public String runService(String ServiceName, MyDataObject myDataObject) {
if (serviceName.equals(SERVICE_ONE) {
return runServiceOne(myDataObject);
} else if ((serviceName.equals(SERVICE_TWO) {
return runServiceTwo(myDataObject);
}
// you guessed right... and so on and so forth
else {
return UNKNOWN_SERVICE
{
}
private String runServiceOne(MyDataObject data) {
String result = RUNNING;
try {
ServiceOne runner = new ServiceOne();
runner.setX(data.getX);
runner.setY(data.getY);
// etc.
runner.start();
} catch (Throwable t) {
result= t.getMessage();
}
return result
}
// all other run methods are constructed in the same way
}
public class ServiceOne {
public static ServiceOne runner = new ServiceOne();
private boolean active;
private void setActive(boolean active) {
this.active = active;
}
public boolean isActive() {
return active;
}
public void run() {
synchronized(serviceOne) { // Synchronization on a non-final field
if(runner != null && runner.isActive()) {
throw new RuntimeException("service already active");
} else {
runner = this;
runner.setActive(true);
}
}
try {
runner.myBusinessStuff();
} catch(Throwable t) {
t.printStackTrace();
} finally {
runner.setActive(false);
}
}
private void myBusinessStuff() {
// do all that pointless stuff that makes our managers rich
}
}
public class myServiceX {
// just like the previous one, with a different runner and different business stuff
// rinse and repeat for each service
}
Refactorings
No refactoring yet !
Stefan Vartolomeev
September 2, 2010, September 02, 2010 11:39, permalink
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
public class ServicesRunner implements Observer{
public static final String UNKNOWN_SERVICE = "Unknown Service";
private static final String RUNNING = "Running";
private static final String NOT_RUNNING = "Not running";
private Map<String, Runnable> runningServices;
private ServiceFactory serviceFactory;
public ServicesRunner(ServiceFactory serviceFactory) {
runningServices = Collections.synchronizedMap(new HashMap<String, Runnable>());
this.serviceFactory = serviceFactory;
}
public String getServiceStatus(String serviceName) {
Runnable service = runningServices.get(serviceName);
if (service != null) {
return RUNNING;
} else {
return NOT_RUNNING;
}
}
public void runService(String serviceName, MyDataObject myDataObject) {
serviceFactory = new ServiceFactory();
Runnable service = serviceFactory.getService(serviceName, myDataObject);
runningServices.put(serviceName, service);
new Thread(service).start();
}
@Override
public void update(Observable o, Object arg) {
for (String key : runningServices.keySet()) {
if (runningServices.get(key).getClass().equals(o.getClass())) {
runningServices.remove(key);
return;
}
}
}
}
import java.util.EnumMap;
import java.util.Map;
public class ServiceFactory {
private Map<ServiceType, Class<? extends Runnable>> services;
public ServiceFactory() {
services = new EnumMap<ServiceType, Class<? extends Runnable>>(ServiceType.class);
init();
}
private void init() {
services.put(ServiceType.SERVICE_ONE, ServiceOne.class);
services.put(ServiceType.SERVICE_TWO, ServiceTwo.class);
}
public Runnable getService(String serviceName, MyDataObject myDataObject) {
Class<? extends Runnable> serviceClass = getServiceClassForName(serviceName);
return createInstance(serviceClass);
}
private Class<? extends Runnable> getServiceClassForName(String serviceName) {
ServiceType serviceType = ServiceType.valueOf(serviceName);
if (serviceType == null) throw new RuntimeException("Service type " + serviceName + " not found");
Class<? extends Runnable> serviceClass = services.get(serviceType);
if (serviceClass == null) throw new RuntimeException("Service for type <" + serviceType.toString() + "> not decleared");
return serviceClass;
}
private Runnable createInstance(Class<? extends Runnable> serviceClass) {
try {
return serviceClass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public enum ServiceType {
SERVICE_ONE, SERVICE_TWO, SERVICE_THREE;
}
import java.util.Observable;
public class ServiceOne extends Observable implements Runnable {
public void run() {
try {
myBusinessStuff();
} catch(Throwable t) {
t.printStackTrace();
} finally {
notifyObservers();
}
}
private void myBusinessStuff() {
System.out.println("ServiceOne");
}
}
The ServicesRunner start the appropriate thread and returns its state. It has a lot of duplicated code, all the getServiceXStatus and the runServiceX methods.
The single Service does all the business stuff, but contains part of the logic used in the ServicesRunner: all the code that returns the state of a service should be either in the runner or in the service. Besides, the use of a static field to control threading prevents the use of a subclass for an AbstractService (at least with this mechanism).
I'd like to separate the business operations from the threading control for each service. If a service is running the user cannot have another instance of the same service until the first one terminates, and serializing threads is not allowed (the user should first check the output of the terminated service).
Moreover, I'd like to test the logic beyond the ServicesRunner without actually having to instantiate and run a real service.