View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   * 
19   */
20  
21  package org.apache.directory.api.ldap.sp;
22  
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.io.Serializable;
28  import java.net.URISyntaxException;
29  import java.net.URL;
30  
31  import javax.naming.NamingException;
32  import javax.naming.directory.Attributes;
33  import javax.naming.directory.BasicAttributes;
34  import javax.naming.ldap.ExtendedRequest;
35  import javax.naming.ldap.ExtendedResponse;
36  import javax.naming.ldap.LdapContext;
37  
38  import org.apache.commons.lang.SerializationUtils;
39  import org.apache.directory.api.ldap.codec.api.LdapApiServiceFactory;
40  import org.apache.directory.api.ldap.extras.extended.storedProcedure.StoredProcedureRequestImpl;
41  import org.apache.directory.api.ldap.model.constants.SchemaConstants;
42  import org.apache.directory.api.util.IOUtils;
43  
44  
45  /**
46   * A utility class for working with Java Stored Procedures at the base level.
47   *
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   */
50  public final class JavaStoredProcUtils
51  {
52  
53      /**
54       * Private constructor.
55       */
56      private JavaStoredProcUtils()
57      {
58      }
59  
60  
61      /**
62       * Returns the stream data of a Java class.
63       * 
64       * @param clazz
65       *           The class whose stream data will be retrieved.
66       * @return
67       *           Stream data of the class file as a byte array.
68       * @throws NamingException
69       *           If an IO error occurs during reading the class file.
70       */
71      public static byte[] getClassFileAsStream( Class<?> clazz ) throws NamingException
72      {
73          String fullClassName = clazz.getName();
74          int lastDot = fullClassName.lastIndexOf( '.' );
75          String classFileName = fullClassName.substring( lastDot + 1 ) + ".class";
76          URL url = clazz.getResource( classFileName );
77          InputStream in;
78          
79          try
80          {
81              in = url.openStream();
82          }
83          catch ( IOException ioe )
84          {
85              NamingException ne = new NamingException();
86              ne.setRootCause( ioe );
87              throw ne;
88          }
89          
90          File file;
91          
92          try
93          {
94              file = new File( url.toURI() );
95          }
96          catch ( URISyntaxException urie )
97          {
98              NamingException ne = new NamingException();
99              ne.setRootCause( urie );
100             throw ne;
101         }
102         
103         int size = ( int ) file.length();
104         byte[] buf = new byte[size];
105 
106         try
107         {
108             in.read( buf );
109         }
110         catch ( IOException e )
111         {
112             NamingException ne = new NamingException();
113             ne.setRootCause( e );
114             throw ne;
115         }
116         finally
117         {
118             IOUtils.closeQuietly( in );
119         }
120 
121         return buf;
122     }
123 
124 
125     /**
126      * Loads a Java class's stream data as a subcontext of an LdapContext given.
127      * 
128      * @param ctx
129      *           The parent context of the Java class entry to be loaded.
130      * @param clazz
131      *           Class to be loaded.
132      * @throws NamingException
133      *           If an error occurs during creating the subcontext.
134      */
135     public static void loadStoredProcedureClass( LdapContext ctx, Class<?> clazz ) throws NamingException
136     {
137         byte[] buf = getClassFileAsStream( clazz );
138         String fullClassName = clazz.getName();
139 
140         Attributes attributes = new BasicAttributes( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, true );
141         attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "storedProcUnit" );
142         attributes.get( SchemaConstants.OBJECT_CLASS_AT ).add( "javaStoredProcUnit" );
143         attributes.put( "storedProcLangId", "Java" );
144         attributes.put( "storedProcUnitName", fullClassName );
145         attributes.put( "javaByteCode", buf );
146 
147         ctx.createSubcontext( "storedProcUnitName=" + fullClassName, attributes );
148     }
149 
150 
151     /**
152      * Invoke a Stored Procedure
153      * 
154      * @param ctx The execution context
155      * @param procedureName The procedure to execute
156      * @param arguments The procedure's arguments
157      * @return The execution resut
158      * @throws NamingException If we have had an error whil executing the stored procedure
159      */
160     public static Object callStoredProcedure( LdapContext ctx, String procedureName, Object[] arguments )
161         throws NamingException
162     {
163         String language = "Java";
164 
165         Object responseObject;
166         
167         try
168         {
169             /**
170              * Create a new stored procedure execution request.
171              */
172             StoredProcedureRequestImpl req = new StoredProcedureRequestImpl( 0, procedureName, language );
173 
174             /**
175              * For each argument UTF-8-encode the type name
176              * and Java-serialize the value
177              * and add them to the request as a parameter object.
178              */
179             for ( int i = 0; i < arguments.length; i++ )
180             {
181                 byte[] type;
182                 byte[] value;
183                 type = arguments[i].getClass().getName().getBytes( "UTF-8" );
184                 value = SerializationUtils.serialize( ( Serializable ) arguments[i] );
185                 req.addParameter( type, value );
186             }
187 
188             /**
189              * Call the stored procedure via the extended operation
190              * and get back its return value.
191              */
192             ExtendedRequest jndiReq = LdapApiServiceFactory.getSingleton().toJndi( req );
193             ExtendedResponse resp = ctx.extendedOperation( jndiReq );
194 
195             /**
196              * Restore a Java object from the return value.
197              */
198             byte[] responseStream = resp.getEncodedValue();
199             responseObject = SerializationUtils.deserialize( responseStream );
200         }
201         catch ( Exception e )
202         {
203             NamingException ne = new NamingException();
204             ne.setRootCause( e );
205             throw ne;
206         }
207 
208         return responseObject;
209     }
210 }