001    package org.apache.ojb.broker.locking;
002    
003    /* Copyright 2004-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.io.BufferedOutputStream;
019    import java.io.ByteArrayOutputStream;
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.ObjectInputStream;
023    import java.io.ObjectOutputStream;
024    import java.io.Serializable;
025    import java.net.HttpURLConnection;
026    import java.net.MalformedURLException;
027    import java.net.ProtocolException;
028    import java.net.URL;
029    
030    import org.apache.ojb.broker.util.configuration.Configurable;
031    import org.apache.ojb.broker.util.configuration.Configuration;
032    import org.apache.ojb.broker.util.configuration.ConfigurationException;
033    import org.apache.ojb.broker.util.logging.Logger;
034    import org.apache.ojb.broker.util.logging.LoggerFactory;
035    
036    /**
037     * This implementation of the {@link LockManager} interface supports locking
038     * in distributed environments in combination with a specific lock servlet.
039     *
040     * @see LockManagerServlet
041     * @version $Id: LockManagerRemoteImpl.java,v 1.1 2007-08-24 22:17:41 ewestfal Exp $
042     */
043    public class LockManagerRemoteImpl implements LockManager, Configurable
044    {
045        private Logger log = LoggerFactory.getLogger(LockManagerRemoteImpl.class);
046    
047        public static final byte METHOD_READ_LOCK = 'a';
048        public static final byte METHOD_WRITE_LOCK = 's';
049        public static final byte METHOD_UPGRADE_LOCK = 'u';
050        public static final byte METHOD_CHECK_READ = 'r';
051        public static final byte METHOD_CHECK_WRITE = 'w';
052        public static final byte METHOD_CHECK_UPGRADE = 'v';
053        public static final byte METHOD_RELEASE_SINGLE_LOCK = 'e';
054        public static final byte METHOD_RELEASE_LOCKS = 'x';
055        public static final byte METHOD_LOCK_INFO = 'i';
056        public static final byte METHOD_LOCK_TIMEOUT = 't';
057        public static final byte METHOD_LOCK_TIMEOUT_SET = 'y';
058        public static final byte METHOD_BLOCK_TIMEOUT = 'c';
059        public static final byte METHOD_BLOCK_TIMEOUT_SET = 'd';
060    
061        private static URL lockservlet = null;
062    
063        public LockManagerRemoteImpl()
064        {
065        }
066    
067        /**
068         * @see org.apache.ojb.broker.util.configuration.Configurable#configure(org.apache.ojb.broker.util.configuration.Configuration)
069         */
070        public void configure(Configuration pConfig) throws ConfigurationException
071        {
072            String url = pConfig.getString("LockServletUrl", "http://127.0.0.1:8080/ojb-lockserver");
073            log.info("Lock server servlet URL: " + url);
074            try
075            {
076                lockservlet = new URL(url);
077            }
078            catch(MalformedURLException e)
079            {
080                throw new ConfigurationException("Invalid LockServlet Url was specified: " + url, e);
081            }
082    
083        }
084    
085        /**
086         * noop
087         * @param timeout
088         */
089        public void setLockTimeout(long timeout)
090        {
091    //        LockInfo info = new LockInfo(timeout, METHOD_LOCK_TIMEOUT_SET);
092    //        try
093    //        {
094    //            byte[] requestBarr = serialize(info);
095    //            performRequestObject(requestBarr);
096    //        }
097    //        catch(Throwable t)
098    //        {
099    //            throw new LockRuntimeException("Can't set locking timeout", t);
100    //        }
101        }
102    
103        public long getLockTimeout()
104        {
105            LockInfo info = new LockInfo(METHOD_LOCK_TIMEOUT);
106            try
107            {
108                byte[] requestBarr = serialize(info);
109                return performRequestLong(requestBarr);
110            }
111            catch(Throwable t)
112            {
113                throw new LockRuntimeException("Can't get locking info", t);
114            }
115        }
116    
117        public long getBlockTimeout()
118        {
119            LockInfo info = new LockInfo(METHOD_BLOCK_TIMEOUT);
120            try
121            {
122                byte[] requestBarr = serialize(info);
123                return performRequestLong(requestBarr);
124            }
125            catch(Throwable t)
126            {
127                throw new LockRuntimeException("Can't get block timeout value", t);
128            }
129        }
130    
131        /**
132         * noop
133         */ 
134        public void setBlockTimeout(long timeout)
135        {
136    //        LockInfo info = new LockInfo(timeout, METHOD_BLOCK_TIMEOUT_SET);
137    //        try
138    //        {
139    //            byte[] requestBarr = serialize(info);
140    //            performRequestObject(requestBarr);
141    //        }
142    //        catch(Throwable t)
143    //        {
144    //            throw new LockRuntimeException("Can't set block timeout value", t);
145    //        }
146        }
147    
148        public String getLockInfo()
149        {
150            LockInfo info = new LockInfo(METHOD_LOCK_INFO);
151            try
152            {
153                byte[] requestBarr = serialize(info);
154                return performRequestString(requestBarr);
155            }
156            catch(Throwable t)
157            {
158                throw new LockRuntimeException("Can't get locking info", t);
159            }
160        }
161    
162        public boolean readLock(Object key, Object resourceId, int isolationLevel)
163        {
164            LockInfo info = new LockInfo(key, resourceId, isolationLevel, METHOD_READ_LOCK);
165            try
166            {
167                byte[] requestBarr = serialize(info);
168                return performRequest(requestBarr);
169            }
170            catch(Throwable t)
171            {
172                throw new LockRuntimeException("Cannot check read lock for '"
173                        + resourceId + "' using key '" + key + "'", t);
174            }
175        }
176    
177        public boolean releaseLock(Object key, Object resourceId)
178        {
179            LockInfo info = new LockInfo(key, resourceId, METHOD_RELEASE_SINGLE_LOCK);
180            try
181            {
182                byte[] requestBarr = serialize(info);
183                return performRequest(requestBarr);
184            }
185            catch(Throwable t)
186            {
187                throw new LockRuntimeException("Cannot remove write lock for '"
188                        + resourceId + "' using key '" + key + "'", t);
189            }
190        }
191    
192    //    public boolean removeReader(Object key, Object resourceId)
193    //    {
194    //        LockInfo info = new LockInfo(key, resourceId, METHOD_RELEASE_SINGLE_LOCK);
195    //        try
196    //        {
197    //            byte[] requestBarr = serialize(info);
198    //            return performRequest(requestBarr);
199    //        }
200    //        catch(Throwable t)
201    //        {
202    //            throw new LockRuntimeException("Cannot remove read lock for '"
203    //                    + resourceId + "' using key '" + key + "'", t);
204    //        }
205    //    }
206    
207        public void releaseLocks(Object key)
208        {
209            LockInfo info = new LockInfo(key, null, METHOD_RELEASE_LOCKS);
210            try
211            {
212                byte[] requestBarr = serialize(info);
213                performRequest(requestBarr);
214            }
215            catch(Throwable t)
216            {
217                throw new LockRuntimeException("Cannot release locks using owner key '" + key + "'", t);
218            }
219        }
220    
221        public boolean writeLock(Object key, Object resourceId, int isolationLevel)
222        {
223            LockInfo info = new LockInfo(key, resourceId, isolationLevel, METHOD_WRITE_LOCK);
224            try
225            {
226                byte[] requestBarr = serialize(info);
227                return performRequest(requestBarr);
228            }
229            catch(Throwable t)
230            {
231                throw new LockRuntimeException("Cannot set write lock for '"
232                        + resourceId + "' using key '" + key + "'", t);
233            }
234        }
235    
236        public boolean upgradeLock(Object key, Object resourceId, int isolationLevel)
237        {
238            LockInfo info = new LockInfo(key, resourceId, isolationLevel, METHOD_UPGRADE_LOCK);
239            try
240            {
241                byte[] requestBarr = serialize(info);
242                return performRequest(requestBarr);
243            }
244            catch(Throwable t)
245            {
246                throw new LockRuntimeException("Cannot set write lock for '"
247                        + resourceId + "' using key '" + key + "'", t);
248            }
249        }
250    
251        public boolean hasRead(Object key, Object resourceId)
252        {
253            try
254            {
255                byte[] requestBarr = serialize(new LockInfo(key, resourceId, METHOD_CHECK_READ));
256                return performRequest(requestBarr);
257            }
258            catch(Throwable t)
259            {
260                throw new LockRuntimeException("Cannot check read lock for '"
261                        + resourceId + "' using key '" + key + "'", t);
262            }
263        }
264    
265        public boolean hasWrite(Object key, Object resourceId)
266        {
267            try
268            {
269                byte[] requestBarr = serialize(new LockInfo(key, resourceId, METHOD_CHECK_WRITE));
270                return performRequest(requestBarr);
271            }
272            catch(Throwable t)
273            {
274                throw new LockRuntimeException("Cannot check write lock for '"
275                        + resourceId + "' using key '" + key + "'", t);
276            }
277        }
278    
279        public boolean hasUpgrade(Object key, Object resourceId)
280        {
281            try
282            {
283                byte[] requestBarr = serialize(new LockInfo(key, resourceId, METHOD_CHECK_UPGRADE));
284                return performRequest(requestBarr);
285            }
286            catch(Throwable t)
287            {
288                throw new LockRuntimeException("Cannot check write lock for '"
289                        + resourceId + "' using key '" + key + "'", t);
290            }
291        }
292    
293        private HttpURLConnection getHttpUrlConnection()
294                throws MalformedURLException, IOException, ProtocolException
295        {
296            URL lockserver = getLockserverUrl();
297            HttpURLConnection conn = (HttpURLConnection) lockserver.openConnection();
298    
299            conn.setDoInput(true);
300            conn.setDoOutput(true);
301            conn.setRequestMethod("POST");
302            conn.setAllowUserInteraction(false);
303            conn.setUseCaches(false);
304            return conn;
305        }
306    
307        private URL getLockserverUrl()
308        {
309            return lockservlet;
310        }
311    
312        public byte[] serialize(Object obj) throws IOException
313        {
314            ByteArrayOutputStream bao = new ByteArrayOutputStream();
315            ObjectOutputStream oos = new ObjectOutputStream(bao);
316            oos.writeObject(obj);
317            oos.close();
318            bao.close();
319            byte[] result = bao.toByteArray();
320            return result;
321        }
322    
323        private boolean performRequest(byte[] requestBarr) throws IOException, ClassNotFoundException
324        {
325            Object result = performRequestObject(requestBarr);
326            if(result instanceof Boolean)
327            {
328                return ((Boolean) result).booleanValue();
329            }
330            else
331            {
332                throw new LockRuntimeException("Remote lock server error, expect return value of type 'Boolean'");
333            }
334        }
335    
336        private String performRequestString(byte[] requestBarr) throws IOException, ClassNotFoundException
337        {
338            Object result = performRequestObject(requestBarr);
339            if(result instanceof String)
340            {
341                return (String) result;
342            }
343            else
344            {
345                throw new LockRuntimeException("Remote lock server error, expect return value of type 'String'");
346            }
347        }
348    
349        private long performRequestLong(byte[] requestBarr) throws IOException, ClassNotFoundException
350        {
351            Object result = performRequestObject(requestBarr);
352            if(result instanceof Long)
353            {
354                return ((Long) result).longValue();
355            }
356            else
357            {
358                throw new LockRuntimeException("Remote lock server error, expect return value of type 'String'");
359            }
360        }
361    
362        private Object performRequestObject(byte[] requestBarr) throws IOException, ClassNotFoundException
363        {
364            HttpURLConnection conn = getHttpUrlConnection();
365    
366            //post request
367            BufferedOutputStream out = new BufferedOutputStream(conn.getOutputStream());
368            out.write(requestBarr, 0, requestBarr.length);
369            out.flush();
370    
371            // read result from
372            InputStream in = conn.getInputStream();
373            ObjectInputStream ois = new ObjectInputStream(in);
374            Object result = ois.readObject();
375    
376            // cleanup
377            ois.close();
378            out.close();
379            conn.disconnect();
380    
381            if(result instanceof Throwable)
382            {
383                throw new LockRuntimeException("Remote lock server error", (Throwable) result);
384            }
385            else
386            {
387                return result;
388            }
389        }
390    
391        public static final class LockInfo implements Serializable
392        {
393            public Object key;
394            public Object resourceId;
395            public int isolationLevel;
396            public byte methodName;
397            public long lockTimeout;
398            public long blockTimeout;
399    
400            public LockInfo(byte methodName)
401            {
402                this.methodName = methodName;
403            }
404    
405            public LockInfo(Object key, Object resourceId, byte methodName)
406            {
407                this.key = key;
408                this.resourceId = resourceId;
409                this.methodName = methodName;
410            }
411    
412            public LockInfo(Object key, Object resourceId, int isolationLevel, byte methodName)
413            {
414                this.key = key;
415                this.resourceId = resourceId;
416                this.isolationLevel = isolationLevel;
417                this.methodName = methodName;
418            }
419    
420    //        public LockInfo(long timeout, byte methodName)
421    //        {
422    //            if(methodName == METHOD_LOCK_TIMEOUT_SET)
423    //            {
424    //                this.lockTimeout = timeout;
425    //            }
426    //            else
427    //            {
428    //                this.blockTimeout = timeout;
429    //            }
430    //            this.methodName = methodName;
431    //        }
432        }
433    }