ModuleArbiterImpl.java
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 callthrows  InterruptException {
058         call.pause();
059         call.getInternalServiceProcess().setBlocked(true);
060         try {
061             final boolean result = lockRequiredModule(call.getInternalServiceProcess(), call.getQedeq(),
062                 call.getService());
063             ((InternalModuleServiceCallImplcall).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 servicethrows  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 && ((InternalModuleServiceCallImplcall).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         ((DefaultKernelQedeqBoqedeq).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         ((DefaultKernelQedeqBoqedeq).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 (ServiceJobblocked.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.Entryi.next();
191             QedeqBo qedeq = (QedeqBoentry.getKey();
192             if (process.equals(entry.getValue())) {
193                 result.add(qedeq);
194             }
195         }
196         return result;
197     }
198 
199 }