2017-10-20 27 views
0

J'ai une fonction azur déclenchée http (écrite en Java) dans laquelle je veux accéder au stockage Blob. Le code compile sous maven, mais lorsque je l'exécute localement et envoie un message à partir de CURL, l'exécution se bloque en raison d'une exception ClassNotFound provoquée par com.microsoft.azure.storage.CloudStorageAccount manquant. azure-storage (version 6.0.0) est répertorié en tant que dépendance dans le fichier POM. Où les fichiers .jar associés doivent-ils être visibles par la fonction? Toute idée concernant les fonctions Java azur serait appréciée. Pouvez-vous partager des détails sur la méthode et les types que vous utilisez?Comment utiliser le stockage blob à partir d'une fonction azure en Java

Répondre

0

Selon vos besoins, je vous suggère de suivre cette official tutorial pour créer, exécuter et déployer votre java azure function.

Fonction Classe:

package com.fabrikam.functions; 

import com.microsoft.azure.serverless.functions.annotation.*; 
import com.microsoft.azure.serverless.functions.ExecutionContext; 

import com.microsoft.azure.storage.*; 
import com.microsoft.azure.storage.blob.*; 

/** 
* Hello function with HTTP Trigger. 
*/ 
public class Function { 

    // Configure the connection-string with your values 
    public static final String storageConnectionString = 
      "DefaultEndpointsProtocol=http;" + 
        "AccountName=jaygong;" + 
        "AccountKey=bmrgAHzDrL8FsbQOJP0xnYYNXrNOmfSZyBdzedlFG/famIHK9gJB/UUQzWbQ6DB/dq7zPZ/YMrk3bMcO+1hjrA=="; 

    @FunctionName("hello") 
    public String hello(@HttpTrigger(name = "req", methods = {"get", "post"}, authLevel = AuthorizationLevel.ANONYMOUS) String req, 
         ExecutionContext context) { 

     try { 
      // Retrieve storage account from connection-string. 
      CloudStorageAccount storageAccount = CloudStorageAccount.parse(storageConnectionString); 

      // Create the blob client. 
      CloudBlobClient blobClient = storageAccount.createCloudBlobClient(); 

      // Get a reference to a container. 
      // The container name must be lower case 
      CloudBlobContainer container = blobClient.getContainerReference(req); 

      // Create the container if it does not exist. 
      container.createIfNotExists(); 

      return String.format("Hello, I get container name : %s!", container.getName()); 

     } catch (Exception e) { 
      // Output the stack trace. 
      e.printStackTrace(); 
      return "Access Error!"; 
     } 
    } 
} 

pom.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>com.fabrikam.functions</groupId> 
    <artifactId>fabrikam-functions</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    <packaging>jar</packaging> 

    <name>Azure Java Functions</name> 

    <properties> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
     <maven.compiler.source>1.8</maven.compiler.source> 
     <maven.compiler.target>1.8</maven.compiler.target> 
     <functionAppName>fabrikam-functions-20171017112209094</functionAppName> 
    </properties> 

    <dependencies> 
     <dependency> 
      <groupId>com.microsoft.azure</groupId> 
      <artifactId>azure-functions-java-core</artifactId> 
      <version>1.0.0-beta-1</version> 
     </dependency> 

     <!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-storage --> 
     <dependency> 
      <groupId>com.microsoft.azure</groupId> 
      <artifactId>azure-storage</artifactId> 
      <version>6.0.0</version> 
     </dependency> 

     <!-- Test --> 
     <dependency> 
      <groupId>junit</groupId> 
      <artifactId>junit</artifactId> 
      <version>4.12</version> 
      <scope>test</scope> 
     </dependency> 
    </dependencies> 

    <build> 
     <pluginManagement> 
      <plugins> 
       <plugin> 
        <artifactId>maven-resources-plugin</artifactId> 
        <version>3.0.2</version> 
       </plugin> 
       <plugin> 
        <groupId>com.microsoft.azure</groupId> 
        <artifactId>azure-functions-maven-plugin</artifactId> 
        <version>0.1.4</version> 
       </plugin> 
      </plugins> 
     </pluginManagement> 

     <plugins> 
      <plugin> 
       <groupId>com.microsoft.azure</groupId> 
       <artifactId>azure-functions-maven-plugin</artifactId> 
       <configuration> 
        <resourceGroup>java-functions-group</resourceGroup> 
        <appName>${functionAppName}</appName> 
        <region>westus2</region> 
        <appSettings> 
         <property> 
          <name>FUNCTIONS_EXTENSION_VERSION</name> 
          <value>beta</value> 
         </property> 
        </appSettings> 
       </configuration> 
       <executions> 
        <execution> 
         <id>package-functions</id> 
         <goals> 
          <goal>package</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 
      <plugin> 
       <artifactId>maven-resources-plugin</artifactId> 
       <executions> 
        <execution> 
         <id>copy-resources</id> 
         <phase>package</phase> 
         <goals> 
          <goal>copy-resources</goal> 
         </goals> 
         <configuration> 
          <overwrite>true</overwrite> 
          <outputDirectory>${project.build.directory}/azure-functions/${functionAppName} 
          </outputDirectory> 
          <resources> 
           <resource> 
            <directory>${project.basedir}</directory> 
            <includes> 
             <include>host.json</include> 
             <include>local.settings.json</include> 
            </includes> 
           </resource> 
          </resources> 
         </configuration> 
        </execution> 
       </executions> 
      </plugin> 
     </plugins> 

    </build> 

</project> 

Utilisez ensuite la commande mvn clean package pour emballer votre projet Maven dans un paquet de pot.

enter image description here

commande use mvn azure-functions:run pour exécuter votre fonction d'azur localement.

enter image description here


Mise à jour Réponse:

j'ai couru ma fonction d'azur et de reproduire la même exception que vous avez dit.

java.lang.NoClassDefFoundError: com/microsoft/azure/storage/CloudStorageAccount

Exception: 
Stack: java.lang.reflect.InvocationTargetException 
[10/25/2017 2:48:44 AM]   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
[10/25/2017 2:48:44 AM]   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
[10/25/2017 2:48:44 AM]   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[10/25/2017 2:48:44 AM]   at java.lang.reflect.Method.invoke(Method.java:498) 
[10/25/2017 2:48:44 AM]   at com.microsoft.azure.webjobs.script.broker.JavaMethodInvokeInfo.invoke(JavaMethodInvokeInfo.java:19) 
[10/25/2017 2:48:44 AM]   at com.microsoft.azure.webjobs.script.broker.JavaMethodExecutor.execute(JavaMethodExecutor.java:34) 
[10/25/2017 2:48:44 AM]   at com.microsoft.azure.webjobs.script.broker.JavaFunctionBroker.invokeMethod(JavaFunctionBroker.java:40) 
[10/25/2017 2:48:44 AM]   at com.microsoft.azure.webjobs.script.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:33) 
[10/25/2017 2:48:44 AM]   at com.microsoft.azure.webjobs.script.handler.InvocationRequestHandler.execute(InvocationRequestHandler.java:10) 
[10/25/2017 2:48:44 AM]   at com.microsoft.azure.webjobs.script.handler.MessageHandler.handle(MessageHandler.java:41) 
[10/25/2017 2:48:44 AM]   at com.microsoft.azure.webjobs.script.JavaWorkerClient$StreamingMessagePeer.lambda$onNext$0(JavaWorkerClient.java:84) 
[10/25/2017 2:48:44 AM]   at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1386) 
[10/25/2017 2:48:44 AM]   at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289) 
[10/25/2017 2:48:44 AM]   at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056) 
[10/25/2017 2:48:44 AM]   at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692) 
[10/25/2017 2:48:44 AM]   at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157) 
[10/25/2017 2:48:44 AM] Caused by: java.lang.NoClassDefFoundError: com/microsoft/azure/storage/CloudStorageAccount 
[10/25/2017 2:48:44 AM]   at com.fabrikam.functions.Function.hello(Function.java:26) 
[10/25/2017 2:48:44 AM]   ... 16 more 
[10/25/2017 2:48:44 AM] Caused by: java.lang.ClassNotFoundException: com.microsoft.azure.storage.CloudStorageAccount 
[10/25/2017 2:48:44 AM]   at java.net.URLClassLoader.findClass(URLClassLoader.java:381) 
[10/25/2017 2:48:44 AM]   at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
[10/25/2017 2:48:44 AM]   at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
[10/25/2017 2:48:44 AM]   ... 17 more 
[10/25/2017 2:48:44 AM] . 
[10/25/2017 2:48:44 AM] Function had errors. See Azure WebJobs SDK dashboard for details. Instance ID is '3450abda-99a0-4d75-add2-a7bc48a0cb51' 
[10/25/2017 2:48:44 AM] System.Private.CoreLib: Exception while executing function: Functions.hello. System.Private.CoreLib: Result: 

Après quelques recherches, j'ai découvert que c'était parce que le pot emballé sans dependent jar packages.

Donc, j'ai ajouté l'extrait de configuration comme ci-dessous dans mon pom.xml

<plugin> 
       <artifactId>maven-assembly-plugin</artifactId> 
       <configuration> 
        <descriptorRefs> 
         <descriptorRef>jar-with-dependencies</descriptorRef> 
        </descriptorRefs> 
        <archive> 
         <manifest> 
          <mainClass>Your main class path</mainClass> 
         </manifest> 
        </archive> 
       </configuration> 
       <executions> 
        <execution> 
         <id>make-assembly</id> 
         <phase>package</phase> 
         <goals> 
          <goal>single</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 

Alors s'il vous plaît utilisez la commande mvn-clean-package et vous verrez deux fichiers jar générés.

enter image description here

L'une est qu'il ne contient pas dependent jar packages, et le second contient dependent jar packages.

Déplacer le pot fabrikam-functions-1.0-SNAPSHOT-jar-with-dependencies dans le chemin: ${project.basedir}/target/azure-functions/${function-app-name}/

Pour moi, il ressemble à E:\TestAzureFunction\fabrikam-functions\target\azure-functions\fabrikam-functions-20171017112209094.

N'oubliez pas de renommer le pot en fabrikam-functions-1.0-SNAPSHOT.

Enfin, j'exécute la fonction azure avec succès et j'obtiens le résultat de sortie via l'url: http://localhost:7071/api/mongo.

enter image description here

De plus, vous pouvez consulter cette github doc pour plus de détails sur la configuration d'azur function maven plugin.

J'espère que ça vous aide.

+0

Je pense que vous constaterez que si vous exécutez réellement votre fonction (ce qui est montré ci-dessus indique simplement que la fonction est chargée), elle se bloquera avec la même erreur que j'ai rencontrée. Utilisez CURL pour lui envoyer un message HTTP. Si cela ne plante pas, alors dites-moi où se trouvent les bibliothèques de stockage MS sur votre machine locale. –

+0

Je suis curieux si vous étiez en mesure d'exécuter votre fonction en utilisant CURL. J'ai été capable de créer et d'exécuter localement une fonction C# qui ressemble beaucoup à votre code Java et fonctionne comme prévu en utilisant les bibliothèques Azure C# (bien que je devais ajouter manuellement un assembly à Visual Studio). Cela me fait penser que le problème que j'ai rencontré est en fait dû à une certaine idiosyncrasie de mon environnement Maven, et je ne connais pas bien Maven. Je voudrais utiliser une fonction Java parce que j'ai un autre code Java que je ne veux pas convertir en C#. Donc, si vous avez réussi, s'il vous plaît faites le moi savoir. –

+0

@JackCopper Désolé de vous répondre si tard, s'il vous plaît donnez-moi du temps pour mettre à jour ma réponse.Merci. –

0

Pour effectuer des liaisons de sortie, vous devez utiliser la classe OutputBinding<T> avec l'annotation correcte. Voici un exemple de celui que je viens de tester à faire semblable à ce que vous avez mentionné:

@FunctionName("hello") 
public String hello(
     @HttpTrigger(name = "req", methods = { 
       "post" }, authLevel = AuthorizationLevel.ANONYMOUS) String req, 
     ExecutionContext context, @BlobOutput(name = "blob", connection = "StorageAccount", path = "test/foo.txt")OutputBinding<String> blob) 
     { 
    blob.setValue("hello world"); 
    return String.format("Hello, %s!", req); 
} 

Notez l'attribut @BlobOutput et le type de OutputBinding (pourrait être String ou byte [], mais je crois que si cela byte [] vous devez également définir dataType à « binaire » sur le @BlobAttribute.

Permettez-moi de savoir si cela fonctionne

+0

Le cas d'utilisation est: un ID de conteneur et un nom de fichier spécifique se trouvent dans l'en-tête de la demande; le blob est un fichier arbitraire (texte) dans un emplacement fixe dans la hiérarchie d'un conteneur qui est passé; dans la fonction, j'ai besoin de lire quelques lignes puis ajouter des méta-données à la tâche. Votre réponse m'a amené à regarder d'autres annotations (par exemple, je pense avoir besoin d'utiliser le flux) mais je suis toujours aux prises avec les bases de l'obtention des paramètres de l'en-tête de demande au nom blob et aux paramètres de connexion requis. –