0
votes

I need to execute task on remote machine.
This task is dummy Runnable or Callable and Serializable to be transferred to remote host, deserialized and executed there.
I need to use spring beans from that task to execute it on remote machine.

What could be the elegant way to 'serialize' bean name when task is serialized on client machine and 'deserialize' real bean while deserialization on remote machine?
Any other solutions?

2
Both machines share database?. - paul
yes, what is your suggestion? - Mike
Just send to the remote machine the id of the entity and load in the remote machine, just like a request/response from client-server web application - paul

2 Answers

1
votes
    private static class MyCommand implements Callable<String>, Serializable {
        private static final long serialVersionUID = 8980820796677215627L;
        private transient SpringBean springBean;
        private String bar;

        public InitDoneRemoteCommand(SpringBean springBean, String bar) {
            this.springBean = springBean;
            this.bar = bar;
        }

        @Override
        public String call() {
            return springBean.foo(bar);
        }

        private void writeObject(java.io.ObjectOutputStream out) throws IOException {
            out.defaultWriteObject();
            out.writeObject(getBeanName(springBean));
        }

        private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
            in.defaultReadObject();
            springBean = getBean((String) in.readObject());
        }
    }

SpringContext .java

    @Resource
    public class SpringContext implements ApplicationContextAware, BeanPostProcessor, BundleContextAware, ServiceListener {
        private static ApplicationContext applicationContext;
        private static BundleContext bundleContext;
        private static Map<Object, String> springBeanToName = synchronizedMap(new WeakHashMap<Object, String>());
        private static Map<String, ServiceReference> osgiNameToServiceReference = synchronizedMap(new WeakHashMap<String, ServiceReference>());

        public static ApplicationContext getApplicationContext() {
            return applicationContext;
        }

        public static BundleContext getBundleContext() {
            return bundleContext;
        }

        @SuppressWarnings("unchecked")
        public static <T> T getBean(String name) {
            ServiceReference ref = osgiNameToServiceReference.get(name);
            if (ref != null)
                return (T) bundleContext.getService(ref);
            return (T) applicationContext.getBean(name);
        }

        public static String getBeanName(Object bean) {
            if (isOsgiBean(bean))
                return getOsgiBeanName(bean);
            return springBeanToName.get(bean);
        }

        public static boolean isOsgiBean(Object bean) {
            return bean instanceof ImportedOsgiServiceProxy || bean instanceof ServiceReferenceProxy || bean instanceof ServiceReference;
        }

        public static String getOsgiBeanName(Object proxy) {
            if (proxy == null)
                return null;
            ServiceReference serviceReference = null;
            if (proxy instanceof ImportedOsgiServiceProxy)
                serviceReference = ((ImportedOsgiServiceProxy) proxy).getServiceReference().getTargetServiceReference();
            else if (proxy instanceof ServiceReferenceProxy)
                serviceReference = ((ServiceReferenceProxy) proxy).getTargetServiceReference();
            else if (proxy instanceof ServiceReference)
                serviceReference = ((ServiceReference) proxy);
            if (serviceReference != null)
                return (String) serviceReference.getProperty(OSGI_BEAN_NAME_PROPERTY);
            throw new IllegalArgumentException(proxy.toString());
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }

        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            springBeanToName.put(bean, beanName);
            return bean;
        }

        @Override
        public void serviceChanged(ServiceEvent event) {
            ServiceReference ref = event.getServiceReference();
            String name = getOsgiBeanName(ref);
            if (event.getType() == ServiceEvent.REGISTERED)
                osgiNameToServiceReference.put(name, ref);
            else if (event.getType() == ServiceEvent.UNREGISTERING)
                osgiNameToServiceReference.remove(name);
        }

        @Override
        public void setApplicationContext(ApplicationContext context) throws BeansException {
            SpringContext.applicationContext = context;
        }

        @Override
        public void setBundleContext(BundleContext bundleContext) {
            SpringContext.bundleContext = bundleContext;
            bundleContext.addServiceListener(this);
        }
    }
0
votes

If you have access to the ApplicationContext you can ask it to create the instance for you, which will e.g. enable autowiring:

appContext.getAutowireCapableBeanFactory().createBean(
    beanClass, 
    AbstractBeanDefinition.AUTOWIRE_BY_TYPE, 
    true)

A more elegant way would be to annotate the class with @Configurable, described here.