├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main └── java ├── META-INF └── MANIFEST.MF └── com └── microsoft └── graphsample ├── PublicClient.java ├── connect ├── AuthenticationManager.java ├── Constants.java ├── Debug.java ├── DebugLogger.java ├── IConnectCallback.java └── MicrosoftAzureAD20Api.java └── msgraph ├── GraphSendMail.java ├── GraphServiceClientManager.java ├── GraphServiceController.java ├── SendMailException.java └── VisibleForTesting.java /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | 11 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 12 | !/.mvn/wrapper/maven-wrapper.jar 13 | ## Eclipse 14 | .settings/ 15 | .loadpath 16 | .metadata 17 | proguard/ 18 | 19 | *.keystore 20 | *.orig 21 | *.log 22 | *.tmp 23 | *.bak 24 | 25 | ## Android Studio 26 | *.apk 27 | *.ap_ 28 | *.dex 29 | *.class 30 | *.iws 31 | *.ipr 32 | *.iml 33 | *~ 34 | *.swp 35 | .idea/ 36 | local.properties 37 | out/ 38 | build/ 39 | production/ 40 | 41 | ## Gradle 42 | .gradle/ 43 | gradle/ 44 | 45 | # generated files 46 | **/build/ 47 | bin/ 48 | gen/ 49 | target/ 50 | out/ 51 | gen-external-apklibs/ 52 | 53 | # Intellj 54 | .settings/ 55 | .libraries/ 56 | 57 | # VSCode 58 | .vscode/ 59 | 60 | # Eclipse files 61 | .classpath 62 | .project -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to this documentation 2 | 3 | Thank you for your interest in our documentation! 4 | 5 | * [Ways to contribute](#ways-to-contribute) 6 | * [Contribute using GitHub](#contribute-using-github) 7 | * [Contribute using Git](#contribute-using-git) 8 | * [How to use Markdown to format your topic](#how-to-use-markdown-to-format-your-topic) 9 | * [FAQ](#faq) 10 | * [More resources](#more-resources) 11 | 12 | ## Ways to contribute 13 | 14 | Here are some ways you can contribute to this documentation: 15 | 16 | * To make small changes to an article, [Contribute using GitHub](#contribute-using-github). 17 | * To make large changes, or changes that involve code, [Contribute using Git](#contribute-using-git). 18 | * Report documentation bugs via GitHub Issues 19 | * Request new documentation at the [Office Developer Platform UserVoice](http://officespdev.uservoice.com) site. 20 | 21 | ## Contribute using GitHub 22 | 23 | Use GitHub to contribute to this documentation without having to clone the repo to your desktop. This is the easiest way to create a pull request in this repository. Use this method to make a minor change that doesn't involve code changes. 24 | 25 | **Note** Using this method allows you to contribute to one article at a time. 26 | 27 | ### To Contribute using GitHub 28 | 29 | 1. Find the article you want to contribute to on GitHub. 30 | 31 | If the article is in MSDN, choose the **suggest and submit changes** link in the **Contribute to this content** section and you'll be taken to the same article on GitHub. 32 | 2. Once you are on the article in GitHub, sign in to GitHub (get a free account [Join GitHub](https://github.com/join). 33 | 3. Choose the **pencil icon** (edit the file in your fork of this project) and make your changes in the **<>Edit file** window. 34 | 4. Scroll to the bottom and enter a description. 35 | 5. Choose **Propose file change**>**Create pull request**. 36 | 37 | You now have successfully submitted a pull request. Pull requests are typically reviewed within 10 business days. 38 | 39 | 40 | ## Contribute using Git 41 | 42 | Use Git to contribute substantive changes, such as: 43 | 44 | * Contributing code. 45 | * Contributing changes that affect meaning. 46 | * Contributing large changes to text. 47 | * Adding new topics. 48 | 49 | ### To Contribute using Git 50 | 51 | 1. If you don't have a GitHub account, set one up at [GitHub](https://github.com/join). 52 | 2. After you have an account, install Git on your computer. Follow the steps in [Setting up Git Tutorial](https://help.github.com/articles/set-up-git/). 53 | 3. To submit a pull request using Git, follow the steps in [Use GitHub, Git, and this repository](#use-github-git-and-this-repository). 54 | 4. You will be asked to sign the Contributor's License Agreement if you are: 55 | 56 | * A member of the Microsoft Open Technologies group. 57 | * A contributors who doesn't work for Microsoft. 58 | 59 | As a community member, you must sign the Contribution License Agreement (CLA) before you can contribute large submissions to a project. You only need to complete and submit the documentation once. Carefully review the document. You may be required to have your employer sign the document. 60 | 61 | Signing the CLA does not grant you rights to commit to the main repository, but it does mean that the Office Developer and Office Developer Content Publishing teams will be able to review and approve your contributions. You will be credited for your submissions. 62 | 63 | Pull requests are typically reviewed within 10 business days. 64 | 65 | ## Use GitHub, Git, and this repository 66 | 67 | **Note:** Most of the information in this section can be found in [GitHub Help] articles. If you're familiar with Git and GitHub, skip to the **Contribute and edit content** section for the specifics of the code/content flow of this repository. 68 | 69 | ### To set up your fork of the repository 70 | 71 | 1. Set up a GitHub account so you can contribute to this project. If you haven't done this, go to [GitHub](https://github.com/join) and do it now. 72 | 2. Install Git on your computer. Follow the steps in the [Setting up Git Tutorial] [Set Up Git]. 73 | 3. Create your own fork of this repository. To do this, at the top of the page, choose the **Fork** button. 74 | 4. Copy your fork to your computer. To do this, open Git Bash. At the command prompt enter: 75 | 76 | git clone https://github.com//.git 77 | 78 | Next, create a reference to the root repository by entering these commands: 79 | 80 | cd 81 | git remote add upstream https://github.com/microsoftgraph/.git 82 | git fetch upstream 83 | 84 | Congratulations! You've now set up your repository. You won't need to repeat these steps again. 85 | 86 | ### Contribute and edit content 87 | 88 | To make the contribution process as seamless as possible, follow these steps. 89 | 90 | #### To contribute and edit content 91 | 92 | 1. Create a new branch. 93 | 2. Add new content or edit existing content. 94 | 3. Submit a pull request to the main repository. 95 | 4. Delete the branch. 96 | 97 | **Important** Limit each branch to a single concept/article to streamline the work flow and reduce the chance of merge conflicts. Content appropriate for a new branch includes: 98 | 99 | * A new article. 100 | * Spelling and grammar edits. 101 | * Applying a single formatting change across a large set of articles (for example, applying a new copyright footer). 102 | 103 | #### To create a new branch 104 | 105 | 1. Open Git Bash. 106 | 2. At the Git Bash command prompt, type `git pull upstream master:`. This creates a new branch locally that is copied from the latest MicrosoftGraph master branch. 107 | 3. At the Git Bash command prompt, type `git push origin `. This alerts GitHub to the new branch. You should now see the new branch in your fork of the repository on GitHub. 108 | 4. At the Git Bash command prompt, type `git checkout ` to switch to your new branch. 109 | 110 | #### Add new content or edit existing content 111 | 112 | You navigate to the repository on your computer by using File Explorer. The repository files are in `C:\Users\\`. 113 | 114 | To edit files, open them in an editor of your choice and modify them. To create a new file, use the editor of your choice and save the new file in the appropriate location in your local copy of the repository. While working, save your work frequently. 115 | 116 | The files in `C:\Users\\` are a working copy of the new branch that you created in your local repository. Changing anything in this folder doesn't affect the local repository until you commit a change. To commit a change to the local repository, type the following commands in GitBash: 117 | 118 | git add . 119 | git commit -v -a -m "" 120 | 121 | The `add` command adds your changes to a staging area in preparation for committing them to the repository. The period after the `add` command specifies that you want to stage all of the files that you added or modified, checking subfolders recursively. (If you don't want to commit all of the changes, you can add specific files. You can also undo a commit. For help, type `git add -help` or `git status`.) 122 | 123 | The `commit` command applies the staged changes to the repository. The switch `-m` means you are providing the commit comment in the command line. The -v and -a switches can be omitted. The -v switch is for verbose output from the command, and -a does what you already did with the add command. 124 | 125 | You can commit multiple times while you are doing your work, or you can commit once when you're done. 126 | 127 | #### Submit a pull request to the main repository 128 | 129 | When you're finished with your work and are ready to have it merged into the main repository, follow these steps. 130 | 131 | #### To submit a pull request to the main repository 132 | 133 | 1. In the Git Bash command prompt, type `git push origin `. In your local repository, `origin` refers to your GitHub repository that you cloned the local repository from. This command pushes the current state of your new branch, including all commits made in the previous steps, to your GitHub fork. 134 | 2. On the GitHub site, navigate in your fork to the new branch. 135 | 3. Choose the **Pull Request** button at the top of the page. 136 | 4. Verify the Base branch is `microsoftgraph/@master` and the Head branch is `/@`. 137 | 5. Choose the **Update Commit Range** button. 138 | 6. Add a title to your pull request, and describe all the changes you're making. 139 | 7. Submit the pull request. 140 | 141 | One of the site administrators will process your pull request. Your pull request will surface on the microsoftgraph/ site under Issues. When the pull request is accepted, the issue will be resolved. 142 | 143 | #### Create a new branch after merge 144 | 145 | After a branch is successfully merged (that is, your pull request is accepted), don't continue working in that local branch. This can lead to merge conflicts if you submit another pull request. To do another update, create a new local branch from the successfully merged upstream branch, and then delete your initial local branch. 146 | 147 | For example, if your local branch X was successfully merged into the OfficeDev/microsoft-graph-docs master branch and you want to make additional updates to the content that was merged. Create a new local branch, X2, from the OfficeDev/microsoft-graph-docs master branch. To do this, open GitBash and execute the following commands: 148 | 149 | cd microsoft-graph-docs 150 | git pull upstream master:X2 151 | git push origin X2 152 | 153 | You now have local copies (in a new local branch) of the work that you submitted in branch X. The X2 branch also contains all the work other writers have merged, so if your work depends on others' work (for example, shared images), it is available in the new branch. You can verify that your previous work (and others' work) is in the branch by checking out the new branch... 154 | 155 | git checkout X2 156 | 157 | ...and verifying the content. (The `checkout` command updates the files in `C:\Users\\microsoft-graph-docs` to the current state of the X2 branch.) Once you check out the new branch, you can make updates to the content and commit them as usual. However, to avoid working in the merged branch (X) by mistake, it's best to delete it (see the following **Delete a branch** section). 158 | 159 | #### Delete a branch 160 | 161 | Once your changes are successfully merged into the main repository, delete the branch you used because you no longer need it. Any additional work should be done in a new branch. 162 | 163 | #### To delete a branch 164 | 165 | 1. In the Git Bash command prompt, type `git checkout master`. This ensures that you aren't in the branch to be deleted (which isn't allowed). 166 | 2. Next, at the command prompt, type `git branch -d `. This deletes the branch on your computer only if it has been successfully merged to the upstream repository. (You can override this behavior with the `–D` flag, but first be sure you want to do this.) 167 | 3. Finally, type `git push origin :` at the command prompt (a space before the colon and no space after it). This will delete the branch on your github fork. 168 | 169 | Congratulations, you have successfully contributed to the project! 170 | 171 | ## How to use Markdown to format your topic 172 | 173 | ### Article template 174 | 175 | The [markdown template](/articles/0-markdown-template-for-new-articles.md) contains the basic Markdown for a topic that includes a table of contents, sections with subheadings, links to other Office developer topics, links to other sites, bold text, italic text, numbered and bulleted lists, code snippets, and images. 176 | 177 | 178 | ### Standard Markdown 179 | 180 | All of the articles in this repository use Markdown. A complete introduction (and listing of all the syntax) can be found at [Markdown Home] []. 181 | 182 | ## FAQ 183 | 184 | ### How do I get a GitHub account? 185 | 186 | Fill out the form at [Join GitHub](https://github.com/join) to open a free GitHub account. 187 | 188 | ### Where do I get a Contributor's License Agreement? 189 | 190 | You will automatically be sent a notice that you need to sign the Contributor's License Agreement (CLA) if your pull request requires one. 191 | 192 | As a community member, **you must sign the Contribution License Agreement (CLA) before you can contribute large submissions to this project**. You only need complete and submit the documentation once. Carefully review the document. You may be required to have your employer sign the document. 193 | 194 | ### What happens with my contributions? 195 | 196 | When you submit your changes, via a pull request, our team will be notified and will review your pull request. You will receive notifications about your pull request from GitHub; you may also be notified by someone from our team if we need more information. We reserve the right to edit your submission for legal, style, clarity, or other issues. 197 | 198 | ### Can I become an approver for this repository's GitHub pull requests? 199 | 200 | Currently, we are not allowing external contributors to approve pull requests in this repository. 201 | 202 | ### How soon will I get a response about my change request or issue? 203 | 204 | We typically review pull requests and respond to issues within 10 business days. 205 | 206 | ## More resources 207 | 208 | * To learn more about Markdown, go to the Git creator's site [Daring Fireball]. 209 | * To learn more about using Git and GitHub, first check out the [GitHub Help section] [GitHub Help]. 210 | 211 | [GitHub Home]: http://github.com 212 | [GitHub Help]: http://help.github.com/ 213 | [Set Up Git]: http://help.github.com/win-set-up-git/ 214 | [Markdown Home]: http://daringfireball.net/projects/markdown/ 215 | [Daring Fireball]: http://daringfireball.net/ 216 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Integrate the Microsoft Graph API into a Java command line app using username and password 2 | 3 | This sample shows a command line app that calls the Azure AD-secured Microsoft Graph API. The app uses the [ScribeJava Authentication Library](https://github.com/scribejava/scribejava) to obtain a JSON Web Token (JWT) through the OAuth 2.0 protocol. The returned JWT is known as an **access token**. The access token is added to all requests on the Microsoft Graph API as an HTTP header. It authenticates the user and gets access to the service. 4 | 5 | This sample shows you how to use **ScribeJava** to authenticate users via simple credentials (username and password) using a text-only interface. 6 | 7 | ## Quick Start 8 | 9 | Getting started with the sample is easy. It is configured to run out of the box with minimal setup. 10 | 11 | ### Step 1: Register an Azure AD Tenant 12 | 13 | To use this sample you need a Azure Active Directory Tenant. If you're not sure what a tenant is or how you would get one, read [What is an Azure AD tenant](http://technet.microsoft.com/library/jj573650.aspx)? or [Sign up for Azure as an organization](http://azure.microsoft.com/documentation/articles/sign-up-organization/). These docs should get you started on your way to using Azure AD. 14 | 15 | ### Step 2: Download Java (7 and above) for your platform 16 | 17 | To use this sample, you need a working installation of [Java](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and [Gradle](https://gradle.org/). 18 | 19 | The sample was built using the Gradle plugin for IntelliJ IDEA IDE. The project build.gradle file is configured to let you run the sample from within IntelliJ. The run build configuration lets you run or debug the sample. 20 | 21 | ### Step 3: Download the Sample application and modules 22 | 23 | Next, clone the sample repository and install the project's dependencies. 24 | 25 | From your shell or command line: 26 | 27 | * `$ git@github.com:microsoftgraph/console-java-connect-sample.git` 28 | * `$ cd console-java-connect-sample` 29 | 30 | ### Step 4: Register the Console Java Connect app 31 | 32 | 1. Sign into [Azure Portal - App Registrations](https://go.microsoft.com/fwlink/?linkid=2083908) using either your personal or work or school account. 33 | 34 | 2. Choose **New registration**. 35 | 36 | 3. In the **Name** section, enter a meaningful application name that will be displayed to users of the app 37 | 38 | 1. In the **Supported account types** section, select **Accounts in any organizational directory and personal Microsoft accounts (e.g. Skype, Xbox, Outlook.com)** 39 | 40 | 1. Select **Register** to create the application. 41 | 42 | The application's Overview page shows the properties of your app. 43 | 44 | 4. Copy the **Application (client) Id**. This is the unique identifier for your app. 45 | 46 | 1. In the application's list of pages, select **Authentication**. 47 | 48 | 1. Under **Redirect URIs** in the **Suggested Redirect URIs for public clients (mobile, desktop)** section, check the box next to **https://login.microsoftonline.com/common/oauth2/nativeclient** 49 | 50 | 8. Choose **Save**. 51 | 52 | > **Note:** The Azure Active Directory v2.0 authorization endpoint uses [incremental and dynamic consent](https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-compare#incremental-and-dynamic-consent) which means that you don't need to set permissions (now called **scopes**) when you register your application. Scopes are requested by the your app at run-time. 53 | 54 | ### Step 5: Configure your app using Constants.java 55 | 56 | Using your favorite code editor, open **..\console-java-connect-sample\src\main\java\com\microsoft\graphsample\connect\Constants.java**. Paste the Application Id from the clipboard into Constants.java, line 11 to replace `ENTER_YOUR_CLIENT_ID`. 57 | 58 | ```java 59 | public class Constants { 60 | 61 | public final static String CLIENT_ID = "ENTER_YOUR_CLIENT_ID"; 62 | 63 | //... 64 | } 65 | ``` 66 | 67 | ### Step 6: Install Gradle 68 | 69 | If you do not have the Gradle build system installed, [install Gradle](https://docs.gradle.org/4.6/userguide/installation.html). 70 | 71 | ### Step 7: Add the Gradle wrapper to the project 72 | 73 | From your shell or command line at the project root: 74 | 75 | ```Shell 76 | gradle wrapper 77 | ``` 78 | 79 | ### Step 8: Build and run the sample 80 | 81 | From your shell or command line at the project root: 82 | 83 | ```Shell 84 | gradle run 85 | ``` 86 | 87 | This will run the sample. 88 | 89 | ### Running the sample 90 | 91 | The command line interface opens a browser window on the Azure Active Directory authorization endpoint. Enter your user name and password to authenticate. When you are authenticated, you're taken to an authorization window for the sample app. Review and accept the scopes requested by the sample app. Click the Ok button on the authorization window. The browser navigates to `https://login.microsoftonline.com`. Copy the full redirect URL address and paste it into a text editor. It should look like the following url: 92 | 93 | ```http 94 | https://login.microsoftonline.com/common/oauth2/nativeclient?code={IAQABAAIAAABHh4kmS_aKT5XrjzxRAtHz5S...p7OoAFPmGPqIq-1_bMCAA}&session_state=dd64ce71-4424-494b-8818-be9a99ca0798 95 | ``` 96 | 97 | > **Note:** URL example is truncated for clarity. `{` and `}` were added to the example to delimit the authorization code for illustration purposes. 98 | 99 | The authorization code starts after `?code=` and ends before `&session_state`. 100 | 101 | Copy the authorization code to the system clipboard and paste it into the console under `And paste the authorization code here`. 102 | 103 | You are prompted with: 104 | 105 | ```Shell 106 | Hello, {your name}. Would you like to send an email to yourself or someone else? 107 | Enter the address to which you'd like to send a message. If you enter nothing, the message will go to your address 108 | ``` 109 | 110 | After you enter an email address or leave the prompt blank, an email is sent to you or the address you type into the console. 111 | 112 | You can send email to other addresses by responding with a "y" to the prompt `Want to send another message? Type 'y' for yes and any other key to exit.` 113 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.microsoft.graph.samples' 2 | version '1.0-SNAPSHOT' 3 | 4 | apply plugin: 'java' 5 | 6 | repositories { 7 | 8 | maven { url "http://repo.maven.apache.org/maven2" } 9 | jcenter {url "https://dl.bintray.com/microsoftgraph/Maven"} 10 | } 11 | dependencies { 12 | compile group: 'com.nimbusds', name: 'oauth2-oidc-sdk', version:'4.5' 13 | compile group: 'org.json', name: 'json', version:'20090211' 14 | compile group: 'org.slf4j', name: 'slf4j-log4j12', version:'1.7.5' 15 | compile group: 'com.github.scribejava', name: 'scribejava-apis', version:'6.2.0' 16 | compile group: 'org.apache.commons', name: 'commons-io', version:'1.3.2' 17 | compile group: 'com.gilecode.yagson', name: 'j9-reflection-utils', version:'1.0' 18 | compile group: 'com.microsoft.graph', name: 'microsoft-graph', version: '1.0.+' 19 | compileOnly group: 'javax.servlet', name: 'javax.servlet-api', version:'3.0.1' 20 | } 21 | 22 | jar { 23 | manifest { 24 | attributes( 25 | 'Class-Path': configurations.compile.collect { it.getName() }.join(' '), 26 | 'Main-Class': 'com.microsoft.graphsample.PublicClient.main' 27 | ) 28 | } 29 | } 30 | 31 | task run(type: JavaExec, dependsOn: classes) { 32 | standardInput = System.in 33 | main = 'com.microsoft.graphsample.PublicClient' 34 | classpath = sourceSets.main.runtimeClasspath 35 | } -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'console-java-connect-sample' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: com.microsoft.graphsample 3 | Main-Class: com.microsoft.graphsample.PublicClient 4 | 5 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/PublicClient.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.graphsample; 2 | 3 | import com.gilecode.reflection.ReflectionAccessUtils; 4 | import com.microsoft.graph.models.extensions.User; 5 | import com.microsoft.graphsample.connect.AuthenticationManager; 6 | import com.microsoft.graphsample.connect.DebugLogger; 7 | import com.microsoft.graphsample.msgraph.GraphSendMail; 8 | import com.microsoft.graphsample.msgraph.SendMailException; 9 | 10 | import java.io.IOException; 11 | import java.util.Scanner; 12 | import java.util.logging.Level; 13 | 14 | public class PublicClient { 15 | protected static AuthenticationManager authenticationManager = null; 16 | DebugLogger mLogger; 17 | Scanner mScanner; 18 | 19 | public PublicClient() throws IOException { 20 | mLogger = DebugLogger.getInstance(); 21 | mScanner = new Scanner(System.in, "UTF-8"); 22 | } 23 | 24 | public static void main(String args[]) throws Exception { 25 | PublicClient publicClient = new PublicClient(); 26 | try { 27 | publicClient.startConnect(); 28 | } catch(Exception ex) { 29 | System.out.println(ex.getMessage()); 30 | 31 | } finally { 32 | publicClient.mScanner.close(); 33 | } 34 | } 35 | 36 | private void startConnect() throws Exception { 37 | System.out.println("Welcome to the Java Console Connect Sample!"); 38 | 39 | //If this call is removed, functionality of the sample is not 40 | //affected. The call simply suppresses warnings generated from json deserialization 41 | //in the Graph SDK 42 | ReflectionAccessUtils.suppressIllegalReflectiveAccessWarnings(); 43 | authenticationManager = AuthenticationManager.getInstance(); 44 | authenticationManager.connect(mScanner); 45 | startSendMail(); 46 | } 47 | 48 | /** 49 | * Prompts user for email address to send mail and prompts 50 | * for additional email address to send mail. Continues until 51 | * user declines to send a mail. 52 | * 53 | * @throws Exception 54 | */ 55 | private void startSendMail() throws Exception { 56 | GraphSendMail graphSendMail = new GraphSendMail(); 57 | User meUser = null; 58 | try { 59 | meUser = graphSendMail.getMeUser(); 60 | 61 | String preferredName = meUser.displayName; 62 | Boolean sendAnotherMail = true; 63 | while (sendAnotherMail) { 64 | System.out.println( 65 | "Hello, " + preferredName + ". Would you like to send an email to yourself or someone else?"); 66 | String sendMailAddress = ""; 67 | sendMailAddress = getUserInput("Enter the address to which you'd like to send a message. " + 68 | "If you enter nothing, the message will go to your address"); 69 | sendMailAddress = sendMailAddress.isEmpty() ? meUser.mail : sendMailAddress; 70 | graphSendMail.sendMail(sendMailAddress); 71 | 72 | String sendAnotherYN = getUserInput("\nEmail sent! \n Want to send another message? Type 'y' for yes and any other key to exit."); 73 | if (sendAnotherYN.isEmpty()) { 74 | sendAnotherMail = false; 75 | } 76 | else { 77 | sendAnotherMail = (sendAnotherYN.charAt(0) == 'y') ? true : false; 78 | } 79 | } 80 | if (PublicClient.getAuthenticationManager() != null) { 81 | PublicClient.getAuthenticationManager().disconnect(); 82 | } 83 | 84 | } catch (SendMailException ex) { 85 | mLogger.writeLog(Level.SEVERE, ex.getMessage(), ex); 86 | return; 87 | } catch (Exception e) { 88 | mLogger.writeLog(Level.SEVERE, "Exception in startSendMail ", e); 89 | } 90 | 91 | } 92 | 93 | public static AuthenticationManager getAuthenticationManager() throws IOException { 94 | 95 | try { 96 | return AuthenticationManager.getInstance(); 97 | } finally { 98 | 99 | } 100 | } 101 | 102 | public String getUserInput(String prompt) throws Exception { 103 | System.out.println(prompt); 104 | final String code = mScanner.nextLine(); 105 | return code; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/connect/AuthenticationManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | package com.microsoft.graphsample.connect; 6 | 7 | import com.github.scribejava.core.builder.ServiceBuilder; 8 | import com.github.scribejava.core.model.*; 9 | import com.github.scribejava.core.oauth.OAuth20Service; 10 | import com.microsoft.graph.logger.LoggerLevel; 11 | 12 | import java.io.*; 13 | import java.net.URI; 14 | import java.net.URISyntaxException; 15 | import java.util.concurrent.ExecutionException; 16 | import java.util.concurrent.Future; 17 | import java.util.logging.Level; 18 | import java.util.Scanner; 19 | 20 | import static java.awt.Desktop.getDesktop; 21 | import static java.awt.Desktop.isDesktopSupported; 22 | 23 | /** 24 | * Handles setup of OAuth library in API clients. 25 | */ 26 | public class AuthenticationManager { 27 | 28 | private static AuthenticationManager INSTANCE; 29 | private OAuth20Service mOAuthService = null; 30 | private OAuth2AccessToken mAccessToken; 31 | 32 | /** 33 | * Initialization block. Runs before constructor to get a logger and start up the ScribeJava OAuth2 34 | * authentication service 35 | */ 36 | { 37 | if (Debug.DebugLevel == LoggerLevel.DEBUG) { 38 | DebugLogger.getInstance().writeLog(Level.INFO, "AuthenticationManager initialization block called"); 39 | 40 | try (OAuth20Service service = new ServiceBuilder(Constants.CLIENT_ID) 41 | .callback(Constants.REDIRECT_URL) 42 | .scope(Constants.SCOPES) 43 | .apiKey(Constants.CLIENT_ID) 44 | .debugStream(System.out) 45 | .debug() 46 | .build(MicrosoftAzureAD20Api.instance()) 47 | ) { 48 | mOAuthService = service; 49 | } catch (java.io.IOException | IllegalArgumentException ex) { 50 | try { 51 | throw ex; 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | } 55 | } 56 | } 57 | else { 58 | try (OAuth20Service service = new ServiceBuilder(Constants.CLIENT_ID) 59 | .callback(Constants.REDIRECT_URL) 60 | .scope(Constants.SCOPES) 61 | .apiKey(Constants.CLIENT_ID) 62 | .build(MicrosoftAzureAD20Api.instance()) 63 | ) { 64 | mOAuthService = service; 65 | } catch (java.io.IOException | IllegalArgumentException ex) { 66 | try { 67 | throw ex; 68 | } catch (IOException e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | } 73 | } 74 | 75 | private AuthenticationManager() throws IOException { 76 | DebugLogger.getInstance().writeLog(Level.INFO, "AuthenticationManager constructor called"); 77 | } 78 | 79 | public static synchronized AuthenticationManager getInstance() throws java.io.IOException { 80 | if (INSTANCE == null) { 81 | INSTANCE = new AuthenticationManager(); 82 | } 83 | return INSTANCE; 84 | } 85 | 86 | public OAuth20Service getOAuthService() { 87 | return mOAuthService; 88 | } 89 | 90 | public static synchronized void resetInstance() { 91 | INSTANCE = null; 92 | } 93 | 94 | public String getRefreshToken() { 95 | if (mAccessToken == null) { 96 | return ""; 97 | } 98 | return mAccessToken.getRefreshToken(); 99 | } 100 | 101 | public String getAccessToken() { 102 | if (mAccessToken == null) { 103 | return ""; 104 | } 105 | return mAccessToken.getAccessToken(); 106 | } 107 | 108 | /** 109 | This method implements the Authorization Grant flow for OAuth 2.0 110 | A user responds to the credentials challenge in the browser opened by this method. User 111 | types their credentials in the browser window and is redirected to an authorization page. 112 | User accepts the sample app's requests to access Microsoft Graph resources and an 113 | authorization token is returned to the callback url. 114 | In a breakpoint before the service.getAccessToken call, update the authorizationCode string variable 115 | with the authorization code from the POST to the callback url. 116 | 117 | @throws URISyntaxException 118 | @throws IOException 119 | @throws InterruptedException 120 | @throws ExecutionException 121 | 122 | */ 123 | 124 | public void connect(Scanner inputScanner) throws URISyntaxException, IOException, InterruptedException, ExecutionException { 125 | try { 126 | mAccessToken = mOAuthService.getAccessToken(getAuthorizationCode(inputScanner)); 127 | showAuthTokenToUser(); 128 | makeAuthenticatedMeRequest(); 129 | } finally { 130 | } 131 | } 132 | 133 | 134 | /** 135 | * Connects the user to Microsoft Graph API asynchronously 136 | * 137 | * @param callback handles the authentication result. 138 | * @throws URISyntaxException 139 | * @throws IOException 140 | * @throws InterruptedException 141 | * @throws ExecutionException 142 | */ 143 | public Future connectAsync( 144 | Scanner inputScanner, 145 | IConnectCallback callback) throws 146 | URISyntaxException, 147 | IOException, 148 | InterruptedException, 149 | ExecutionException 150 | { 151 | Future future = null; 152 | try { 153 | final String code = getAuthorizationCode(inputScanner); 154 | future = mOAuthService 155 | .getAccessToken(code, new OAuthAsyncRequestCallback() { 156 | 157 | @Override 158 | public void onCompleted(OAuth2AccessToken oAuth2AccessToken) { 159 | mAccessToken = oAuth2AccessToken; 160 | showAuthTokenToUser(); 161 | 162 | try { 163 | callback.onCompleted(); 164 | } catch (InterruptedException e) { 165 | e.printStackTrace(); 166 | } catch (ExecutionException e) { 167 | e.printStackTrace(); 168 | } 169 | 170 | } 171 | 172 | @Override 173 | public void onThrowable(Throwable throwable) { 174 | callback.onThrowable(throwable); 175 | } 176 | }, Constants.CLIENT_ID); 177 | return future; 178 | 179 | } finally { 180 | 181 | } 182 | } 183 | 184 | 185 | /** 186 | * Disconnects the app from Office 365 by clearing the token cache, setting the client objects 187 | * to null, and removing the user id from shred preferences. 188 | */ 189 | public void disconnect() throws Exception { 190 | try { 191 | // Commented out - ScribeJava does not support revoking access tokens yet. 192 | // mOAuthService.revokeToken(mAccessToken.getAccessToken()); 193 | } finally { 194 | } 195 | } 196 | 197 | /** 198 | * Displays the raw token to the user on the system console 199 | */ 200 | private void showAuthTokenToUser() { 201 | System.out.println("Got the Access Token!"); 202 | System.out.println( 203 | "(if you're curious the raw answer looks like this: " + mAccessToken.getRawResponse() + "')"); 204 | System.out.println(); 205 | // Now let's go and ask for a protected resource! 206 | System.out.println("Now we're going to access a protected resource..."); 207 | } 208 | 209 | /** 210 | * Creates and runs REST authenticated request for Me resource synchronously. The response includes a 211 | * JSON payload with a representation of the me resource. 212 | * 213 | * @throws InterruptedException 214 | * @throws ExecutionException 215 | * @throws IOException 216 | */ 217 | private void makeAuthenticatedMeRequest() throws InterruptedException, ExecutionException, IOException { 218 | final OAuthRequest request = new OAuthRequest(Verb.GET, Constants.PROTECTED_RESOURCE_URL); 219 | mOAuthService.signRequest(mAccessToken, request); 220 | request.addHeader("Accept", "application/json, text/plain, */*"); 221 | final Response response = mOAuthService.execute(request); 222 | System.out.println("Got it! Let's see what we found..."); 223 | System.out.println(); 224 | System.out.println(response.getCode()); 225 | System.out.println(response.getBody()); 226 | System.out.println(); 227 | System.out.println("Thats it! Go and build something awesome with Microsoft Graph! :)"); 228 | } 229 | 230 | /** 231 | * Gets the user authorization grant flow URL, opens a browser tab with the resulting authorization token 232 | * embedded in the address URL. User copies the URL, extracts the token, and pastes it into the system console. 233 | * The Scanner reads the authorization code from the system console and returns the value to calling code. 234 | * 235 | * The authorization code is returned when the user accepts the sample app's requests to access Microsoft Graph 236 | * resources. The authorization code lists the Microsoft Graph resources that the user has given the sample 237 | * app permission to access. 238 | * 239 | * @return Authorization code 240 | * @throws IOException 241 | * @throws URISyntaxException 242 | */ 243 | private String getAuthorizationCode(Scanner inputScanner) throws IOException, URISyntaxException { 244 | System.out.println("=== " + Constants.NETWORK_NAME + "'s OAuth Workflow ==="); 245 | System.out.println(); 246 | // Obtain the Authorization URL 247 | System.out.println("Fetching the Authorization URL..."); 248 | final String authorizationUrl = mOAuthService.getAuthorizationUrl(); 249 | System.out.println("Got the Authorization URL!"); 250 | if (isDesktopSupported()) { 251 | getDesktop().browse(new URI(authorizationUrl)); 252 | } 253 | else { 254 | System.out.println("Now go and authorize Java-Native-Console-Connect here:"); 255 | System.out.println(authorizationUrl); 256 | } 257 | String code = ""; 258 | System.out.println("And paste the authorization code here"); 259 | try { 260 | code = inputScanner.nextLine(); 261 | } catch (Exception e) { 262 | e.printStackTrace(); 263 | } 264 | System.out.println(); 265 | // Trade the Request Token and Verfier for the Access Token 266 | System.out.println("Trading the Request Token for an Access Token..."); 267 | return code; 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/connect/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | package com.microsoft.graphsample.connect; 6 | 7 | public class Constants { 8 | 9 | public static final String DEFAULT_IMAGE_FILENAME = "test.jpg"; 10 | public static final String NETWORK_NAME = "Microsoft Azure Active Directory"; 11 | public final static String CLIENT_ID = "ENTER_YOUR_CLIENT_ID"; 12 | public final static String REDIRECT_URL = "https://login.microsoftonline.com/common/oauth2/nativeclient"; 13 | public final static String SCOPES = "Files.ReadWrite openid User.Read Mail.Send Mail.ReadWrite"; 14 | public static final String PROTECTED_RESOURCE_URL = "https://graph.microsoft.com/v1.0/me"; 15 | 16 | public static final String SUBJECT_TEXT = "Welcome to Microsoft Graph development for Java with the Connect sample"; 17 | 18 | // The Microsoft Graph delegated permissions that you set in the application 19 | // registration portal must match these scope values. 20 | // Update this constant with the scope (permission) values for your application: 21 | public static final String MESSAGE_BODY = "\n" + 22 | "\n" + 23 | "\n" + 24 | "\n" + 25 | "\n" + 26 | "

Congratulations!

\n" + 27 | "

This is a message from the Microsoft Graph Connect Sample. You are well on your way to incorporating Microsoft Graph endpoints in your apps.

See the photo you just uploaded!\n" + 28 | "

What\'s next?

    \n" + 29 | "
  • Check out developer.microsoft.com/graph to start building Microsoft Graph apps today with all the latest tools, templates, and guidance to get started quickly.
  • \n" + 30 | "
  • Use the Graph Explorer to explore the rest of the APIs and start your testing.
  • \n" + 31 | "
  • Browse other samples on GitHub to see more of the APIs in action.
  • \n" + 32 | "
\n" + 33 | "

Give us feedback

\n" + 34 | "

If you have any trouble running this sample, please \n" + 35 | "log an issue on our repository.

For general questions about the Microsoft Graph API, post to Stack Overflow. Make sure that your questions or comments are tagged with [microsoftgraph].

\n" + 36 | "

Thanks, and happy coding!
\n" + 37 | "&nbsp;&nbsp;&nbsp;&nbsp;Your Microsoft Graph samples development team

\n" + 38 | "
\n" + 39 | "\n" + 40 | "\n" + 41 | "\n" + 42 | "\n" + 44 | "\n" + 46 | "\n" + 48 | "\n" + 49 | "\n" + 50 | "
See on GitHub\n" + 43 | "Suggest on UserVoice\n" + 45 | "Share on Twitter\n" + 47 | "
\n" + 51 | "
\n" + 52 | "\n" + 53 | ""; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/connect/Debug.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.graphsample.connect; 2 | 3 | import com.microsoft.graph.logger.LoggerLevel; 4 | 5 | /** 6 | * Exposes a module wide debug vaiable that results in the conditional 7 | * execution of debug code. 8 | */ 9 | public final class Debug { 10 | //set to false to allow compiler to identify and eliminate 11 | //unreachable code 12 | public static final LoggerLevel DebugLevel = LoggerLevel.ERROR; 13 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/connect/DebugLogger.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.graphsample.connect; 2 | 3 | import com.microsoft.graph.logger.LoggerLevel; 4 | 5 | import java.util.logging.ConsoleHandler; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public final class DebugLogger { 10 | 11 | private static Logger log; 12 | private static DebugLogger INSTANCE; 13 | 14 | 15 | /** 16 | * Initialization block called before default constructor 17 | * Configures 18 | */ 19 | { 20 | String loggerName = "com.microsoft.graphsample.connect"; 21 | log = Logger.getLogger(loggerName); 22 | ConsoleHandler handler = new ConsoleHandler(); 23 | handler.setLevel(Level.ALL); 24 | log.addHandler(handler); 25 | log.setLevel(Level.ALL); 26 | } 27 | 28 | public static synchronized DebugLogger getInstance() throws java.io.IOException { 29 | 30 | if (INSTANCE == null) { 31 | INSTANCE = new DebugLogger(); 32 | } 33 | return INSTANCE; 34 | } 35 | 36 | 37 | /** 38 | * Writes an exception to system.out 39 | * @param logLevel 40 | * @param message 41 | * @param exception 42 | */ 43 | public void writeLog(Level logLevel, String message, Exception exception) { 44 | if (Debug.DebugLevel == LoggerLevel.DEBUG) { 45 | log.log(logLevel, message, exception); 46 | } 47 | } 48 | 49 | /** 50 | * Writes info messages to system.out 51 | * @param logLevel 52 | * @param message 53 | */ 54 | public void writeLog(Level logLevel, String message) { 55 | if (Debug.DebugLevel == LoggerLevel.DEBUG) { 56 | log.log(logLevel, message); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/connect/IConnectCallback.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.graphsample.connect; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | 5 | public interface IConnectCallback { 6 | void onCompleted() throws InterruptedException, ExecutionException; 7 | 8 | void onThrowable(Throwable var1); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/connect/MicrosoftAzureAD20Api.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.graphsample.connect; 2 | 3 | import com.github.scribejava.core.builder.api.DefaultApi20; 4 | import com.github.scribejava.core.oauth2.clientauthentication.ClientAuthentication; 5 | import com.github.scribejava.core.oauth2.clientauthentication.RequestBodyAuthenticationScheme; 6 | 7 | public class MicrosoftAzureAD20Api extends DefaultApi20 { 8 | 9 | private MicrosoftAzureAD20Api() { 10 | } 11 | 12 | private static class InstanceHolder { 13 | static MicrosoftAzureAD20Api INSTANCE = new MicrosoftAzureAD20Api(); 14 | } 15 | 16 | @Override 17 | public String getAccessTokenEndpoint() { 18 | return "https://login.microsoftonline.com/common/oauth2/v2.0/token"; 19 | } 20 | 21 | @Override 22 | protected String getAuthorizationBaseUrl() { 23 | return "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; 24 | } 25 | 26 | public static DefaultApi20 instance() { 27 | return InstanceHolder.INSTANCE; 28 | } 29 | 30 | @Override 31 | public ClientAuthentication getClientAuthentication() { 32 | return RequestBodyAuthenticationScheme.instance(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/msgraph/GraphSendMail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | package com.microsoft.graphsample.msgraph; 6 | 7 | import com.microsoft.graph.models.extensions.DriveItem; 8 | import com.microsoft.graph.models.extensions.Message; 9 | import com.microsoft.graph.models.extensions.Permission; 10 | import com.microsoft.graph.models.extensions.User; 11 | import com.microsoft.graphsample.connect.Constants; 12 | import com.microsoft.graphsample.connect.DebugLogger; 13 | 14 | import java.util.logging.Level; 15 | 16 | /** 17 | * This class handles the send mail operation of the app. 18 | * The app must be connected to Office 365 before this activity can send an email. 19 | * It also uses the GraphServiceController to send the message. 20 | */ 21 | public class GraphSendMail { 22 | final private GraphServiceController mGraphServiceController; 23 | DebugLogger mLogger; 24 | private String mRecipientEmailAddress; 25 | 26 | public GraphSendMail() throws SendMailException { 27 | try { 28 | mGraphServiceController = new GraphServiceController(); 29 | mLogger = DebugLogger.getInstance(); 30 | 31 | } catch (Exception e) { 32 | throw new SendMailException("Exception in GraphSendMail constructor", e); 33 | } 34 | } 35 | 36 | /** 37 | * Handler for the onclick event of the send mail button. It uses the GraphServiceController to 38 | * send an email. When the call is completed, the call will return to either the success() 39 | * or failure() methods in this class which will then take the next steps on the UI. 40 | * This method sends the email using the address stored in the mEmailEditText view. 41 | * The subject and body of the message is stored in the strings.xml file. 42 | *

43 | * The following calls are made asynchronously in a chain of callback invocations. 44 | * 1. Get the user's profile picture from Microsoft Graph 45 | * 2. Upload the profile picture to the user's OneDrive root folder 46 | * 3. Get a sharing link to the picture from OneDrive 47 | * 4. Create and post a draft email 48 | * 5. Get the draft message 49 | * 6. Attach the profile picture to the draft mail as a byte array 50 | * 7. Send the draft email 51 | */ 52 | 53 | public void sendMail(String sendAddress) { 54 | mRecipientEmailAddress = sendAddress; 55 | byte[] photoBytes = null; 56 | try { 57 | //1. Get the signed in user's profile picture 58 | photoBytes = mGraphServiceController.getUserProfilePicture(); 59 | if (photoBytes != null) { 60 | 61 | //2. Upload the profile picture to OneDrive 62 | DriveItem driveItem = mGraphServiceController.uploadPictureToOneDrive(photoBytes); 63 | if (driveItem != null) { 64 | //3. Get the sharing link and if success, call step 4 helper 65 | mLogger.writeLog(Level.INFO, "Getting the sharing link "); 66 | getPermissionSharingLink(driveItem, photoBytes); 67 | } 68 | } 69 | } catch (SendMailException ex) { 70 | mLogger.writeLog(Level.SEVERE, ex.getMessage(), ex); 71 | } finally { 72 | mLogger.writeLog(Level.INFO, "Started send mail operation "); 73 | } 74 | } 75 | 76 | 77 | public User getMeUser() throws SendMailException { 78 | return mGraphServiceController.getUser(); 79 | } 80 | 81 | /** 82 | * Gets the picture sharing link from OneDrive and calls the step 4 helper 83 | * 84 | * @param driveItem 85 | * @param bytes 86 | */ 87 | private void getPermissionSharingLink(DriveItem driveItem, final byte[] bytes) { 88 | //3. Get a sharing link to the picture uploaded to OneDrive 89 | try { 90 | createDraftMail(mGraphServiceController.getPermissionSharingLink(driveItem.id), bytes); 91 | } catch (SendMailException ex) { 92 | mLogger.writeLog(Level.SEVERE, ex.getMessage(), ex); 93 | } 94 | } 95 | 96 | /** 97 | * Creates a draft mail and calls the step 5 helper 98 | * 99 | * @param permission 100 | * @param bytes 101 | */ 102 | private void createDraftMail(final Permission permission, final byte[] bytes) { 103 | //Prepare body message and insert name of sender 104 | String body = Constants.MESSAGE_BODY; 105 | try { 106 | //replace() is used instead of format() because the mail body string contains several 107 | //'%' characters, most of which are not string place holders. When format() is used, 108 | //format exception is thrown. Place holders do not match replacement parameters. 109 | body = body.replace("a href=%s", "a href=" + permission.link.webUrl.toString()); 110 | final String mailBody = body; 111 | //4. Create a draft mail message 112 | Message message = mGraphServiceController.createDraftMail( 113 | mRecipientEmailAddress, 114 | Constants.SUBJECT_TEXT, 115 | mailBody); 116 | if (message != null) { 117 | mLogger.writeLog(Level.INFO, "Create draft mail "); 118 | Message draftMessage = mGraphServiceController.getDraftMessage(message.id); 119 | addPictureToDraftMessage(draftMessage, permission, bytes); 120 | } 121 | else { 122 | mLogger.writeLog(Level.INFO, "Create draft mail failed "); 123 | } 124 | 125 | } catch (SendMailException ex) { 126 | mLogger.writeLog(Level.SEVERE, ex.getMessage(), ex); 127 | } 128 | } 129 | 130 | 131 | /** 132 | * Adds the picture bytes as attachment to the draft message and calls the step 7 helper 133 | * 134 | * @param aMessage 135 | * @param permission 136 | * @param bytes 137 | */ 138 | private void addPictureToDraftMessage(final Message aMessage, final Permission permission, final byte[] bytes) { 139 | //6. Add the profile picture to the draft mail 140 | try { 141 | mGraphServiceController.addPictureToDraftMessage(aMessage.id, bytes, permission.link.webUrl); 142 | mLogger.writeLog(Level.INFO, "Sending draft message "); 143 | sendDraftMessage(aMessage); 144 | } catch (SendMailException ex) { 145 | mLogger.writeLog(Level.SEVERE, ex.getMessage(), ex); 146 | } 147 | } 148 | 149 | 150 | /** 151 | * Sends the draft message 152 | * 153 | * @param aMessage 154 | */ 155 | private void sendDraftMessage(final Message aMessage) { 156 | //7. Send the draft message to the recipient 157 | try { 158 | mGraphServiceController.sendDraftMessage(aMessage.id, 0); 159 | mLogger.writeLog(Level.INFO, "Draft message sent "); 160 | 161 | } catch (SendMailException ex) { 162 | mLogger.writeLog(Level.SEVERE, ex.getMessage(), ex); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/msgraph/GraphServiceClientManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | package com.microsoft.graphsample.msgraph; 6 | 7 | 8 | import com.microsoft.graph.authentication.IAuthenticationProvider; 9 | import com.microsoft.graph.core.ClientException; 10 | import com.microsoft.graph.core.DefaultClientConfig; 11 | import com.microsoft.graph.core.IClientConfig; 12 | import com.microsoft.graph.http.IHttpRequest; 13 | import com.microsoft.graph.logger.LoggerLevel; 14 | import com.microsoft.graph.models.extensions.IGraphServiceClient; 15 | import com.microsoft.graph.requests.extensions.GraphServiceClient; 16 | import com.microsoft.graphsample.connect.AuthenticationManager; 17 | 18 | import java.io.IOException; 19 | 20 | /** 21 | * Singleton class that manages a GraphServiceClient object. 22 | * It implements the IAuthentication provider interface necessary to 23 | * insert the access token obtained in {@link AuthenticationManager} class 24 | * into REST requests in the Java Graph SDK. 25 | */ 26 | public class GraphServiceClientManager implements IAuthenticationProvider { 27 | private IGraphServiceClient mGraphServiceClient; 28 | private static GraphServiceClientManager INSTANCE; 29 | 30 | private GraphServiceClientManager() { 31 | } 32 | 33 | public static synchronized GraphServiceClientManager getInstance() { 34 | if (INSTANCE == null) { 35 | INSTANCE = new GraphServiceClientManager(); 36 | } 37 | return INSTANCE; 38 | } 39 | 40 | /** 41 | * Appends an access token obtained from the {@link AuthenticationManager} class to the 42 | * Authorization header of the request. 43 | * 44 | * @param request 45 | */ 46 | @Override 47 | public void authenticateRequest(IHttpRequest request) { 48 | try { 49 | request.addHeader("Authorization", "Bearer " 50 | + AuthenticationManager.getInstance() 51 | .getAccessToken()); 52 | // This header has been added to identify this sample in the Microsoft Graph service. 53 | // If you're using this code for your project please remove the following line. 54 | // request.addHeader("SampleID", "android-java-connect-sample"); 55 | // Log.getLog() .i("Connect", "Request: " + request.toString()); 56 | } catch (ClientException e) { 57 | e.printStackTrace(); 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } catch (NullPointerException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | public synchronized IGraphServiceClient getGraphServiceClient() { 66 | return getGraphServiceClient(this); 67 | } 68 | 69 | /** 70 | * Provides a new instance of the Microsoft Graph SDK client on first request. 71 | * 72 | * @param authenticationProvider The interface that exposes an access token getter. Getter is called with each HTTP operation 73 | * @return 74 | */ 75 | public synchronized IGraphServiceClient getGraphServiceClient(IAuthenticationProvider authenticationProvider) { 76 | if (mGraphServiceClient == null) { 77 | IClientConfig clientConfig = DefaultClientConfig.createWithAuthenticationProvider( 78 | authenticationProvider 79 | ); 80 | clientConfig.getLogger().setLoggingLevel(LoggerLevel.ERROR); 81 | mGraphServiceClient = GraphServiceClient.fromConfig(clientConfig); 82 | } 83 | 84 | return mGraphServiceClient; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/msgraph/GraphServiceController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. 3 | * See LICENSE in the project root for license information. 4 | */ 5 | package com.microsoft.graphsample.msgraph; 6 | 7 | 8 | import com.microsoft.graph.concurrency.ICallback; 9 | import com.microsoft.graph.models.extensions.*; 10 | import com.microsoft.graph.models.generated.BodyType; 11 | import com.microsoft.graphsample.connect.Constants; 12 | import com.microsoft.graphsample.connect.DebugLogger; 13 | import org.apache.commons.io.output.ByteArrayOutputStream; 14 | 15 | import java.io.*; 16 | import java.io.File; 17 | import java.util.Collections; 18 | import java.util.logging.Level; 19 | 20 | /** 21 | * Handles the creation and update of a mail message. Uses the GraphServiceClient to 22 | * send the message. The user must authenticate with the Microsoft Graph before sending mail 23 | * {@link #createDraftMail(String, String, String)}method. 24 | */ 25 | class GraphServiceController { 26 | 27 | private final IGraphServiceClient mGraphServiceClient; 28 | 29 | public GraphServiceController() { 30 | mGraphServiceClient = GraphServiceClientManager.getInstance().getGraphServiceClient(); 31 | } 32 | 33 | /** 34 | * Creates a draft email message using the Microsoft Graph API on Office 365. The mail is sent 35 | * from the address of the signed in user. 36 | * 37 | * @param emailAddress The recipient email address. 38 | * @param subject The subject to use in the mail message. 39 | * @param body The body of the message. 40 | */ 41 | public Message createDraftMail( 42 | final String emailAddress, 43 | final String subject, 44 | final String body 45 | ) throws SendMailException { 46 | Message message = null; 47 | try { 48 | // create the email message 49 | message = createMessage(subject, body, emailAddress); 50 | message = mGraphServiceClient 51 | .me() 52 | .messages() 53 | .buildRequest() 54 | .post(message); 55 | } catch (Exception ex) { 56 | throw new SendMailException("exception on send mail", ex); 57 | } 58 | 59 | return message; 60 | } 61 | 62 | 63 | /** 64 | * Creates a new email message with attachment and sends it to a specified recipient 65 | * 66 | * @param emailAddress Recipient email address 67 | * @param subject Subject of the email message 68 | * @param body Email body 69 | * @param attachment Attachment object. Sent as not inline 70 | * @param callback Callback invoked when send mail operation is complete 71 | * @throws SendMailException Exception thrown by Graph SDK with custom exception description 72 | */ 73 | public void sendNewMessageAsync( 74 | final String emailAddress, 75 | final String subject, 76 | final String body, 77 | final Attachment attachment, 78 | ICallback callback 79 | ) throws SendMailException { 80 | try { 81 | Message message = createMessage(subject, body, emailAddress); 82 | message.attachments.getCurrentPage().add(attachment); 83 | mGraphServiceClient 84 | .me() 85 | .sendMail(message, true) 86 | .buildRequest() 87 | .post(callback); 88 | 89 | } catch (Exception ex) { 90 | try { 91 | DebugLogger.getInstance().writeLog(Level.SEVERE, "exception on send new message", ex); 92 | 93 | } catch (Exception e) { 94 | throw new SendMailException("SendNewMessageAsync failed ", e); 95 | } 96 | 97 | } 98 | } 99 | 100 | /** 101 | * Posts a file attachment in a draft message by message Id 102 | * 103 | * @param messageId String. The id of the draft message to add an attachment to 104 | * @param picture Byte[]. The picture in bytes 105 | * @param sharingLink String. The sharing link to the uploaded picture 106 | */ 107 | public Attachment addPictureToDraftMessage( 108 | String messageId, 109 | byte[] picture, 110 | String sharingLink) throws SendMailException { 111 | Attachment attachment = null; 112 | try { 113 | byte[] attachementBytes = new byte[picture.length]; 114 | if (picture.length > 0) { 115 | attachementBytes = picture; 116 | } 117 | else { 118 | attachementBytes = getDefaultPicture(); 119 | } 120 | 121 | FileAttachment fileAttachment = new FileAttachment(); 122 | fileAttachment.oDataType = "#microsoft.graph.fileAttachment"; 123 | fileAttachment.contentBytes = attachementBytes; 124 | fileAttachment.name = "me.png"; 125 | fileAttachment.size = attachementBytes.length; 126 | fileAttachment.isInline = false; 127 | fileAttachment.id = "blabla"; 128 | 129 | DebugLogger.getInstance().writeLog(Level.INFO, "attachement id " + fileAttachment.id); 130 | attachment = mGraphServiceClient 131 | .me() 132 | .messages(messageId) 133 | .attachments() 134 | .buildRequest() 135 | .post(fileAttachment); 136 | } catch (Exception ex) { 137 | throw new SendMailException("Exception on add picture to draft message", ex); 138 | } 139 | 140 | return attachment; 141 | } 142 | 143 | /** 144 | * Sends a draft message to the specified recipients 145 | * 146 | * @param messageId String. The id of the message to send 147 | * @param callback Method invoked when operation is complete 148 | */ 149 | public void addAttachmentToDraftAsync( 150 | String messageId, 151 | Attachment attachment, 152 | ICallback callback) throws SendMailException { 153 | try { 154 | 155 | mGraphServiceClient 156 | .me() 157 | .messages(messageId).attachments(messageId).buildRequest() 158 | .post(attachment, callback); 159 | 160 | } catch (Exception ex) { 161 | throw new SendMailException("exception on send draft message ", ex); 162 | } 163 | } 164 | 165 | /** 166 | * Sends a draft message to the specified recipients 167 | * 168 | * @param messageId String. The id of the message to send 169 | */ 170 | public void sendDraftMessage( 171 | String messageId, 172 | int content_length) throws SendMailException { 173 | try { 174 | 175 | mGraphServiceClient 176 | .me() 177 | .mailFolders("Drafts") 178 | .messages(messageId) 179 | .send() 180 | .buildRequest() 181 | .post(); 182 | 183 | } catch (Exception ex) { 184 | throw new SendMailException("Exception on send draft mail", ex); 185 | } 186 | } 187 | 188 | /** 189 | * Gets a draft message by message id 190 | * 191 | * @param messageId 192 | */ 193 | public Message getDraftMessage(String messageId) throws SendMailException { 194 | Message draftMessage = null; 195 | try { 196 | draftMessage = mGraphServiceClient.me() 197 | .messages(messageId) 198 | .buildRequest() 199 | .get(); 200 | } catch (Exception ex) { 201 | throw new SendMailException("exception on get draft message ", ex); 202 | } 203 | 204 | return draftMessage; 205 | } 206 | 207 | 208 | /** 209 | * Gets the local user who is authenticated with the Microsoft Graph API endpoint 210 | * 211 | * @return 212 | * @throws SendMailException 213 | */ 214 | public User getUser() throws SendMailException { 215 | User user = null; 216 | try { 217 | user = mGraphServiceClient 218 | .me() 219 | .buildRequest() 220 | .get(); 221 | } catch (Exception ex) { 222 | throw new SendMailException("Exception on get me", ex); 223 | } 224 | 225 | return user; 226 | } 227 | 228 | /** 229 | * Gets the signed in user's profile picture from the Microsoft Graph 230 | * 231 | * @return Byte array of the user's profile picture 232 | * @throws SendMailException 233 | */ 234 | public byte[] getUserProfilePicture() throws SendMailException { 235 | InputStream photoStream = null; 236 | byte[] pictureBytes = new byte[1024]; 237 | try { 238 | photoStream = mGraphServiceClient 239 | .me() 240 | .photo() 241 | .content() 242 | .buildRequest() 243 | .get(); 244 | if (photoStream == null) { 245 | DebugLogger.getInstance().writeLog(Level.INFO, "no picture found "); 246 | pictureBytes = getDefaultPicture(); 247 | } 248 | else { 249 | pictureBytes = inputStreamToByteArray(photoStream); 250 | if (pictureBytes.length <= 0) { 251 | pictureBytes = getDefaultPicture(); 252 | } 253 | } 254 | } catch (Exception ex) { 255 | throw new SendMailException("Exception on get user profile photo", ex); 256 | } 257 | 258 | return pictureBytes; 259 | } 260 | 261 | 262 | /** 263 | * Converts an inputStream to a byte array. The input stream should be a stream of profile 264 | * picture bytes that comes in the response to a GET request on the Microsoft Graph API 265 | * 266 | * @param inputStream 267 | * @return 268 | */ 269 | private byte[] inputStreamToByteArray(InputStream inputStream) throws IOException { 270 | byte[] pictureBytes = null; 271 | try { 272 | BufferedInputStream bufferedInputStream = (BufferedInputStream) inputStream; 273 | byte[] buff = new byte[8000]; 274 | 275 | 276 | ByteArrayOutputStream bao = new ByteArrayOutputStream(); 277 | int bytesRead = 0; 278 | 279 | //This seems to be executing on the main thread!!! 280 | while ((bytesRead = bufferedInputStream.read(buff)) != -1) { 281 | bao.write(buff, 0, bytesRead); 282 | } 283 | pictureBytes = bao.toByteArray(); 284 | 285 | bao.close(); 286 | } catch (IOException ex) { 287 | DebugLogger.getInstance().writeLog(Level.SEVERE, 288 | "Attempting to read buffered network resource", 289 | ex); 290 | } 291 | 292 | return pictureBytes; 293 | } 294 | 295 | /** 296 | * Uploads a user picture as byte array to the user's OneDrive root folder 297 | * 298 | * @param picture byte[] picture byte array 299 | */ 300 | public DriveItem uploadPictureToOneDrive(byte[] picture) throws SendMailException { 301 | 302 | DriveItem driveItem = null; 303 | try { 304 | driveItem = mGraphServiceClient 305 | .me() 306 | .drive() 307 | .root() 308 | .itemWithPath("me2.png") 309 | .content() 310 | .buildRequest() 311 | .put(picture); 312 | } catch (Exception ex) { 313 | throw new SendMailException("exception on upload picture to OneDrive ", ex); 314 | } 315 | 316 | return driveItem; 317 | } 318 | 319 | /** 320 | * Requests OneDrive to create a public sharing link to a picture stored in OneDrive. 321 | * 322 | * @param id 323 | * @return Permission. The Permission object that exposes the requested sharing link 324 | * @throws SendMailException 325 | */ 326 | public Permission getPermissionSharingLink(String id) throws SendMailException { 327 | 328 | Permission permission = null; 329 | try { 330 | permission = mGraphServiceClient 331 | .me() 332 | .drive() 333 | .items(id) 334 | .createLink("view", "organization") 335 | .buildRequest() 336 | .post(); 337 | } catch (Exception ex) { 338 | throw new SendMailException("exception on get OneDrive sharing link ", ex); 339 | } 340 | 341 | return permission; 342 | } 343 | 344 | /** 345 | * Gets a picture from the device external storage root folder 346 | * 347 | * @return byte[] the default picture in a byte array 348 | */ 349 | private byte[] getDefaultPicture() throws SendMailException{ 350 | 351 | 352 | int bytesRead; 353 | byte[] bytes = new byte[1024]; 354 | 355 | File file = new File("/", Constants.DEFAULT_IMAGE_FILENAME); 356 | FileInputStream buf = null; 357 | if (file.exists() && file.canRead()) { 358 | int size = (int) file.length(); 359 | bytes = new byte[size]; 360 | try { 361 | buf = new FileInputStream(file); 362 | bytesRead = buf.read(bytes, 0, size); 363 | DebugLogger.getInstance().writeLog(Level.INFO, "Bytes read " + String.valueOf(bytesRead)); 364 | } catch (FileNotFoundException e) { 365 | throw new SendMailException("Could not find default picture file", e); 366 | 367 | } catch (IOException e) { 368 | throw new SendMailException("Could not open default picture file", e); 369 | } 370 | } 371 | return bytes; 372 | } 373 | 374 | 375 | @VisibleForTesting 376 | 377 | /** 378 | * Creates a new Message object 379 | */ 380 | private Message createMessage( 381 | String subject, 382 | String body, 383 | String address) { 384 | 385 | if (address == null || address.isEmpty()) { 386 | throw new IllegalArgumentException("The address parameter can't be null or empty."); 387 | } 388 | else { 389 | // perform a simple validation of the email address 390 | String addressParts[] = address.split("@"); 391 | if (addressParts.length != 2 || addressParts[0].length() == 0 || addressParts[1].indexOf('.') == -1) { 392 | throw new IllegalArgumentException( 393 | String.format("The address parameter must be a valid email address {0}", address) 394 | ); 395 | } 396 | } 397 | Message message = new Message(); 398 | EmailAddress emailAddress = new EmailAddress(); 399 | emailAddress.address = address; 400 | Recipient recipient = new Recipient(); 401 | recipient.emailAddress = emailAddress; 402 | message.toRecipients = Collections.singletonList(recipient); 403 | ItemBody itemBody = new ItemBody(); 404 | itemBody.content = body; 405 | itemBody.contentType = BodyType.HTML; 406 | message.body = itemBody; 407 | message.subject = subject; 408 | return message; 409 | } 410 | } -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/msgraph/SendMailException.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.graphsample.msgraph; 2 | 3 | public class SendMailException extends Exception { 4 | private static final long serialVersionUID = 1L; 5 | public SendMailException() { super(); } 6 | public SendMailException(String message) { super(message); } 7 | public SendMailException(String message, Throwable cause) { super(message, cause); } 8 | public SendMailException(Throwable cause) { super(cause); } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/microsoft/graphsample/msgraph/VisibleForTesting.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.graphsample.msgraph; 2 | 3 | public @interface VisibleForTesting { 4 | } 5 | --------------------------------------------------------------------------------