/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.util; import com.liferay.portal.kernel.memory.FinalizeAction; import com.liferay.portal.kernel.memory.FinalizeManager; import com.liferay.registry.Registry; import com.liferay.registry.RegistryUtil; import com.liferay.registry.ServiceTracker; import com.liferay.registry.ServiceTrackerCustomizer; import com.liferay.registry.ServiceTrackerFieldUpdaterCustomizer; import java.lang.ref.Reference; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author Tina Tian */ public class ServiceProxyFactory { public static <T> T newServiceTrackedInstance( Class<T> serviceClass, Class<?> declaringClass, String fieldName, boolean blocking) { return newServiceTrackedInstance( serviceClass, declaringClass, fieldName, null, blocking, false); } public static <T> T newServiceTrackedInstance( Class<T> serviceClass, Class<?> declaringClass, String fieldName, boolean blocking, boolean useNullAsDummyService) { return newServiceTrackedInstance( serviceClass, declaringClass, fieldName, null, blocking, useNullAsDummyService); } public static <T> T newServiceTrackedInstance( Class<T> serviceClass, Class<?> declaringClass, String fieldName, String filterString, boolean blocking) { return newServiceTrackedInstance( serviceClass, declaringClass, fieldName, filterString, blocking, false); } public static <T> T newServiceTrackedInstance( Class<T> serviceClass, Class<?> declaringClass, String fieldName, String filterString, boolean blocking, boolean useNullAsDummyService) { try { Field field = declaringClass.getDeclaredField(fieldName); if (!Modifier.isStatic(field.getModifiers())) { throw new IllegalArgumentException(field + " is not static"); } field.setAccessible(true); return _newServiceTrackedInstance( serviceClass, null, field, filterString, blocking, useNullAsDummyService); } catch (ReflectiveOperationException roe) { return ReflectionUtil.throwException(roe); } } public static <T, V> T newServiceTrackedInstance( Class<T> serviceClass, Class<V> declaringClass, V declaringInstance, String fieldName, String filterString, boolean blocking) { if (declaringInstance == null) { return newServiceTrackedInstance( serviceClass, declaringClass, fieldName, filterString, blocking, false); } try { Field field = declaringClass.getDeclaredField(fieldName); if (Modifier.isStatic(field.getModifiers())) { throw new IllegalArgumentException(field + " is static"); } field.setAccessible(true); T serviceInstance = null; synchronized (declaringInstance) { serviceInstance = (T)field.get(declaringInstance); if (serviceInstance == null) { return _newServiceTrackedInstance( serviceClass, declaringInstance, field, filterString, blocking, false); } } return serviceInstance; } catch (ReflectiveOperationException roe) { return ReflectionUtil.throwException(roe); } } private static <T, V> T _newServiceTrackedInstance( Class<T> serviceClass, V declaringInstance, Field field, String filterString, boolean blocking, boolean useNullAsDummyService) throws ReflectiveOperationException { ServiceTrackerCustomizer<T, T> serviceTrackerCustomizer = null; if (blocking) { ReentrantLock lock = new ReentrantLock(); Condition realServiceSet = lock.newCondition(); T awaitService = (T)ProxyUtil.newProxyInstance( serviceClass.getClassLoader(), new Class<?>[] {serviceClass}, new AwaitServiceInvocationHandler(field, realServiceSet, lock)); field.set(declaringInstance, awaitService); serviceTrackerCustomizer = new AwaitServiceTrackerFieldUpdaterCustomizer<>( field, declaringInstance, awaitService, realServiceSet, lock); } else { T dummyService = null; if (!useNullAsDummyService) { dummyService = ProxyFactory.newDummyInstance(serviceClass); field.set(declaringInstance, dummyService); } serviceTrackerCustomizer = new ServiceTrackerFieldUpdaterCustomizer<>( field, declaringInstance, dummyService); } ServiceTracker<T, T> serviceTracker = _openServiceTracker( serviceClass, filterString, serviceTrackerCustomizer); if (declaringInstance != null) { FinalizeManager.register( declaringInstance, new CloseServiceTrackerFinalizeAction(serviceTracker), FinalizeManager.PHANTOM_REFERENCE_FACTORY); } return (T)field.get(declaringInstance); } private static <T> ServiceTracker<T, T> _openServiceTracker( Class<T> serviceClass, String filterString, ServiceTrackerCustomizer<T, T> serviceTrackerCustomizer) { ServiceTracker<T, T> serviceTracker = null; String serviceName = serviceClass.getName(); Registry registry = RegistryUtil.getRegistry(); if (Validator.isNull(filterString)) { serviceTracker = registry.trackServices( serviceName, serviceTrackerCustomizer); } else { StringBundler sb = new StringBundler(5); sb.append("(&(objectClass="); sb.append(serviceName); sb.append(StringPool.CLOSE_PARENTHESIS); sb.append(filterString); sb.append(StringPool.CLOSE_PARENTHESIS); serviceTracker = registry.trackServices( registry.getFilter(sb.toString()), serviceTrackerCustomizer); } serviceTracker.open(); return serviceTracker; } private static class AwaitServiceInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { while (true) { _lock.lock(); try { Object service = _field.get(null); if (!ProxyUtil.isProxyClass(service.getClass()) || (ProxyUtil.getInvocationHandler(service) != this)) { return method.invoke(service, arguments); } _realServiceSet.await(); } finally { _lock.unlock(); } } } private AwaitServiceInvocationHandler( Field field, Condition realServiceSet, Lock lock) { _field = field; _realServiceSet = realServiceSet; _lock = lock; } private final Field _field; private final Lock _lock; private final Condition _realServiceSet; } private static class AwaitServiceTrackerFieldUpdaterCustomizer<S, T> extends ServiceTrackerFieldUpdaterCustomizer<S, T> { @Override protected void doServiceUpdate(T newService) { _lock.lock(); try { super.doServiceUpdate(newService); if (newService != _awaitService) { _realServiceSet.signalAll(); } } finally { _lock.unlock(); } } private AwaitServiceTrackerFieldUpdaterCustomizer( Field serviceField, Object serviceHolder, T awaitService, Condition realServiceSet, Lock lock) { super(serviceField, serviceHolder, awaitService); _awaitService = awaitService; _realServiceSet = realServiceSet; _lock = lock; } private final T _awaitService; private final Lock _lock; private final Condition _realServiceSet; } private static class CloseServiceTrackerFinalizeAction implements FinalizeAction { @Override public void doFinalize(Reference<?> reference) { _serviceTracker.close(); } private CloseServiceTrackerFinalizeAction( ServiceTracker<?, ?> serviceTracker) { _serviceTracker = serviceTracker; } private final ServiceTracker<?, ?> _serviceTracker; } }