View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.named.ipc;
20  
21  import java.util.ArrayDeque;
22  import java.util.Collection;
23  import java.util.Objects;
24  import java.util.concurrent.TimeUnit;
25  import java.util.concurrent.TimeoutException;
26  
27  import org.eclipse.aether.named.NamedLockKey;
28  import org.eclipse.aether.named.support.LockUpgradeNotSupportedException;
29  import org.eclipse.aether.named.support.NamedLockSupport;
30  
31  /**
32   * IPC named locks.
33   *
34   * @since 2.0.1
35   */
36  class IpcNamedLock extends NamedLockSupport {
37  
38      final IpcClient client;
39      final Collection<String> keys;
40      final ThreadLocal<ArrayDeque<Ctx>> contexts;
41  
42      IpcNamedLock(NamedLockKey key, IpcNamedLockFactory factory, IpcClient client, Collection<String> keys) {
43          super(key, factory);
44          this.client = client;
45          this.keys = keys;
46          this.contexts = ThreadLocal.withInitial(ArrayDeque::new);
47      }
48  
49      @Override
50      public boolean doLockShared(long time, TimeUnit unit) {
51          ArrayDeque<Ctx> contexts = this.contexts.get();
52          if (!contexts.isEmpty()) {
53              contexts.push(new Ctx(false, null, true));
54              return true;
55          }
56          try {
57              String contextId = client.newContext(true, time, unit);
58              client.lock(Objects.requireNonNull(contextId), keys, time, unit);
59              contexts.push(new Ctx(true, contextId, true));
60              return true;
61          } catch (TimeoutException e) {
62              return false;
63          }
64      }
65  
66      @Override
67      public boolean doLockExclusively(long time, TimeUnit unit) {
68          ArrayDeque<Ctx> contexts = this.contexts.get();
69          if (contexts.stream().anyMatch(c -> c.shared)) {
70              throw new LockUpgradeNotSupportedException(this);
71          }
72          if (!contexts.isEmpty()) {
73              contexts.push(new Ctx(false, null, false));
74              return true;
75          }
76          try {
77              String contextId = client.newContext(false, time, unit);
78              client.lock(Objects.requireNonNull(contextId), keys, time, unit);
79              contexts.push(new Ctx(true, contextId, false));
80              return true;
81          } catch (TimeoutException e) {
82              return false;
83          }
84      }
85  
86      @Override
87      public void doUnlock() {
88          ArrayDeque<Ctx> contexts = this.contexts.get();
89          if (contexts.isEmpty()) {
90              throw new IllegalStateException("improper boxing");
91          }
92          Ctx ctx = contexts.pop();
93          if (ctx.acted) {
94              client.unlock(ctx.contextId);
95          }
96      }
97  
98      private static final class Ctx {
99          private final boolean acted;
100         private final String contextId;
101         private final boolean shared;
102 
103         private Ctx(boolean acted, String contextId, boolean shared) {
104             this.acted = acted;
105             this.contextId = contextId;
106             this.shared = shared;
107         }
108     }
109 }