001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002 *
003 * Copyright 2000-2014, Michael Meyling <mime@qedeq.org>.
004 *
005 * "Hilbert II" is free software; you can redistribute
006 * it and/or modify it under the terms of the GNU General Public
007 * License as published by the Free Software Foundation; either
008 * version 2 of the License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU General Public License for more details.
014 */
015
016 package org.qedeq.kernel.bo.service.internal;
017
018 import java.util.HashMap;
019 import java.util.Iterator;
020 import java.util.Map;
021
022 import org.qedeq.base.trace.Trace;
023 import org.qedeq.base.utility.StringUtility;
024 import org.qedeq.kernel.bo.common.QedeqBo;
025 import org.qedeq.kernel.bo.common.QedeqBoSet;
026 import org.qedeq.kernel.bo.common.ServiceJob;
027 import org.qedeq.kernel.bo.job.InternalModuleServiceCallImpl;
028 import org.qedeq.kernel.bo.module.InternalModuleServiceCall;
029 import org.qedeq.kernel.bo.module.InternalServiceJob;
030 import org.qedeq.kernel.bo.module.ModuleArbiter;
031 import org.qedeq.kernel.se.common.Service;
032 import org.qedeq.kernel.se.visitor.InterruptException;
033
034 /**
035 * Get locks for modules.
036 * TODO 20130508 m31: Currently we make no difference between read and write locks. We also lock
037 * a module during the whole plugin processing for that module. This could be limited to status
038 * changes only.
039 *
040 * @author Michael Meyling
041 */
042 public class ModuleArbiterImpl implements ModuleArbiter {
043
044 /** This class. */
045 private static final Class CLASS = ModuleArbiterImpl.class;
046
047 /** Map blocked QEDEQ modules to service processes. */
048 private final Map blocked;
049
050 /**
051 * Constructor.
052 */
053 public ModuleArbiterImpl() {
054 blocked = new HashMap();
055 }
056
057 public boolean lockRequiredModule(final InternalModuleServiceCall call) throws InterruptException {
058 call.pause();
059 call.getInternalServiceProcess().setBlocked(true);
060 try {
061 final boolean result = lockRequiredModule(call.getInternalServiceProcess(), call.getQedeq(),
062 call.getService());
063 ((InternalModuleServiceCallImpl) call).setNewlyBlockedModule(result);
064 return result;
065 } finally {
066 call.getInternalServiceProcess().setBlocked(false);
067 call.resume();
068 }
069 }
070
071 private boolean lockRequiredModule(final InternalServiceJob process,
072 final QedeqBo qedeq, final Service service) throws InterruptException {
073 if (isAlreadyLocked(process, qedeq)) {
074 return false;
075 }
076 process.setBlocked(true);
077 // we try to get a lock, if not we wait
078 try {
079 while (!lock(process, qedeq, service)) {
080 final Object monitor = new Object();
081 synchronized (monitor) {
082 try {
083 monitor.wait(1000);
084 } catch (InterruptedException e) {
085 process.setInterruptedState();
086 throw new InterruptException(qedeq.getModuleAddress().createModuleContext());
087 }
088 }
089 if (Thread.interrupted()) {
090 process.setInterruptedState();
091 throw new InterruptException(qedeq.getModuleAddress().createModuleContext());
092 }
093 }
094 } finally {
095 process.setBlocked(false);
096 }
097 return true;
098 }
099
100 public boolean unlockRequiredModule(final InternalModuleServiceCall call) {
101 // TODO 20130521 m31: do it without cast
102 if (call != null && ((InternalModuleServiceCallImpl) call).getNewlyBlockedModule()) {
103 return unlockRequiredModule(call.getInternalServiceProcess(), call.getQedeq());
104 }
105 return false;
106 }
107
108 public boolean unlockRequiredModule(final ServiceJob process, final QedeqBo qedeq) {
109 return unlock(process, qedeq);
110
111 }
112
113 private synchronized boolean lock(final ServiceJob process, final QedeqBo qedeq, final Service service) {
114 final String method = "lock";
115 if (Trace.isTraceOn()) {
116 Trace.info(CLASS, this, method, getName(process) + " is trying to lock " + qedeq.getName());
117 }
118 final ServiceJob origin = getProcess(qedeq);
119 if (origin != null) {
120 if (Trace.isTraceOn()) {
121 Trace.info(CLASS, this, method, getName(process) + " failed to lock " + qedeq.getName()
122 + "\tbecause it is locked by " + getName(origin));
123 }
124 return false;
125 }
126 addLock(process, qedeq);
127 ((DefaultKernelQedeqBo) qedeq).setCurrentlyRunningService(service);
128 return true;
129 }
130
131 private synchronized boolean unlock(final ServiceJob process, final QedeqBo qedeq) {
132 final String method = "unlock";
133 if (Trace.isTraceOn()) {
134 Trace.info(CLASS, this, method, getName(process) + " is trying to unlock " + qedeq.getName());
135 }
136 ((DefaultKernelQedeqBo) qedeq).setCurrentlyRunningService(null);
137 final ServiceJob origin = getProcess(qedeq);
138 if (origin != null) {
139 if (origin.equals(process)) {
140 removeLock(process, qedeq);
141 return true;
142 } else {
143 RuntimeException e = new IllegalArgumentException(getName(process) + " illegal unlock try for "
144 + qedeq.getName() + " which is currently locked by service process " + getName(origin));
145 Trace.fatal(CLASS, this, method, "Programming error. Unallowed unlock try.", e);
146 // TODO 20130324 m31: later on we might handle this differently but for now:
147 throw e;
148 }
149 } else {
150 if (Trace.isTraceOn()) {
151 Trace.info(CLASS, this, method, getName(process) + " unlock unnecessary " + qedeq.getName());
152 }
153 return false;
154 }
155 }
156
157 private String getName(final ServiceJob process) {
158 return StringUtility.format(process.getId(), 3) + " "
159 + (process.getModuleServiceCall() != null ? StringUtility.getLastDotString(
160 process.getModuleServiceCall().getService().getServiceId()) : "");
161 }
162
163 private synchronized ServiceJob getProcess(final QedeqBo qedeq) {
164 return (ServiceJob) blocked.get(qedeq);
165 }
166
167 private synchronized boolean isAlreadyLocked(final ServiceJob process,
168 final QedeqBo qedeq) {
169 return process.equals(blocked.get(qedeq));
170 }
171
172 private synchronized void addLock(final ServiceJob process, final QedeqBo qedeq) {
173 if (Trace.isTraceOn()) {
174 Trace.info(CLASS, this, "addLock", getName(process) + " locked successfuly " + qedeq.getName());
175 }
176 blocked.put(qedeq, process);
177 }
178
179 private synchronized void removeLock(final ServiceJob process, final QedeqBo qedeq) {
180 if (Trace.isTraceOn()) {
181 Trace.info(CLASS, this, "removeLock", getName(process) + " unlocked " + qedeq.getName());
182 }
183 blocked.remove(qedeq);
184 }
185
186 public synchronized QedeqBoSet getBlockedModules(final ServiceJob process) {
187 QedeqBoSet result = new QedeqBoSet();
188 final Iterator i = blocked.entrySet().iterator();
189 while (i.hasNext()) {
190 final Map.Entry entry = (Map.Entry) i.next();
191 QedeqBo qedeq = (QedeqBo) entry.getKey();
192 if (process.equals(entry.getValue())) {
193 result.add(qedeq);
194 }
195 }
196 return result;
197 }
198
199 }
|