11package ru .mifi .practice .voln .object ;
22
3- import lombok .SneakyThrows ;
43import lombok .extern .slf4j .Slf4j ;
54import net .bytebuddy .ByteBuddy ;
65import net .bytebuddy .dynamic .loading .ClassLoadingStrategy ;
6+ import net .bytebuddy .dynamic .scaffold .subclass .ConstructorStrategy ;
77import net .bytebuddy .implementation .MethodDelegation ;
8+ import net .bytebuddy .implementation .bind .annotation .AllArguments ;
9+ import net .bytebuddy .implementation .bind .annotation .Origin ;
10+ import net .bytebuddy .implementation .bind .annotation .RuntimeType ;
811
912import java .io .Closeable ;
10- import java .lang .reflect .Field ;
13+ import java .io .IOException ;
14+ import java .lang .reflect .Constructor ;
15+ import java .lang .reflect .InvocationTargetException ;
16+ import java .lang .reflect .Method ;
1117import java .util .Optional ;
1218import java .util .concurrent .BlockingQueue ;
1319import java .util .concurrent .LinkedBlockingQueue ;
1420import java .util .concurrent .Semaphore ;
1521import java .util .concurrent .TimeUnit ;
22+ import java .util .concurrent .atomic .AtomicBoolean ;
1623import java .util .concurrent .atomic .AtomicInteger ;
1724import java .util .function .Consumer ;
1825import java .util .function .Predicate ;
1926import java .util .function .Supplier ;
2027
2128import static net .bytebuddy .matcher .ElementMatchers .isPublic ;
22- import static net .bytebuddy .matcher .ElementMatchers .named ;
23- import static net .bytebuddy .matcher .ElementMatchers .not ;
2429
2530public interface ObjectPool <T extends Closeable > extends Closeable {
2631 Optional <T > getObject (long timeout , TimeUnit unit );
@@ -31,6 +36,10 @@ default Optional<T> getObject() {
3136
3237 void dispose (T object );
3338
39+ interface ProxyMarker {
40+ Object getTarget ();
41+ }
42+
3443 @ SuppressWarnings ("PMD.CloseResource" )
3544 @ Slf4j
3645 final class Generic <T extends Closeable > implements ObjectPool <T > {
@@ -42,6 +51,7 @@ final class Generic<T extends Closeable> implements ObjectPool<T> {
4251 private final AtomicInteger createdCount ;
4352 private final Semaphore semaphore ;
4453 private final Class <T > type ;
54+ private final Object lock = new Object ();
4555
4656 public Generic (Supplier <T > creator ,
4757 Consumer <T > refresh ,
@@ -77,24 +87,32 @@ private void initializePool(int minSize) {
7787 }
7888 }
7989
80- @ SneakyThrows
8190 @ Override
8291 public Optional <T > getObject (long timeout , TimeUnit unit ) {
83- if (!semaphore .tryAcquire (timeout , unit )) {
92+ try {
93+ if (!semaphore .tryAcquire (timeout , unit )) {
94+ return Optional .empty ();
95+ }
96+ } catch (InterruptedException e ) {
97+ Thread .currentThread ().interrupt ();
8498 return Optional .empty ();
8599 }
86100
87101 try {
88102 T obj = pool .poll ();
89103 if (obj == null ) {
90- synchronized (this ) {
104+ synchronized (lock ) {
91105 if (createdCount .get () < maxSize ) {
92106 obj = creator .get ();
93107 createdCount .incrementAndGet ();
94108 }
95109 }
96110 if (obj == null ) {
97- obj = pool .poll (timeout , unit );
111+ try {
112+ obj = pool .poll (timeout , unit );
113+ } catch (InterruptedException e ) {
114+ Thread .currentThread ().interrupt ();
115+ }
98116 }
99117 }
100118
@@ -110,51 +128,50 @@ public Optional<T> getObject(long timeout, TimeUnit unit) {
110128 }
111129
112130 return Optional .of (Wrapper .proxy (this , obj , type ));
113- } catch (Exception e ) {
131+ } catch (RuntimeException e ) {
114132 semaphore .release ();
115133 throw e ;
134+ } catch (Exception e ) {
135+ semaphore .release ();
136+ throw new RuntimeException (e );
116137 }
117138 }
118139
119- @ SuppressWarnings ("PMD.AvoidAccessibilityAlteration" )
120140 @ Override
121141 public void dispose (T object ) {
122142 if (object == null ) {
123143 semaphore .release ();
124144 return ;
125145 }
126- try {
127- Field target = object .getClass ().getDeclaredField ("target" );
128- target .setAccessible (true );
129- if (target .get (object ).getClass ().equals (type )) {
130- object = (T ) target .get (object );
131- }
132- } catch (Exception e ) {
133- if (log .isErrorEnabled ()) {
134- log .error ("Error fetch field: {}" , e .getMessage ());
135- }
146+
147+ T target = object ;
148+ if (object instanceof ProxyMarker ) {
149+ target = (T ) ((ProxyMarker ) object ).getTarget ();
136150 }
137151
138152 try {
139- if (validator .test (object )) {
140- refresh .accept (object );
141- if (!pool .offer (object )) {
142- destroyObject (object );
153+ if (validator .test (target )) {
154+ refresh .accept (target );
155+ if (!pool .offer (target )) {
156+ destroyObject (target );
143157 }
144158 } else {
145- destroyObject (object );
159+ destroyObject (target );
146160 }
147161 } catch (Exception e ) {
148- destroyObject (object );
162+ destroyObject (target );
149163 } finally {
150164 semaphore .release ();
151165 }
152166 }
153167
154- @ SneakyThrows
155168 private void destroyObject (T object ) {
156169 try {
157170 object .close ();
171+ } catch (IOException e ) {
172+ if (log .isErrorEnabled ()) {
173+ log .error ("Error closing object: {}" , e .getMessage ());
174+ }
158175 } finally {
159176 createdCount .decrementAndGet ();
160177 }
@@ -169,28 +186,67 @@ public void close() {
169186 createdCount .set (0 );
170187 }
171188
172- private record Wrapper <T extends Closeable >(T target , ObjectPool <T > pool ) {
189+ private static final class Wrapper <T extends Closeable > {
190+ private final T target ;
191+ private final ObjectPool <T > pool ;
192+ private final AtomicBoolean closed = new AtomicBoolean (false );
193+
194+ private Wrapper (T target , ObjectPool <T > pool ) {
195+ this .target = target ;
196+ this .pool = pool ;
197+ }
173198
174- @ SuppressWarnings ("resource" )
175- @ SneakyThrows
199+ @ SuppressWarnings ("unchecked" )
176200 public static <T extends Closeable > T proxy (ObjectPool <T > pool , T target , Class <T > clazz ) {
177- return new ByteBuddy ()
178- . subclass ( clazz )
179- .method ( isPublic (). and ( not ( named ( "close" ))) )
180- .intercept ( MethodDelegation . to ( target ) )
181- .method (named ( "close" ))
182- .intercept (MethodDelegation .to (new Wrapper <>( target , pool ) ))
201+ Wrapper < T > wrapper = new Wrapper <>( target , pool );
202+ Class <? extends T > proxyClass = new ByteBuddy ( )
203+ .subclass ( clazz , ConstructorStrategy . Default . NO_CONSTRUCTORS )
204+ .implement ( ProxyMarker . class )
205+ .method (isPublic ( ))
206+ .intercept (MethodDelegation .to (wrapper ))
183207 .make ()
184208 .load (clazz .getClassLoader () != null ? clazz .getClassLoader () : ClassLoader .getSystemClassLoader (),
185209 ClassLoadingStrategy .Default .INJECTION )
186- .getLoaded ()
187- .getDeclaredConstructor ()
188- .newInstance ();
210+ .getLoaded ();
211+
212+ try {
213+ try {
214+ return proxyClass .getDeclaredConstructor ().newInstance ();
215+ } catch (NoSuchMethodException e ) {
216+ Constructor <?> objCtx = Object .class .getDeclaredConstructor ();
217+ Constructor <?> cons = sun .reflect .ReflectionFactory .getReflectionFactory ()
218+ .newConstructorForSerialization (proxyClass , objCtx );
219+ return (T ) cons .newInstance ();
220+ }
221+ } catch (Exception e ) {
222+ throw new RuntimeException ("Failed to create proxy" , e );
223+ }
189224 }
190225
191- @ SuppressWarnings ("unused" )
192- public void close () {
193- pool .dispose (target );
226+ @ RuntimeType
227+ public Object intercept (@ Origin Method method , @ AllArguments Object [] args ) throws Throwable {
228+ String name = method .getName ();
229+ int params = method .getParameterCount ();
230+
231+ if ("close" .equals (name ) && params == 0 ) {
232+ if (closed .compareAndSet (false , true )) {
233+ pool .dispose (target );
234+ }
235+ return null ;
236+ }
237+ if ("getTarget" .equals (name ) && params == 0 ) {
238+ return target ;
239+ }
240+
241+ if (closed .get ()) {
242+ throw new IllegalStateException ("Object pool proxy is closed" );
243+ }
244+
245+ try {
246+ return method .invoke (target , args );
247+ } catch (InvocationTargetException e ) {
248+ throw e .getCause ();
249+ }
194250 }
195251 }
196252 }
0 commit comments