001/*
002 *   Licensed to the Apache Software Foundation (ASF) under one
003 *   or more contributor license agreements.  See the NOTICE file
004 *   distributed with this work for additional information
005 *   regarding copyright ownership.  The ASF licenses this file
006 *   to you under the Apache License, Version 2.0 (the
007 *   "License"); you may not use this file except in compliance
008 *   with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *   Unless required by applicable law or agreed to in writing,
013 *   software distributed under the License is distributed on an
014 *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *   KIND, either express or implied.  See the License for the
016 *   specific language governing permissions and limitations
017 *   under the License.
018 *
019 */
020package org.apache.directory.server.ldap.handlers;
021
022
023import java.util.concurrent.TimeUnit;
024
025import org.apache.directory.api.ldap.model.cursor.ClosureMonitor;
026import org.apache.directory.api.ldap.model.cursor.CursorClosedException;
027import org.apache.directory.api.ldap.model.exception.LdapTimeLimitExceededException;
028import org.apache.directory.server.i18n.I18n;
029
030
031/**
032 * A ClosureMonitor implementation which takes into account a time limit.
033 *
034 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
035 */
036public class SearchTimeLimitingMonitor implements ClosureMonitor
037{
038    private final long startTime = System.currentTimeMillis();
039    private final long millisToLive;
040
041    private boolean closed;
042    private Exception cause;
043
044
045    /**
046     * Creates a new instance of SearchTimeLimitingMonitor.
047     *
048     * @param timeToLive the time before changing state to closed.
049     * @param unit the time units for the timeToLive parameter
050     * @see TimeUnit
051     */
052    public SearchTimeLimitingMonitor( long timeToLive, TimeUnit unit )
053    {
054        switch ( unit )
055        {
056            case MICROSECONDS:
057                this.millisToLive = timeToLive / 1000;
058                break;
059            case MILLISECONDS:
060                this.millisToLive = timeToLive;
061                break;
062            case SECONDS:
063                this.millisToLive = timeToLive * 1000;
064                break;
065            default:
066                throw new IllegalStateException( I18n.err( I18n.ERR_687, unit ) );
067        }
068    }
069
070
071    /**
072     * {@inheritDoc}
073     */
074    public void checkNotClosed() throws CursorClosedException
075    {
076        if ( ( System.currentTimeMillis() > startTime + millisToLive ) && !closed )
077        {
078            // state check needed to "try" not to overwrite exception (lack of 
079            // synchronization may still allow overwriting but who cares that 
080            // much
081            // not going to sync because who cares if it takes a little 
082            // longer to stop but we need to set cause before toggling 
083            // closed state or else check for closure can throw null cause 
084            cause = new LdapTimeLimitExceededException();
085            closed = true;
086        }
087
088        if ( closed )
089        {
090            throw new CursorClosedException( cause.getMessage(), cause );
091        }
092    }
093
094
095    /*
096     * (non-Javadoc)
097     * @see org.apache.directory.server.core.cursor.ClosureMonitor#close()
098     */
099    public void close()
100    {
101        if ( !closed )
102        {
103            // not going to sync because who cares if it takes a little longer 
104            // to stop but we need to set cause before toggling closed state 
105            // or else check for closure can throw null cause 
106            cause = new CursorClosedException();
107            closed = true;
108        }
109    }
110
111
112    /*
113     * (non-Javadoc)
114     * @see org.apache.directory.server.core.cursor.ClosureMonitor#close(java.lang.String)
115     */
116    public void close( String cause )
117    {
118        if ( !closed )
119        {
120            // not going to sync because who cares if it takes a little longer 
121            // to stop but we need to set cause before toggling closed state 
122            // or else check for closure can throw null cause 
123            this.cause = new CursorClosedException( cause );
124            closed = true;
125        }
126    }
127
128
129    /*
130     * (non-Javadoc)
131     * @see org.apache.directory.server.core.cursor.ClosureMonitor#close(java.lang.Exception)
132     */
133    public void close( Exception cause )
134    {
135        if ( !closed )
136        {
137            // not going to sync because who cares if it takes a little longer 
138            // to stop but we need to set cause before toggling closed state 
139            // or else check for closure can throw null cause 
140            this.cause = cause;
141            closed = true;
142        }
143    }
144
145
146    /*
147     * (non-Javadoc)
148     * @see org.apache.directory.server.core.cursor.ClosureMonitor#getCause()
149     */
150    public Exception getCause()
151    {
152        return cause;
153    }
154
155
156    /*
157     * (non-Javadoc)
158     * @see org.apache.directory.server.core.cursor.ClosureMonitor#isClosed()
159     */
160    public boolean isClosed()
161    {
162        if ( System.currentTimeMillis() > startTime + millisToLive )
163        {
164            // set cause first always
165            cause = new LdapTimeLimitExceededException();
166            closed = true;
167        }
168
169        return closed;
170    }
171}