001    package org.apache.fulcrum.yaafi.service.reconfiguration;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.ByteArrayOutputStream;
023    import java.io.File;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.OutputStream;
027    import java.security.MessageDigest;
028    
029    import org.apache.avalon.framework.logger.Logger;
030    import org.apache.fulcrum.yaafi.framework.util.InputStreamLocator;
031    
032    /**
033     * Monitors a resource and checks if it has changed
034     *
035     * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl</a>
036     */
037    
038    public class ReconfigurationEntry
039    {
040        /** buffer size for copy() */
041        private static final int BUF_SIZE = 1024;
042    
043        /** the location to monitor for changes */
044        private String location;
045    
046        /** the list of services to be reconfigured */
047        private String[] serviceList;
048    
049        /** the last message digest of the location */
050        private byte[] digest;
051    
052        /** the locator to load the monitored resource */
053        private InputStreamLocator locator;
054    
055        /** keep a notice for the very first invocation */
056        private boolean isFirstInvocation;
057    
058        /** the logger to be used */
059        private Logger logger;
060    
061        /**
062         * Constructor
063         *
064         * @param logger the logger to use
065         * @param applicationDir the home directory of the application
066         * @param location the location to monitor for changes
067         * @param serviceList the list of services to be reconfigured
068         */
069        public ReconfigurationEntry( Logger logger, File applicationDir, String location, String[] serviceList )
070        {
071            this.isFirstInvocation = true;
072            this.location = location;
073            this.locator  = new InputStreamLocator( applicationDir );
074            this.logger = logger;
075            this.serviceList = serviceList;
076        }
077    
078        /**
079         * @return has the monitored location changed
080         */
081        public boolean hasChanged()
082        {
083            boolean result = false;
084            InputStream is = null;
085            byte[] currDigest = null;
086    
087            try
088            {
089                // get a grip on our resource
090    
091                is = this.locate();
092    
093                if( is == null )
094                {
095                    String msg = "Unable to find the following resource : " + this.getLocation();
096                    this.getLogger().warn(msg);
097                }
098                else
099                {
100                    // calculate a SHA-1 digest
101    
102                    currDigest = this.getDigest(is);
103                    is.close();
104                    is = null;
105    
106                    if( this.isFirstInvocation() == true )
107                    {
108                        isFirstInvocation = false;
109                        this.getLogger().debug( "Storing SHA-1 digest of " + this.getLocation() );
110                        this.setDigest( currDigest );
111                    }
112                    else
113                    {
114                        if( equals( this.digest, currDigest ) == false )
115                        {
116                            this.getLogger().debug( "The following resource has changed : " + this.getLocation() );
117                            this.setDigest( currDigest );
118                            result = true;
119                        }
120                    }
121                }
122    
123                return result;
124            }
125            catch(Exception e)
126            {
127                String msg = "The ShutdownService encountered an internal error";
128                this.getLogger().error(msg,e);
129                return false;
130            }
131            finally
132            {
133                if( is != null )
134                {
135                    try
136                    {
137                        is.close();
138                    }
139                    catch (Exception e)
140                    {
141                        String msg = "Can't close the InputStream during error recovery";
142                        this.getLogger().error(msg,e);
143                    }
144                }
145            }
146    
147        }
148    
149        /**
150         * @return Returns the serviceList.
151         */
152        public String [] getServiceList()
153        {
154            return serviceList;
155        }
156    
157        /**
158         * @return Returns the isFirstInvocation.
159         */
160        private boolean isFirstInvocation()
161        {
162            return isFirstInvocation;
163        }
164    
165        /**
166         * @return Returns the location.
167         */
168        private String getLocation()
169        {
170            return location;
171        }
172    
173        /**
174         * @return Returns the locator.
175         */
176        private InputStreamLocator getLocator()
177        {
178            return locator;
179        }
180    
181        /**
182         * Creates an InputStream.
183         * @return the input stream
184         * @throws IOException the creation failed
185         */
186        public InputStream locate() throws IOException
187        {
188            return this.getLocator().locate(this.getLocation());
189        }
190    
191        /**
192         * Creates a message digest.
193         *
194         * @param is the input stream as input for the message digest
195         * @return the message digest
196         * @throws Exception the creation failed
197         */
198        private byte[] getDigest( InputStream is )
199            throws Exception
200        {
201            byte[] result = null;
202            byte[] content = null;
203    
204            ByteArrayOutputStream baos = new ByteArrayOutputStream();
205            copy( is, baos );
206            content = baos.toByteArray();
207            baos.close();
208    
209            MessageDigest sha1 = MessageDigest.getInstance( "SHA1" );
210            sha1.update( content );
211            result = sha1.digest();
212    
213            return result;
214        }
215    
216        /**
217         * @param digest The digest to set.
218         */
219        private void setDigest(byte [] digest)
220        {
221            this.digest = digest;
222        }
223    
224        /**
225         * Compares two byte[] for equality
226         *
227         * @param lhs the left-hand side
228         * @param rhs the right-hand side
229         * @return true if the byte[] are equal
230         */
231        private static boolean equals(byte[] lhs, byte[] rhs)
232        {
233            if( lhs == rhs )
234            {
235                return true;
236            }
237            else if( lhs.length != rhs.length )
238            {
239                return false;
240            }
241            else
242            {
243                for( int i=0; i<lhs.length; i++ )
244                {
245                    if( lhs[i] != rhs[i] )
246                    {
247                        return false;
248                    }
249                }
250            }
251    
252            return true;
253        }
254    
255        /**
256         * Pumps the input stream to the output stream.
257         *
258         * @param is the source input stream
259         * @param os the target output stream
260         * @throws IOException the copying failed
261         */
262        private static void copy( InputStream is, OutputStream os )
263            throws IOException
264        {
265            byte[] buf = new byte[BUF_SIZE];
266            int n = 0;
267            int total = 0;
268    
269            while ((n = is.read(buf)) > 0)
270            {
271                os.write(buf, 0, n);
272                total += n;
273            }
274    
275            is.close();
276    
277            os.flush();
278            os.close();
279        }
280    
281        /**
282         * @return Returns the logger.
283         */
284        private Logger getLogger()
285        {
286            return logger;
287        }
288    }