Mykhaylo Adamovych
Mykhaylo Adamovych

Reputation: 20976

Use spring beans from Serializable objects

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?

Upvotes: 0

Views: 1461

Answers (2)

Mykhaylo Adamovych
Mykhaylo Adamovych

Reputation: 20976

    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);
        }
    }

Upvotes: 1

marthursson
marthursson

Reputation: 3300

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.

Upvotes: 0

Related Questions