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     */
020    
021    
022    package org.apache.directory.shared.ldap.sp;
023    
024    import java.io.File;
025    import java.io.IOException;
026    import java.io.InputStream;
027    import java.io.Serializable;
028    import java.net.URL;
029    
030    import javax.naming.NamingException;
031    import javax.naming.directory.Attributes;
032    import javax.naming.directory.BasicAttributes;
033    import javax.naming.ldap.LdapContext;
034    
035    import org.apache.commons.lang.SerializationUtils;
036    import org.apache.directory.shared.ldap.constants.SchemaConstants;
037    import org.apache.directory.shared.ldap.message.extended.StoredProcedureRequest;
038    import org.apache.directory.shared.ldap.message.extended.StoredProcedureResponse;
039    
040    /**
041     * A utility class for working with Java Stored Procedures at the base level.
042     *
043     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
044     * @version $Rev:$
045     */
046    public class JavaStoredProcUtils
047    {
048    
049        
050        /**
051         * Returns the stream data of a Java class.
052         * 
053         * @param clazz
054         *           The class whose stream data will be retrieved.
055         * @return
056         *           Stream data of the class file as a byte array.
057         * @throws NamingException
058         *           If an IO error occurs during reading the class file.
059         */
060        public static byte[] getClassFileAsStream( Class<?> clazz ) throws NamingException
061        {
062            String fullClassName = clazz.getName();
063            int lastDot = fullClassName.lastIndexOf( '.' );
064            String classFileName = fullClassName.substring( lastDot + 1 ) + ".class";
065            URL url = clazz.getResource( classFileName );
066            InputStream in = clazz.getResourceAsStream( classFileName );
067            File file = new File( url.getFile() );
068            int size = ( int ) file.length();
069            byte[] buf = new byte[size];
070            
071            try
072            {
073                in.read( buf );
074                in.close();
075            }
076            catch ( IOException e )
077            {
078                NamingException ne = new NamingException();
079                ne.setRootCause( e );
080                throw ne;
081            }
082            
083            return buf;
084        }
085        
086        /**
087         * Loads a Java class's stream data as a subcontext of an LdapContext given.
088         * 
089         * @param ctx
090         *           The parent context of the Java class entry to be loaded.
091         * @param clazz
092         *           Class to be loaded. 
093         * @throws NamingException
094         *           If an error occurs during creating the subcontext.
095         */
096        public static void loadStoredProcedureClass( LdapContext ctx, Class<?> clazz ) throws NamingException
097        {
098            byte[] buf = getClassFileAsStream( clazz );
099            String fullClassName = clazz.getName();
100            
101            Attributes attributes = new BasicAttributes( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, true );
102            attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "storedProcUnit" );
103            attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "javaStoredProcUnit" );
104            attributes.put( "storedProcLangId", "Java" );
105            attributes.put( "storedProcUnitName", fullClassName );
106            attributes.put( "javaByteCode", buf );
107            
108            ctx.createSubcontext( "storedProcUnitName=" + fullClassName , attributes );
109        }
110        
111        public static Object callStoredProcedure( LdapContext ctx, String procedureName, Object[] arguments ) throws NamingException
112        {
113            String language = "Java";
114            
115            Object responseObject;
116            try
117            {
118                /**
119                 * Create a new stored procedure execution request.
120                 */
121                StoredProcedureRequest req = new StoredProcedureRequest( 0, procedureName, language );
122                
123                /**
124                 * For each argument UTF-8-encode the type name
125                 * and Java-serialize the value
126                 * and add them to the request as a parameter object.
127                 */
128                for ( int i = 0; i < arguments.length; i++ )
129                {
130                    byte[] type;
131                    byte[] value;
132                    type = arguments[i].getClass().getName().getBytes( "UTF-8" );
133                    value = SerializationUtils.serialize( ( Serializable ) arguments[i] );
134                    req.addParameter( type, value );
135                }
136                
137                /**
138                 * Call the stored procedure via the extended operation
139                 * and get back its return value.
140                 */
141                StoredProcedureResponse resp = ( StoredProcedureResponse ) ctx.extendedOperation( req );
142                
143                /**
144                 * Restore a Java object from the return value.
145                 */
146                byte[] responseStream = resp.getEncodedValue();
147                responseObject = SerializationUtils.deserialize( responseStream );
148            }
149            catch ( Exception e )
150            {
151                NamingException ne = new NamingException();
152                ne.setRootCause( e );
153                throw ne;
154            }
155            
156            return responseObject;
157        }
158        
159    }