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