├── .coding-ci.yml ├── .dockerignore ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README-en.md ├── README.md ├── lib ├── annotations.jar ├── javac2.jar ├── jna-platform.jar ├── jna.jar ├── jsch-0.1.51.jar ├── jzlib-1.1.1.jar ├── pty4j-0.5.jar └── purejavacomm-0.0.17.jar ├── pom.xml └── src ├── main ├── java │ ├── com │ │ └── pty4j │ │ │ └── util │ │ │ └── PtyUtil.java │ └── net │ │ └── coding │ │ └── ide │ │ ├── Application.java │ │ ├── config │ │ ├── AspectConfig.java │ │ ├── Config.java │ │ ├── JpaConfig.java │ │ ├── SwaggerConfig.java │ │ ├── WebConfig.java │ │ └── WebSocketConfig.java │ │ ├── dto │ │ ├── BranchDTO.java │ │ ├── DiffDTO.java │ │ ├── DirDTO.java │ │ ├── FileDTO.java │ │ ├── FileSearchResultEntryDTO.java │ │ ├── KeyDTO.java │ │ ├── Package.java │ │ ├── ProjectDTO.java │ │ ├── UserDTO.java │ │ └── WorkspaceDTO.java │ │ ├── entity │ │ ├── BaseEntity.java │ │ ├── ConfigEntity.java │ │ ├── ProjectEntity.java │ │ └── WorkspaceEntity.java │ │ ├── event │ │ ├── FileChangeEvent.java │ │ ├── FileCreateEvent.java │ │ ├── FileDeleteEvent.java │ │ ├── FileModifyEvent.java │ │ ├── GitCheckoutEvent.java │ │ ├── WorkspaceDeleteEvent.java │ │ ├── WorkspaceOfflineEvent.java │ │ ├── WorkspaceOnlineEvent.java │ │ └── WorkspaceStatusEvent.java │ │ ├── git │ │ ├── Identity.java │ │ ├── KnownHosts.java │ │ ├── MultiUserSshSessionFactory.java │ │ ├── PrivateKeyCredentialsProvider.java │ │ └── rebase │ │ │ ├── EditActionHandler.java │ │ │ ├── RebaseActionHandler.java │ │ │ ├── RewordActionHandler.java │ │ │ └── SquashActionHandler.java │ │ ├── model │ │ ├── Branches.java │ │ ├── CheckoutResponse.java │ │ ├── CommitStatus.java │ │ ├── ConflictFile.java │ │ ├── DiffEntry.java │ │ ├── FileInfo.java │ │ ├── FileSearchResultEntry.java │ │ ├── GitBlame.java │ │ ├── GitLog.java │ │ ├── GitRef.java │ │ ├── GitStatus.java │ │ ├── HttpSessions.java │ │ ├── Key.java │ │ ├── ListStashResponse.java │ │ ├── MergeResponse.java │ │ ├── Message.java │ │ ├── PersonIdent.java │ │ ├── PushCommits.java │ │ ├── PushResponse.java │ │ ├── RebaseOperation.java │ │ ├── RebaseResponse.java │ │ ├── RepositoryState.java │ │ ├── ResetType.java │ │ ├── User.java │ │ ├── Workspace.java │ │ └── exception │ │ │ ├── GitCloneAuthFailException.java │ │ │ ├── GitCommitMessageNeedEditException.java │ │ │ ├── GitInvalidDiffException.java │ │ │ ├── GitInvalidPathException.java │ │ │ ├── GitInvalidRefException.java │ │ │ ├── GitOperationException.java │ │ │ ├── NotFoundException.java │ │ │ ├── ProjectTypeConstraintException.java │ │ │ ├── TransportProtocolUnsupportedException.java │ │ │ ├── WorkspaceCreationException.java │ │ │ ├── WorkspaceDeletedException.java │ │ │ ├── WorkspaceDeletingException.java │ │ │ ├── WorkspaceException.java │ │ │ ├── WorkspaceIOException.java │ │ │ ├── WorkspaceMaintainingException.java │ │ │ └── WorkspaceMissingException.java │ │ ├── repository │ │ ├── ConfigRepository.java │ │ ├── ProjectRepository.java │ │ └── WorkspaceRepository.java │ │ ├── service │ │ ├── BaseService.java │ │ ├── ConfigService.java │ │ ├── ConfigServiceImpl.java │ │ ├── GitManager.java │ │ ├── GitManagerImpl.java │ │ ├── KeyManager.java │ │ ├── KeyManagerImpl.java │ │ ├── PackageService.java │ │ ├── PackageServiceImpl.java │ │ ├── ProjectService.java │ │ ├── ProjectServiceImpl.java │ │ ├── WatchedPathStore.java │ │ ├── WorkspaceManager.java │ │ ├── WorkspaceManagerImpl.java │ │ └── WorkspaceWatcher.java │ │ ├── tty │ │ ├── ProcessTtyConnector.java │ │ ├── SocketIOHandler.java │ │ ├── TerminalEmulator.java │ │ └── TtyConnector.java │ │ ├── utils │ │ ├── AsyncResult.java │ │ ├── Callback.java │ │ ├── CookieStoreHolder.java │ │ ├── Debouncer.java │ │ ├── FileUtil.java │ │ ├── HostMatcher.java │ │ ├── KeyUtils.java │ │ ├── ProjectUtil.java │ │ ├── RandomGenerator.java │ │ ├── RebaseStateUtils.java │ │ ├── RebaseTodoUtils.java │ │ ├── ShortKeyGenerator.java │ │ ├── TemporaryFileFilter.java │ │ └── WildcardMatcher.java │ │ └── web │ │ ├── controller │ │ ├── ExceptionAdvice.java │ │ ├── GitController.java │ │ ├── PackageController.java │ │ ├── ProjectController.java │ │ ├── UserController.java │ │ ├── WorkspaceAdvice.java │ │ └── WorkspaceController.java │ │ ├── mapping │ │ └── PersonIdentConverter.java │ │ └── message │ │ ├── EventExchange.java │ │ ├── GsonMessageConverter.java │ │ ├── OnlineWorkspaceStore.java │ │ ├── SessionCacheWebSocketHandler.java │ │ ├── SpaceKeyHandshakeInterceptor.java │ │ ├── SpringfoxJsonToGsonAdapter.java │ │ └── WebSocketSessionStore.java └── resources │ ├── META-INF │ └── mime.types │ ├── application.properties │ ├── banner.txt │ ├── lib │ ├── linux │ │ ├── x86 │ │ │ └── libpty.so │ │ └── x86_64 │ │ │ └── libpty.so │ ├── macosx │ │ ├── x86 │ │ │ └── libpty.dylib │ │ └── x86_64 │ │ │ └── libpty.dylib │ └── win │ │ └── x86 │ │ ├── libwinpty.dll │ │ └── winpty-agent.exe │ ├── logback-spring.xml │ └── logback.xml └── test ├── java └── net │ └── coding │ └── ide │ ├── TestProperty.java │ ├── annotation │ └── RepositoryTest.java │ ├── config │ └── TestPropConfig.java │ ├── model │ └── WorkspaceTest.java │ ├── repository │ ├── ProjectRepositoryTest.java │ └── WorkspaceRepositoryTest.java │ ├── service │ ├── BaseServiceTest.java │ ├── GitManagerTest.java │ ├── PackageServiceImplTest.java │ └── WorkspaceManagerTest.java │ └── utils │ ├── DataPopulator.java │ ├── FilesUtils.java │ └── RepositoryHelper.java └── resources ├── application-test.properties ├── db └── workspace.xml └── workspace └── upload.txt /.coding-ci.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk7 5 | 6 | script: mvn test 7 | 8 | after_failure: cp ./target/surefile_reports $CODING_ARCHIVE_DIR/ 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | ** 2 | !src/main/resources/lib/ 3 | !target/ide-backend.jar 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | *.orig 4 | target/ 5 | **/*.env 6 | *.db 7 | *.ipr 8 | *.iws 9 | qwerty/ 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: java 3 | jdk: 4 | - oraclejdk8 5 | 6 | cache: 7 | directories: 8 | - "$HOME/.m2" 9 | 10 | env: 11 | global: 12 | - COMMIT=${TRAVIS_COMMIT::8} 13 | - DOCKER_CACHE_FILE=${HOME}/docker/cache.tar.gz 14 | - REPO=webide/backend 15 | - secure: qL8NHgOsEC6KgNUVWjeNqj1YBYUnKsZ+j14098jConbNqVeXOOvjeihjP85mCULo/ory0RjuvSMbDpqUpkgfNRE+NgrAcnlPY36Y08FXWn0BDZphHQhRTu8Jbp/AIYWS0mhI0ZEPmAEyIJsZu3nggEL2XtthK2aCQGJjVMcBJBxo2xZfhN+hrFj0u8kJUpIQNxTbS5+rCQYjAekch5EkjDzUQEoSPerQ9Vb4DcJ5S/YSA/Z3FfFhEwUuh7cgqZOko9GEJOEbIHTXG2ZqyghlTzW26bJPbQsitgAZWTMmn70IBFXQakamhC/6uFxJZOK0EO1Lh33MDqKN6qGhJVLWMYJ9M7RD3cQzj9dpElwsLRgcj1shTGmrh1DzTIc2rNNT5EV5Kfw6KwPhk4CbDj4CP0fZRBPuvaNg/KcR2DS130unxi+PBqasslfqrE7Pvahayjz9OCxa/tMblX+SR5m4CnY4P4IzOyTxLOjN68S5D2oCTmV9W/iOoXF1kUm7pe6fFKWTWH/cI2H3dh6DFtQBR39y2zRfTryuGLGmhbNz0fN1323r80scYEplZQ5tsdkvLxIBXkn2+Cvgmek5nl10xkUD1/DU52hhI9cFC9K8Cj74KTJ4Us38157OR6zCC8Es44zpBpDzyodYJ6/zkBLMIr+MWN4FzJcY2EteKEGliYo= 16 | - secure: V4CHmKz/DbPeUkXI7zng/vwpMP/EnDV/s60fHd+g7/G15WADX/OaZj+PE6lQEaGzvKwdmjjisnFHPrpOpegJA80AGjhj2f/x+WcpcCw4k0ct7tFHrXylgzols4n04jlMBcvyxgKrk0pbyKP/zAUe5UF3YZ8cjjNjOlDZDbvpaNphHmcQn3NkrW3z4CcwemJqG2l4+di3enXb2/VETn/WRJTvk1epXpZ2jCk3+2xQG7LkxgAvZTm2vK8YxQ7rXFqzoas2nYT3YFSBktU+B6rU2gtiH0R0FaLhuNKEukJo4EgdPUNXZebDzKbKmzgSBFw5O3sR6eoMdQhMVrO4dZuwGcUAdSd8VLFBG6I+7ulRiafik2RcFhnft2Vy+x5pdroToGyt9Dc8Q4bpMgGAAn4q7orm0Om4r3UgKCwktboMYbFLrlMTflp7tRCHXYunoh7YQEpNZYGT8typ0wRu/D4MHIf7m0wcT8jceYMo43GVqN/yVPycIHwgslR/SyPJdBrChk4scunXKOOmxo8tn9KnYnuA4JHiZC3J3mQ/Rjnp6Glw37IX2M7TKcwk47ve24FU84pgoXKE/4gnsmNEPborEMPw4arN8MlO/3wDoIFv7iBClx64hqZ/GPPGqx2cXzMqGJMxl90/XJRnbzVm01HjmobRJ71YAqnJDTBEkY/wUqU= 17 | 18 | before_install: 19 | - sudo apt-get update 20 | - sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce 21 | - if [ -f ${DOCKER_CACHE_FILE} ]; then gunzip -c ${DOCKER_CACHE_FILE} | docker load; 22 | fi 23 | 24 | install: true 25 | 26 | script: 27 | - set -e 28 | - mvn -Dskip-webjars=true package 29 | - docker build -f Dockerfile -t $REPO:$COMMIT . 30 | - if [ "$TRAVIS_BRANCH" == "master" ]; then docker tag $REPO:$COMMIT $REPO:latest; fi 31 | - docker login -u $DOCKER_USER -p $DOCKER_PASS 32 | - docker push $REPO 33 | 34 | before_cache: 35 | - set -e 36 | - mkdir -p $(dirname ${DOCKER_CACHE_FILE}) 37 | - docker save $(docker history -q $REPO:$COMMIT | grep -v '') | gzip > ${DOCKER_CACHE_FILE} 38 | 39 | notifications: 40 | slack: 41 | on_failure: always 42 | on_success: change 43 | rooms: 44 | secure: Oe/Ym9+gt7hTlwuCGv/RjBO/z0BnBHenL9J+E6yOevaPjaf3SGaHeOdv0buYy8PdmejoxyxMtyRTR5eLPgDbw+TSMJ0OPRbr3F9yez9laT8i9esWEH4EYkQdQNEniBiBFuDd70ZCYYd0q70Qbx3WbMQ0YxoRjT18fNY5A9dv4MFe1yPXPE6zcSKzRAiQAWgBjir5I6qJmL1AjmQAvEBy64yO+jkWbUEEtS9kBPJFSBKpRFp/AEjHMrut00c7FT+Z+8WKiozqK5/I6voHckdf6bC5Y6DWG7mIdw8BDfoxpzefwrCZY8X6KGnmWZmPb8FXxrs9qgdPx7shFnYPxsWwn81H1pk5aGALMcAg+pyK99NNgHCyV5cNIWrw6cSvXiLsDYsTFBUSJ/AWwe1JlgP8tl2HhGEkqBSPy/8dWcIAqP6i1mXAz78hgbU/LAlD3zsa+jc0KlMYPbMceUjgjArucS5yvOnBUKCmAzIfWVvAYXjx0avgOwUqoNmLFk3AP1UHHhfEX0Lz7PpjnvkLmGum6lxQlognEhiy9oIXRsnF++ztSGKemHfGHWyCqOBbgKdZuGSb0JIAbWEupJNG6/zRCYyz0L6wvZlzPre/FVgdxZpCjL4+q5kY8P/zXeCJX/cdM1ZvvlymM4uS8lnMUhEwlrjGGba19660A9eWLXFkj+c= 45 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM java:8-jre-alpine 2 | 3 | EXPOSE 8080 4 | 5 | RUN set -ex && \ 6 | if [ $(wget -qO- ipinfo.io/country) == CN ]; then echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories ;fi && \ 7 | apk update && \ 8 | apk add --no-cache zsh git 9 | 10 | # Install oh-my-zsh 11 | RUN git clone --depth=1 git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh \ 12 | && cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc 13 | 14 | ENV SHELL /bin/zsh 15 | 16 | ADD target/ide-backend.jar /root 17 | ADD src/main/resources/lib /root/lib 18 | 19 | WORKDIR /root 20 | CMD ["java", "-jar", "ide-backend.jar", "--PTY_LIB_FOLDER=/root/lib"] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 CODING(https://coding.net/). 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /README-en.md: -------------------------------------------------------------------------------- 1 | # Coding WebIDE Backend 2 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://github.com/Coding/WebIDE/blob/master/LICENSE) [![Build Status](https://travis-ci.org/Coding/WebIDE-Backend.svg?branch=master)](https://travis-ci.org/Coding/WebIDE-Backend) [![Docker Stars](https://img.shields.io/docker/stars/webide/backend.svg)](https://hub.docker.com/r/webide/backend 'DockerHub') [![Docker Pulls](https://img.shields.io/docker/pulls/webide/backend.svg)](https://hub.docker.com/r/webide/backend 'DockerHub') 3 | 4 | This repo is the backend of **Coding [WebIDE](https://ide.coding.net) Community Edition** 5 | 6 | ## Dev Environment 7 | 8 | The backend is written in Java, and uses Maven as build tool. We recommend IntelliJ IDEA or Eclipse as IDE. 9 | 10 | ## Configuration 11 | 12 | Everything in `/src/main/resources/application.properties`, including user info, project and database configs, etc. 13 | 14 | ## Packages 15 | 16 | > **config:** Config classes 17 | > **dto:** dto classes 18 | > **entity:** Entity classes 19 | > **event:** Event-related classes 20 | > **git:** Classes that extend jgit class 21 | > **model:** Data model/structure 22 | > **repository:** Repository class, for database query purpose 23 | > **service:** Service classes 24 | > **tty:** A Java implementation of terminal 25 | > **utils:** Utils classes 26 | > **web:** Web-related classes, controllers etc. 27 | 28 | ## Run the project 29 | 30 | The backend first packs the frontend to webjar, and import as a maven dependency, thus packing the frontend is required before run. 31 | 32 | Please refer to the script in `WebIDE-Workspace` repo, which provides a command to compile and run the whole project. 33 | 34 | If you want to pack the frontend manually, please refer to `WebIDE-Frontend-Webjars` repo. 35 | 36 | Once done packing, start the poject with `mvn spring-boot:run`. 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coding WebIDE Backend 2 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://github.com/Coding/WebIDE/blob/master/LICENSE) [![Build Status](https://travis-ci.org/Coding/WebIDE-Backend.svg?branch=master)](https://travis-ci.org/Coding/WebIDE-Backend) [![Docker Stars](https://img.shields.io/docker/stars/webide/backend.svg)](https://hub.docker.com/r/webide/backend 'DockerHub') [![Docker Pulls](https://img.shields.io/docker/pulls/webide/backend.svg)](https://hub.docker.com/r/webide/backend 'DockerHub') 3 | 4 | 本项目是开源版 [WebIDE](https://ide.coding.net) 的后端程序。 5 | 6 | ## 开发环境 7 | 8 | 本项目使用 Maven 作为构建系统, 开发 IDE 可以为 Intellj IDEA 或 Eclipse, 只需要在按照 maven 类型导入即可。 9 | 10 | ## 配置文件 11 | 12 | * `/src/main/resources/application.properties` 包括用户、项目、数据库等配置 13 | 14 | > **SPACE_HOME:** 存放 workspace 的目录,默认为 ${"user.home"}/.workspace 15 | > **server.port:** 应用启动的端口 16 | 17 | ## 包说明 18 | 19 | > **config:** 项目配置类 20 | > **dto:** dto 类 21 | > **entity:** 实体类 22 | > **event:** 与事件相关的类 23 | > **git:** 扩展 jgit 的类 24 | > **model:** 一些数据结构 25 | > **repository:** repository 类, 用于数据库查询 26 | > **service:** 服务类 27 | > **tty:** terminal 的 java 实现 28 | > **utils:** 工具类 29 | > **web:** 与 web 相关的类, 比如 controller 等 30 | 31 | ## 项目运行 32 | 33 | 本项目将 [WebIDE-Frontend](https://github.com/Coding/WebIDE-Frontend) 前端打包成 `webjar`, 作为 maven 依赖引入, 因此在运行本项目前需要先打包前端项目。 34 | 35 | 可以参见 [WebIDE](https://github.com/Coding/WebIDE) 项目的[脚本](https://github.com/Coding/WebIDE/blob/master/ide.sh), 该脚本提供了一键编译、运行 `WebIDE` 的功能)。 36 | 37 | 如果想手动打包前端, 可以参见 `WebIDE-Frontend-Webjars` 项目。 38 | 39 | 打包完成后, 使用 `mvn spring-boot:run` 启动项目即可。 40 | -------------------------------------------------------------------------------- /lib/annotations.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/annotations.jar -------------------------------------------------------------------------------- /lib/javac2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/javac2.jar -------------------------------------------------------------------------------- /lib/jna-platform.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/jna-platform.jar -------------------------------------------------------------------------------- /lib/jna.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/jna.jar -------------------------------------------------------------------------------- /lib/jsch-0.1.51.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/jsch-0.1.51.jar -------------------------------------------------------------------------------- /lib/jzlib-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/jzlib-1.1.1.jar -------------------------------------------------------------------------------- /lib/pty4j-0.5.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/pty4j-0.5.jar -------------------------------------------------------------------------------- /lib/purejavacomm-0.0.17.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/lib/purejavacomm-0.0.17.jar -------------------------------------------------------------------------------- /src/main/java/com/pty4j/util/PtyUtil.java: -------------------------------------------------------------------------------- 1 | package com.pty4j.util; 2 | 3 | import com.google.common.base.Function; 4 | import com.google.common.collect.Lists; 5 | import com.pty4j.windows.WinPty; 6 | import com.sun.jna.Platform; 7 | 8 | import java.io.File; 9 | import java.net.URI; 10 | import java.security.CodeSource; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * @author traff 16 | */ 17 | public class PtyUtil { 18 | public static final String OS_VERSION = System.getProperty("os.version").toLowerCase(); 19 | 20 | private final static String PTY_LIB_FOLDER = System.getenv("PTY_LIB_FOLDER") != null ? System.getenv("PTY_LIB_FOLDER") : System.getProperty("PTY_LIB_FOLDER"); 21 | 22 | public static String[] toStringArray(Map environment) { 23 | if (environment == null) return new String[0]; 24 | List list = Lists.transform(Lists.newArrayList(environment.entrySet()), new Function, String>() { 25 | public String apply(Map.Entry entry) { 26 | return entry.getKey() + "=" + entry.getValue(); 27 | } 28 | }); 29 | return list.toArray(new String[list.size()]); 30 | } 31 | 32 | /** 33 | * Returns the folder that contains a jar that contains the class 34 | * 35 | * @param aclass a class to find a jar 36 | * @return 37 | */ 38 | public static String getJarContainingFolderPath(Class aclass) throws Exception { 39 | CodeSource codeSource = aclass.getProtectionDomain().getCodeSource(); 40 | 41 | File jarFile; 42 | 43 | if (codeSource.getLocation() != null) { 44 | jarFile = new File(codeSource.getLocation().toURI()); 45 | } else { 46 | String path = aclass.getResource(aclass.getSimpleName() + ".class").getPath(); 47 | 48 | int startIndex = path.indexOf(":") + 1; 49 | int endIndex = path.indexOf("!"); 50 | if (startIndex == -1 || endIndex == -1) { 51 | throw new IllegalStateException("Class " + aclass.getSimpleName() + " is located not within a jar: " + path); 52 | } 53 | String jarFilePath = path.substring(startIndex, endIndex); 54 | jarFilePath = new URI(jarFilePath).getPath(); 55 | jarFile = new File(jarFilePath); 56 | } 57 | return jarFile.getParentFile().getAbsolutePath(); 58 | } 59 | 60 | public static String getPtyLibFolderPath() throws Exception { 61 | if (PTY_LIB_FOLDER != null) { 62 | return PTY_LIB_FOLDER; 63 | } 64 | //Class aclass = WinPty.class.getClassLoader().loadClass("com.jediterm.pty.PtyMain"); 65 | Class aclass = WinPty.class; 66 | 67 | return getJarContainingFolderPath(aclass); 68 | } 69 | 70 | public static File resolveNativeLibrary() throws Exception { 71 | String libFolderPath = getPtyLibFolderPath(); 72 | 73 | if (libFolderPath != null) { 74 | 75 | File libFolder = new File(libFolderPath); 76 | File lib = resolveNativeLibrary(libFolder); 77 | 78 | lib = lib.exists() ? lib : resolveNativeLibrary(new File(libFolder, "libpty")); 79 | 80 | if (!lib.exists()) { 81 | throw new IllegalStateException(String.format("Couldn't find %s, jar folder %s", lib.getName(), 82 | libFolder.getAbsolutePath())); 83 | } 84 | 85 | return lib; 86 | } else { 87 | throw new IllegalStateException("Couldn't detect lib folder"); 88 | } 89 | } 90 | 91 | public static File resolveNativeLibrary(File parent) { 92 | return resolveNativeFile(parent, getNativeLibraryName()); 93 | } 94 | 95 | public static File resolveNativeFile(String fileName) throws Exception { 96 | File libFolder = new File(getPtyLibFolderPath()); 97 | File file = resolveNativeFile(libFolder, fileName); 98 | return file.exists() ? file : resolveNativeFile(new File(libFolder, "libpty"), fileName); 99 | } 100 | 101 | public static File resolveNativeFile(File parent, String fileName) { 102 | final File path = new File(parent, getPlatformFolder()); 103 | 104 | String arch = Platform.is64Bit() ? "x86_64" : "x86"; 105 | String prefix = isWinXp() ? "xp" : arch; 106 | 107 | if (new File(parent, prefix).exists()) { 108 | return new File(new File(parent, prefix), fileName); 109 | } else { 110 | return new File(new File(path, prefix), fileName); 111 | } 112 | } 113 | 114 | private static String getPlatformFolder() { 115 | String result; 116 | 117 | if (Platform.isMac()) { 118 | result = "macosx"; 119 | } else if (Platform.isWindows()) { 120 | result = "win"; 121 | } else if (Platform.isLinux()) { 122 | result = "linux"; 123 | } else if (Platform.isFreeBSD()) { 124 | result = "freebsd"; 125 | } else if (Platform.isOpenBSD()) { 126 | result = "openbsd"; 127 | } else { 128 | throw new IllegalStateException("Platform " + Platform.getOSType() + " is not supported"); 129 | } 130 | 131 | return result; 132 | } 133 | 134 | private static String getNativeLibraryName() { 135 | String result; 136 | 137 | if (Platform.isMac()) { 138 | result = "libpty.dylib"; 139 | } else if (Platform.isWindows()) { 140 | result = "winpty.dll"; 141 | } else if (Platform.isLinux() || Platform.isFreeBSD() || Platform.isOpenBSD()) { 142 | result = "libpty.so"; 143 | } else { 144 | throw new IllegalStateException("Platform " + Platform.getOSType() + " is not supported"); 145 | } 146 | 147 | return result; 148 | } 149 | 150 | public static boolean isWinXp() { 151 | return Platform.isWindows() && (OS_VERSION.equals("5.1") || OS_VERSION.equals("5.2")); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide; 6 | 7 | import org.apache.commons.io.FileUtils; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.boot.SpringApplication; 10 | import org.springframework.boot.autoconfigure.SpringBootApplication; 11 | 12 | import javax.annotation.PostConstruct; 13 | import java.io.File; 14 | import java.io.IOException; 15 | import java.net.URISyntaxException; 16 | import java.nio.file.Files; 17 | 18 | /** 19 | * Created by vangie on 16/1/18. 20 | */ 21 | @SpringBootApplication 22 | public class Application { 23 | 24 | @Value("${PTY_LIB_FOLDER}") 25 | private String ptyLibFolder; 26 | 27 | public static void main(String[] args) throws URISyntaxException { 28 | SpringApplication.run(Application.class, args); 29 | } 30 | 31 | @PostConstruct 32 | public void init() throws IOException { 33 | System.setProperty("PTY_LIB_FOLDER", ptyLibFolder); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/config/AspectConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.config; 6 | 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 9 | 10 | /** 11 | * Created by mingshun on 15-11-2. 12 | */ 13 | @Configuration 14 | @EnableAspectJAutoProxy 15 | public class AspectConfig { 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/config/Config.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.config; 6 | 7 | import net.coding.ide.Application; 8 | import net.coding.ide.git.MultiUserSshSessionFactory; 9 | import org.eclipse.jgit.transport.SshSessionFactory; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.ComponentScan; 12 | import org.springframework.context.annotation.ComponentScan.Filter; 13 | import org.springframework.context.annotation.Configuration; 14 | import org.springframework.context.annotation.FilterType; 15 | import org.springframework.core.type.filter.RegexPatternTypeFilter; 16 | import org.springframework.stereotype.Controller; 17 | import org.springframework.stereotype.Repository; 18 | import org.springframework.web.context.request.RequestContextListener; 19 | 20 | import java.util.regex.Pattern; 21 | 22 | /** 23 | * Created by vangie on 14/11/12. 24 | */ 25 | 26 | @Configuration 27 | @ComponentScan(basePackages = "net.coding.ide", excludeFilters = { 28 | @Filter(type = FilterType.ANNOTATION, value = Controller.class), 29 | @Filter(type = FilterType.ANNOTATION, value = Repository.class), 30 | @Filter(type = FilterType.CUSTOM, value = Config.WebBeanFilter.class), 31 | @Filter(type = FilterType.CUSTOM, value = Config.ConfigBeanFilter.class), 32 | @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Application.class) 33 | }) 34 | public class Config { 35 | 36 | static class WebBeanFilter extends RegexPatternTypeFilter { 37 | public WebBeanFilter() { 38 | super(Pattern.compile("net\\.coding\\.ide\\.web\\..*")); 39 | } 40 | } 41 | 42 | static class ConfigBeanFilter extends RegexPatternTypeFilter { 43 | public ConfigBeanFilter() { 44 | super(Pattern.compile("net\\.coding\\.ide\\.config\\..*")); 45 | } 46 | } 47 | 48 | static { 49 | SshSessionFactory.setInstance(new MultiUserSshSessionFactory()); 50 | } 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/config/JpaConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.config; 6 | 7 | import com.zaxxer.hikari.HikariDataSource; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.boot.autoconfigure.domain.EntityScan; 10 | import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 14 | import org.springframework.data.jpa.repository.config.EnableJpaRepositories; 15 | import org.springframework.transaction.annotation.EnableTransactionManagement; 16 | 17 | import javax.annotation.PostConstruct; 18 | import javax.sql.DataSource; 19 | import java.io.File; 20 | import java.io.IOException; 21 | import java.nio.file.Files; 22 | 23 | /** 24 | * Created by vangie on 14/12/4. 25 | */ 26 | @Configuration 27 | @EnableJpaAuditing 28 | @EnableJpaRepositories("net.coding.ide.repository") 29 | @EntityScan("net.coding.ide.entity") 30 | @EnableTransactionManagement 31 | public class JpaConfig { 32 | 33 | /** 34 | * ConfigurationProperties not support spel 35 | * @see > converters) { 39 | converters.add(new ResourceHttpMessageConverter()); 40 | 41 | GsonHttpMessageConverter converter = new GsonHttpMessageConverter(); 42 | 43 | converter.setGson(gson()); 44 | converters.add(converter); 45 | } 46 | 47 | @Bean 48 | public ModelMapper modelMapper(List converters) { 49 | ModelMapper mapper = new ModelMapper(); 50 | 51 | converters.stream().forEach(mapper::addConverter); 52 | 53 | return mapper; 54 | } 55 | 56 | private Gson gson() { 57 | final GsonBuilder builder = new GsonBuilder(); 58 | // support swagger json 59 | builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()); 60 | 61 | return Converters.registerDateTime(builder).create(); 62 | } 63 | 64 | @Override 65 | public void addViewControllers(ViewControllerRegistry registry) { 66 | super.addViewControllers(registry); 67 | registry.addViewController("/ws/**").setViewName("forward:/workspace.html"); 68 | } 69 | 70 | @Override 71 | public void addCorsMappings(CorsRegistry registry) { 72 | registry.addMapping("/**") 73 | .allowedOrigins(allowedOrigins) 74 | .allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE") 75 | .maxAge(3600) 76 | .allowedHeaders("X-Requested-With", "X-Credentials", "X-Sharding-Group", "X-Space-Key", "content-type") 77 | .exposedHeaders("Requests-Auth", "Requests-Auth-Url", "Requests-Auth-Return-Url") 78 | .allowCredentials(true); 79 | } 80 | 81 | @Override 82 | public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { 83 | configurer.favorPathExtension(true); 84 | } 85 | 86 | @Override 87 | public void configurePathMatch(PathMatchConfigurer configurer) { 88 | configurer.setUseSuffixPatternMatch(false); 89 | configurer.setUseRegisteredSuffixPatternMatch(false); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/config/WebSocketConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.config; 6 | 7 | import com.fatboyindustrial.gsonjodatime.Converters; 8 | import com.google.gson.GsonBuilder; 9 | import net.coding.ide.web.message.GsonMessageConverter; 10 | import net.coding.ide.web.message.SessionCacheWebSocketHandler; 11 | import net.coding.ide.web.message.SpaceKeyHandshakeInterceptor; 12 | import net.coding.ide.web.message.WebSocketSessionStore; 13 | import org.atmosphere.cpr.AtmosphereServlet; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.beans.factory.annotation.Value; 16 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 17 | import org.springframework.context.annotation.Bean; 18 | import org.springframework.context.annotation.Configuration; 19 | import org.springframework.messaging.converter.ByteArrayMessageConverter; 20 | import org.springframework.messaging.converter.MessageConverter; 21 | import org.springframework.messaging.converter.StringMessageConverter; 22 | import org.springframework.messaging.simp.config.MessageBrokerRegistry; 23 | import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; 24 | import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; 25 | import org.springframework.web.socket.config.annotation.StompEndpointRegistry; 26 | import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration; 27 | import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; 28 | 29 | import java.util.List; 30 | 31 | /** 32 | * Created by vangie on 15/1/30. 33 | */ 34 | @Configuration 35 | @EnableWebSocketMessageBroker 36 | public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { 37 | 38 | @Value("#{'${ALLOWED_ORIGINS}'.split(',')}") 39 | private String[] allowedOrigins; 40 | 41 | @Value("${SPACE_HOME}") 42 | private String spaceHome; 43 | 44 | @Autowired 45 | private WebSocketSessionStore webSocketSessionStore; 46 | 47 | @Override 48 | public void registerStompEndpoints(StompEndpointRegistry registry) { 49 | registry.addEndpoint("/sockjs") 50 | .addInterceptors(new SpaceKeyHandshakeInterceptor()) 51 | .setAllowedOrigins(allowedOrigins) 52 | .withSockJS(); 53 | } 54 | 55 | @Override 56 | public void configureWebSocketTransport(WebSocketTransportRegistration registration) { 57 | registration.addDecoratorFactory(handler -> new SessionCacheWebSocketHandler(handler, webSocketSessionStore)); 58 | } 59 | 60 | @Override 61 | public void configureMessageBroker(MessageBrokerRegistry config) { 62 | config.setApplicationDestinationPrefixes("/app") 63 | .enableSimpleBroker("/queue", "/topic"); 64 | } 65 | 66 | @Override 67 | public boolean configureMessageConverters(List messageConverters) { 68 | messageConverters.add(new StringMessageConverter()); 69 | messageConverters.add(new ByteArrayMessageConverter()); 70 | 71 | GsonMessageConverter gsonMessageConverter = new GsonMessageConverter(); 72 | gsonMessageConverter.setGson(Converters.registerDateTime(new GsonBuilder()).create()); 73 | messageConverters.add(gsonMessageConverter); 74 | 75 | return false; 76 | } 77 | 78 | @Bean // for socket.io 79 | public ServletRegistrationBean servletRegistrationBean() { 80 | ServletRegistrationBean bean = new ServletRegistrationBean(new AtmosphereServlet(), "/coding-ide-tty1/*"); 81 | 82 | bean.addInitParameter("socketio-transport", "websocket"); 83 | bean.addInitParameter("socketio-timeout", "25000"); 84 | bean.addInitParameter("socketio-heartbeat", "15000"); 85 | bean.addInitParameter("socketio-suspendTime", "30000"); 86 | bean.addInitParameter("org.atmosphere.cpr.sessionSupport", "true"); 87 | bean.addInitParameter("SPACE_HOME", spaceHome); 88 | bean.setLoadOnStartup(100); 89 | bean.setAsyncSupported(true); 90 | 91 | return bean; 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/BranchDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | import lombok.NonNull; 9 | import lombok.RequiredArgsConstructor; 10 | 11 | /** 12 | * Created by tan on 16/8/23. 13 | */ 14 | @Data 15 | @RequiredArgsConstructor(staticName = "of") 16 | public class BranchDTO { 17 | 18 | @NonNull 19 | private String name; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/DiffDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | import lombok.NonNull; 9 | import lombok.RequiredArgsConstructor; 10 | 11 | /** 12 | * Created by tan on 16/8/23. 13 | */ 14 | @Data 15 | @RequiredArgsConstructor(staticName = "of") 16 | public class DiffDTO { 17 | 18 | @NonNull 19 | private String diff; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/DirDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | import lombok.NonNull; 9 | 10 | /** 11 | * Created by tan on 16/8/23. 12 | */ 13 | @Data(staticConstructor = "of") 14 | public class DirDTO { 15 | 16 | @NonNull 17 | private String spaceKey; 18 | 19 | @NonNull 20 | private String path; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/FileDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | import lombok.NonNull; 9 | import lombok.RequiredArgsConstructor; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | 12 | /** 13 | * Created by tan on 16/8/23. 14 | */ 15 | @Data 16 | @RequiredArgsConstructor(staticName = "of") 17 | public class FileDTO { 18 | 19 | @NonNull 20 | private String path; 21 | 22 | private String content; 23 | 24 | private Boolean base64; 25 | 26 | private Long lastModified; 27 | 28 | private String encoding; 29 | 30 | private FileDTO(String path, String content, String encoding, Boolean base64, Long lastModified) { 31 | this.path = path; 32 | this.content = content; 33 | this.base64 = base64; 34 | this.lastModified = lastModified; 35 | this.encoding = encoding; 36 | } 37 | 38 | public static FileDTO of(String path, String content, String encoding, Boolean base64, Long lastModified) { 39 | return new FileDTO(path, content, encoding, base64, lastModified); 40 | } 41 | 42 | public static FileDTO of(String path, String content, Boolean base64) { 43 | return new FileDTO(path, content, null, base64, null); 44 | } 45 | 46 | public static FileDTO of(String path, String content, String encoding, Boolean base64) { 47 | return new FileDTO(path, content, encoding, base64, null); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/FileSearchResultEntryDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by tan on 16/8/23. 11 | */ 12 | @Data 13 | public class FileSearchResultEntryDTO { 14 | private String path; 15 | 16 | private String contentType; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/KeyDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by tan on 16/8/23. 11 | */ 12 | @Data 13 | public class KeyDTO { 14 | private String publicKey; 15 | 16 | private String fingerprint; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/Package.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Created by tan on 09/03/2017. 7 | */ 8 | @Data 9 | public class Package { 10 | 11 | public enum Requirement { 12 | Optional, Required 13 | } 14 | 15 | private String name; 16 | 17 | private String version; 18 | 19 | private String description; 20 | 21 | private String author; 22 | 23 | private String displayName; 24 | 25 | private Requirement requirement; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/ProjectDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by tan on 16/8/23. 11 | */ 12 | @Data 13 | public class ProjectDTO { 14 | private Long id; 15 | 16 | private String name; 17 | 18 | private String fullName; 19 | 20 | private String iconUrl; 21 | 22 | private String gitUrl; 23 | 24 | private String sshUrl; 25 | 26 | private String httpsUrl; 27 | 28 | private String htmlUrl; 29 | 30 | private String ownerName; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | import lombok.NonNull; 9 | import lombok.RequiredArgsConstructor; 10 | 11 | /** 12 | * Created by tan on 16/8/23. 13 | */ 14 | @Data 15 | @RequiredArgsConstructor(staticName = "of") 16 | public class UserDTO { 17 | 18 | @NonNull 19 | private String username; 20 | 21 | @NonNull 22 | private String avatar; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/dto/WorkspaceDTO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.dto; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by tan on 16/8/23. 11 | */ 12 | @Data 13 | public class WorkspaceDTO { 14 | private String spaceKey; 15 | 16 | private String ownerName; 17 | 18 | private String projectName; 19 | 20 | private String projectIconUrl; 21 | 22 | private String projectHtmlUrl; 23 | 24 | private String encoding; 25 | 26 | private String workingStatus; 27 | 28 | private String description; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/entity/BaseEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.entity; 6 | 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import lombok.Setter; 10 | import lombok.ToString; 11 | import org.joda.time.DateTime; 12 | import org.springframework.data.annotation.CreatedDate; 13 | import org.springframework.data.annotation.LastModifiedDate; 14 | import org.springframework.data.domain.Persistable; 15 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 16 | 17 | import javax.persistence.*; 18 | import java.util.Date; 19 | 20 | /** 21 | * Created by vangie on 14/12/4. 22 | */ 23 | @ToString 24 | @EqualsAndHashCode 25 | @MappedSuperclass 26 | @EntityListeners({AuditingEntityListener.class}) 27 | public class BaseEntity implements Persistable { 28 | 29 | @Getter 30 | @Setter 31 | @Id 32 | @Column(name = "F_ID") 33 | @GeneratedValue(strategy = GenerationType.AUTO) 34 | private Long id; 35 | 36 | @Column(name = "F_CREATED_AT") 37 | @CreatedDate 38 | @Temporal(TemporalType.TIMESTAMP) 39 | private Date createdDate; 40 | 41 | @Column(name = "F_LAST_MODIFIED_AT") 42 | @LastModifiedDate 43 | @Temporal(TemporalType.TIMESTAMP) 44 | private Date lastModifiedDate; 45 | 46 | @Getter 47 | @Setter 48 | @Version 49 | @Column(name = "F_VERSION") 50 | private Long version; 51 | 52 | @Override 53 | public boolean isNew() { 54 | return null == getId(); 55 | } 56 | 57 | public DateTime getCreatedDate() { 58 | 59 | return null == createdDate ? null : new DateTime(createdDate); 60 | } 61 | 62 | public void setCreatedDate(final DateTime createdDate) { 63 | 64 | this.createdDate = null == createdDate ? null : createdDate.toDate(); 65 | } 66 | 67 | public DateTime getLastModifiedDate() { 68 | 69 | return null == lastModifiedDate ? null : new DateTime(lastModifiedDate); 70 | } 71 | 72 | public void setLastModifiedDate(final DateTime lastModifiedDate) { 73 | 74 | this.lastModifiedDate = null == lastModifiedDate ? null : lastModifiedDate.toDate(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/entity/ConfigEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.entity; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.Entity; 12 | import javax.persistence.Table; 13 | import javax.persistence.UniqueConstraint; 14 | 15 | /** 16 | * Created by vangie on 15/4/7. 17 | */ 18 | @Data 19 | @EqualsAndHashCode(callSuper = true) 20 | @Entity 21 | @Table(name = "T_CONFIG", uniqueConstraints = @UniqueConstraint(name = "UK_CONFIG_KEY", columnNames = {"F_KEY"})) 22 | public class ConfigEntity extends BaseEntity { 23 | 24 | @Column(name = "F_KEY", nullable = false) 25 | private String key; 26 | 27 | @Column(name = "F_NAME") 28 | private String name; 29 | 30 | @Column(columnDefinition = "text", name = "F_VALUE") 31 | private String value; 32 | 33 | public ConfigEntity() { 34 | 35 | } 36 | 37 | public ConfigEntity(String key, String value) { 38 | this.key = key; 39 | this.value = value; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/entity/ProjectEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.entity; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | 10 | import javax.persistence.Column; 11 | import javax.persistence.Entity; 12 | import javax.persistence.Enumerated; 13 | import javax.persistence.Table; 14 | 15 | import static javax.persistence.EnumType.STRING; 16 | 17 | /** 18 | * Created by vangie on 14/12/10. 19 | */ 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @Entity 23 | @Table(name = "T_PROJECT") 24 | public class ProjectEntity extends BaseEntity { 25 | 26 | @Column(name = "F_NAME") 27 | private String name; 28 | 29 | @Column(name = "F_FULL_NAME") 30 | private String fullName; 31 | 32 | @Column(name = "F_ICON_URL") 33 | private String iconUrl; 34 | 35 | @Column(name = "F_URL") 36 | private String url; 37 | 38 | @Column(name = "F_OWNER_NAME") 39 | private String ownerName; 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/entity/WorkspaceEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.entity; 6 | 7 | import com.fasterxml.jackson.annotation.JsonIgnore; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | 11 | import javax.persistence.*; 12 | 13 | import static javax.persistence.EnumType.STRING; 14 | 15 | /** 16 | * Created by vangie on 14/12/4. 17 | */ 18 | @Data 19 | @EqualsAndHashCode(callSuper = true) 20 | @Entity 21 | @Table(name = "T_WORKSPACE", uniqueConstraints = @UniqueConstraint(name = "UK_SPACE_KEY", columnNames = {"F_SPACE_KEY"}) 22 | , indexes = {@Index(columnList = "F_CREATED_AT", name = "IDX_CREATED_AT")}) 23 | public class WorkspaceEntity extends BaseEntity { 24 | 25 | public enum WsWorkingStatus { 26 | Online, 27 | Offline, 28 | Deleted, 29 | Maintaining 30 | } 31 | 32 | @ManyToOne 33 | @JoinColumn(name = "F_PROJECT_ID", foreignKey = @ForeignKey(name = "FK_WS_PRJ")) 34 | @JsonIgnore 35 | private ProjectEntity project; 36 | 37 | @Column(name = "F_SPACE_KEY") 38 | private String spaceKey; 39 | 40 | @Column(name = "F_WORKING_STATUS") 41 | @Enumerated(STRING) 42 | private WsWorkingStatus workingStatus; 43 | 44 | @Column(name = "F_ENCODING") 45 | private String encoding; 46 | 47 | @Column(columnDefinition = "text", name = "F_DESCRIPTION") 48 | private String description; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/FileChangeEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | import net.coding.ide.model.FileInfo; 11 | import net.coding.ide.model.Workspace; 12 | 13 | /** 14 | * Created by phy on 2015/1/30. 15 | */ 16 | @Data 17 | @NoArgsConstructor 18 | @EqualsAndHashCode 19 | public class FileChangeEvent { 20 | 21 | protected String spaceKey; 22 | 23 | protected FileInfo fileInfo; 24 | 25 | public FileChangeEvent(String spaceKey, FileInfo fileInfo) { 26 | this.spaceKey = spaceKey; 27 | this.fileInfo = fileInfo; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/FileCreateEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import net.coding.ide.model.FileInfo; 10 | import net.coding.ide.model.Workspace; 11 | 12 | /** 13 | * Created by phy on 2015/1/30. 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class FileCreateEvent extends FileChangeEvent { 18 | 19 | public FileCreateEvent(String spaceKey, FileInfo fileInfo) { 20 | super(spaceKey, fileInfo); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/FileDeleteEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import net.coding.ide.model.FileInfo; 10 | import net.coding.ide.model.Workspace; 11 | 12 | /** 13 | * Created by phy on 2015/1/30. 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class FileDeleteEvent extends FileChangeEvent { 18 | 19 | public FileDeleteEvent(String spaceKey, FileInfo fileInfo) { 20 | super(spaceKey, fileInfo); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/FileModifyEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import net.coding.ide.model.FileInfo; 10 | import net.coding.ide.model.Workspace; 11 | 12 | /** 13 | * Created by phy on 2015/1/30. 14 | */ 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | public class FileModifyEvent extends FileChangeEvent { 18 | 19 | public FileModifyEvent(String spaceKey, FileInfo fileInfo) { 20 | super(spaceKey, fileInfo); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/GitCheckoutEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | import lombok.Getter; 8 | import net.coding.ide.model.Workspace; 9 | 10 | public class GitCheckoutEvent { 11 | 12 | @Getter 13 | protected Workspace workspace; 14 | 15 | @Getter 16 | protected String branch; 17 | 18 | public GitCheckoutEvent(Workspace source, String branch) { 19 | this.workspace = source; 20 | this.branch = branch; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/WorkspaceDeleteEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | 8 | public class WorkspaceDeleteEvent extends WorkspaceStatusEvent { 9 | 10 | public WorkspaceDeleteEvent(Object source, String spaceKey) { 11 | super(source, spaceKey); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/WorkspaceOfflineEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | /** 8 | * Created by phy on 2015/1/30. 9 | */ 10 | public class WorkspaceOfflineEvent extends WorkspaceStatusEvent { 11 | 12 | public WorkspaceOfflineEvent(Object source, String spaceKey) { 13 | super(source, spaceKey); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/WorkspaceOnlineEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | /** 8 | * Created by phy on 2015/1/30. 9 | */ 10 | public class WorkspaceOnlineEvent extends WorkspaceStatusEvent { 11 | 12 | public WorkspaceOnlineEvent(Object source, String spaceKey) { 13 | super(source, spaceKey); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/event/WorkspaceStatusEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.event; 6 | 7 | import lombok.Data; 8 | import org.springframework.context.ApplicationEvent; 9 | 10 | /** 11 | * Created by phy on 2015/1/30. 12 | */ 13 | @Data 14 | public class WorkspaceStatusEvent extends ApplicationEvent { 15 | 16 | private String spaceKey; 17 | 18 | 19 | public WorkspaceStatusEvent(Object source, String spaceKey){ 20 | super(source); 21 | this.spaceKey = spaceKey; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/git/Identity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.git; 6 | 7 | import org.eclipse.jgit.transport.CredentialItem; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * Created by vangie on 14/12/30. 13 | */ 14 | public class Identity extends CredentialItem { 15 | 16 | 17 | private File identityFile; 18 | 19 | 20 | public Identity() { 21 | super(null, false); 22 | } 23 | 24 | @Override 25 | public void clear() { 26 | 27 | } 28 | 29 | public File getValue() { 30 | return identityFile; 31 | } 32 | 33 | public void setValue(File identityFile) { 34 | this.identityFile = identityFile; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/git/KnownHosts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.git; 6 | 7 | import org.eclipse.jgit.transport.CredentialItem; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * Created by vangie on 14/12/30. 13 | */ 14 | public class KnownHosts extends CredentialItem { 15 | 16 | private File knownHostsFile; 17 | 18 | 19 | public KnownHosts() { 20 | super(null, false); 21 | } 22 | 23 | @Override 24 | public void clear() { 25 | 26 | } 27 | 28 | public File getValue() { 29 | return knownHostsFile; 30 | } 31 | 32 | public void setValue(File knowHostsFile) { 33 | this.knownHostsFile = knowHostsFile; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/git/PrivateKeyCredentialsProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.git; 6 | 7 | import org.eclipse.jgit.errors.UnsupportedCredentialItem; 8 | import org.eclipse.jgit.transport.CredentialItem; 9 | import org.eclipse.jgit.transport.CredentialsProvider; 10 | import org.eclipse.jgit.transport.URIish; 11 | 12 | import java.io.File; 13 | 14 | /** 15 | * Created by vangie on 14/12/30. 16 | */ 17 | public class PrivateKeyCredentialsProvider extends CredentialsProvider { 18 | 19 | private File identityFile; 20 | private File knownHostsFile; 21 | 22 | public PrivateKeyCredentialsProvider(File identityFile, File knownHostsFile) { 23 | this.identityFile = identityFile; 24 | this.knownHostsFile = knownHostsFile; 25 | } 26 | 27 | @Override 28 | public boolean isInteractive() { 29 | return false; 30 | } 31 | 32 | @Override 33 | public boolean supports(CredentialItem... items) { 34 | for (CredentialItem i : items) { 35 | if (i instanceof Identity) { 36 | continue; 37 | } else if (i instanceof KnownHosts) { 38 | continue; 39 | } else { 40 | return false; 41 | } 42 | 43 | } 44 | return true; 45 | } 46 | 47 | @Override 48 | public boolean get(URIish uri, CredentialItem... items) throws UnsupportedCredentialItem { 49 | for (CredentialItem i : items) { 50 | if (i instanceof Identity) { 51 | ((Identity) i).setValue(identityFile); 52 | continue; 53 | } 54 | if (i instanceof KnownHosts) { 55 | ((KnownHosts) i).setValue(knownHostsFile); 56 | continue; 57 | } 58 | throw new UnsupportedCredentialItem(uri, i.getClass().getName() 59 | + ":" + i.getPromptText()); //$NON-NLS-1$ 60 | } 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/git/rebase/EditActionHandler.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.git.rebase; 2 | 3 | import net.coding.ide.model.RebaseResponse; 4 | import org.eclipse.jgit.api.Git; 5 | import org.eclipse.jgit.api.RebaseCommand; 6 | import org.eclipse.jgit.api.RebaseResult; 7 | import org.eclipse.jgit.api.errors.GitAPIException; 8 | import org.eclipse.jgit.lib.RebaseTodoLine; 9 | import org.eclipse.jgit.lib.Repository; 10 | import org.eclipse.jgit.util.IO; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | 15 | import static net.coding.ide.utils.RebaseStateUtils.getRebaseFile; 16 | import static org.eclipse.jgit.lib.RebaseTodoLine.Action.EDIT; 17 | 18 | /** 19 | * Created by tan on 28/03/2017. 20 | */ 21 | public class EditActionHandler implements RebaseActionHandler { 22 | 23 | @Override 24 | public RebaseTodoLine.Action getAction() { 25 | return EDIT; 26 | } 27 | 28 | @Override 29 | public RebaseResponse process(Repository repository, String message) throws GitAPIException { 30 | try (Git git = Git.wrap(repository)) { 31 | git.commit() 32 | .setAll(true) 33 | .setAmend(true) 34 | .setNoVerify(true) 35 | .setMessage(message) 36 | .call(); 37 | 38 | RebaseResult result = git.rebase() 39 | .setOperation(RebaseCommand.Operation.CONTINUE) 40 | .runInteractively(handler) 41 | .call(); 42 | 43 | // 如果 conflict and edit,amend 后 continue 会返回 NOTHING_TO_COMMIT 44 | // so skip this commit 45 | if (result.getStatus().equals(RebaseResult.Status.NOTHING_TO_COMMIT)) { 46 | result = git.rebase().setOperation(RebaseCommand.Operation.SKIP).call(); 47 | } 48 | 49 | return new RebaseResponse(result); 50 | } 51 | } 52 | 53 | @Override 54 | public RebaseResponse extractMessage(Repository repository) throws IOException { 55 | File messageFile = getRebaseFile(repository, MESSAGE); 56 | 57 | RebaseResponse response = new RebaseResponse(false, RebaseResponse.Status.INTERACTIVE_EDIT); 58 | 59 | if (messageFile.exists()) { 60 | response.setMessage(new String(IO.readFully(messageFile))); 61 | } 62 | 63 | return response; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/git/rebase/RebaseActionHandler.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.git.rebase; 2 | 3 | import net.coding.ide.model.RebaseResponse; 4 | import net.coding.ide.model.exception.GitCommitMessageNeedEditException; 5 | import org.eclipse.jgit.api.RebaseCommand; 6 | import org.eclipse.jgit.api.errors.GitAPIException; 7 | import org.eclipse.jgit.lib.RebaseTodoLine; 8 | import org.eclipse.jgit.lib.Repository; 9 | 10 | import java.io.IOException; 11 | import java.util.List; 12 | 13 | public interface RebaseActionHandler { 14 | 15 | String DONE = "done"; //$NON-NLS-1$ 16 | 17 | /** 18 | * @see RebaseCommand#MESSAGE_SQUASH 19 | */ 20 | String MESSAGE_SQUASH = "message-squash"; //$NON-NLS-1$ 21 | 22 | /** 23 | * @see RebaseCommand#MESSAGE_FIXUP 24 | */ 25 | String MESSAGE_FIXUP = "message-fixup"; //$NON-NLS-1$ 26 | 27 | /** 28 | * @see RebaseCommand#MESSAGE 29 | */ 30 | String MESSAGE = "message"; //$NON-NLS-1$ 31 | 32 | RebaseCommand.InteractiveHandler handler = new RebaseCommand.InteractiveHandler() { // stopped for edit message when squash and reword 33 | @Override 34 | public void prepareSteps(List steps) { 35 | // does nothing 36 | } 37 | 38 | @Override 39 | public String modifyCommitMessage(String commit) { 40 | throw new GitCommitMessageNeedEditException(commit); 41 | } 42 | }; 43 | 44 | RebaseResponse process(Repository repository, String message) throws GitAPIException, IOException; 45 | 46 | RebaseResponse extractMessage(Repository repository) throws IOException; 47 | 48 | RebaseTodoLine.Action getAction(); 49 | } -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/git/rebase/RewordActionHandler.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.git.rebase; 2 | 3 | import net.coding.ide.model.RebaseResponse; 4 | import org.eclipse.jgit.api.Git; 5 | import org.eclipse.jgit.api.RebaseCommand; 6 | import org.eclipse.jgit.api.RebaseResult; 7 | import org.eclipse.jgit.api.errors.GitAPIException; 8 | import org.eclipse.jgit.lib.ObjectReader; 9 | import org.eclipse.jgit.lib.RebaseTodoLine; 10 | import org.eclipse.jgit.lib.Repository; 11 | import org.eclipse.jgit.revwalk.RevCommit; 12 | import org.eclipse.jgit.revwalk.RevWalk; 13 | 14 | import java.io.IOException; 15 | import java.util.List; 16 | 17 | import static net.coding.ide.utils.RebaseStateUtils.getRebasePath; 18 | 19 | /** 20 | * Created by tan on 28/03/2017. 21 | */ 22 | public class RewordActionHandler implements RebaseActionHandler { 23 | 24 | @Override 25 | public RebaseTodoLine.Action getAction() { 26 | return RebaseTodoLine.Action.REWORD; 27 | } 28 | 29 | @Override 30 | public RebaseResponse process(Repository repository, String message) throws GitAPIException, IOException { 31 | 32 | try (Git git = Git.wrap(repository)) { 33 | git.commit() 34 | .setMessage(message) 35 | .setAmend(true) 36 | .setNoVerify(true) 37 | .call(); 38 | 39 | RebaseResult result = git.rebase() 40 | .setOperation(RebaseCommand.Operation.SKIP) 41 | .runInteractively(handler) 42 | .call(); 43 | 44 | return new RebaseResponse(result); 45 | } 46 | } 47 | 48 | @Override 49 | public RebaseResponse extractMessage(Repository repository) throws IOException { 50 | List rebaseTodoLines = repository.readRebaseTodo(getRebasePath(repository, DONE), false); 51 | // the last rebase_todo_line 52 | RebaseTodoLine line = rebaseTodoLines.get(rebaseTodoLines.size() - 1); 53 | 54 | try (RevWalk walk = new RevWalk(repository)) { 55 | ObjectReader or = repository.newObjectReader(); 56 | RevCommit commitToPick = walk.parseCommit(or.resolve(line.getCommit()).iterator().next()); 57 | 58 | String oldMessage = commitToPick.getFullMessage(); 59 | 60 | RebaseResponse response = new RebaseResponse(false, RebaseResponse.Status.INTERACTIVE_EDIT); 61 | response.setMessage(oldMessage); 62 | 63 | return response; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/git/rebase/SquashActionHandler.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.git.rebase; 2 | 3 | import net.coding.ide.model.RebaseResponse; 4 | import org.eclipse.jgit.api.Git; 5 | import org.eclipse.jgit.api.RebaseCommand; 6 | import org.eclipse.jgit.api.RebaseResult; 7 | import org.eclipse.jgit.api.errors.GitAPIException; 8 | import org.eclipse.jgit.lib.RebaseTodoLine; 9 | import org.eclipse.jgit.lib.Repository; 10 | import org.eclipse.jgit.util.IO; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | 15 | import static net.coding.ide.utils.RebaseStateUtils.createFile; 16 | import static net.coding.ide.utils.RebaseStateUtils.getRebaseFile; 17 | 18 | /** 19 | * Created by tan on 28/03/2017. 20 | */ 21 | public class SquashActionHandler implements RebaseActionHandler { 22 | 23 | @Override 24 | public RebaseTodoLine.Action getAction() { 25 | return RebaseTodoLine.Action.SQUASH; 26 | } 27 | 28 | @Override 29 | public RebaseResponse process(Repository repository, String message) throws GitAPIException, IOException { 30 | 31 | try (Git git = Git.wrap(repository)) { 32 | git.commit() 33 | .setMessage(stripCommentLines(message)) 34 | .setAmend(true).setNoVerify(true).call(); 35 | 36 | getRebaseFile(repository, MESSAGE_SQUASH).delete(); 37 | getRebaseFile(repository, MESSAGE_FIXUP).delete(); 38 | 39 | createFile(repository, MESSAGE, message); 40 | 41 | RebaseResult result = git.rebase() 42 | .setOperation(RebaseCommand.Operation.SKIP) 43 | .runInteractively(handler) 44 | .call(); 45 | 46 | return new RebaseResponse(result); 47 | } 48 | } 49 | 50 | @Override 51 | public RebaseResponse extractMessage(Repository repository) throws IOException { 52 | File messageFile = getRebaseFile(repository, MESSAGE_SQUASH); 53 | RebaseResponse response = new RebaseResponse(false, RebaseResponse.Status.INTERACTIVE_EDIT); 54 | 55 | if (messageFile.exists()) { 56 | response.setMessage(new String(IO.readFully(messageFile))); 57 | } 58 | 59 | return response; 60 | } 61 | 62 | /** 63 | * @param commitMessage 64 | * 65 | * @return 66 | * 67 | * @see RebaseCommand#stripCommentLines 68 | */ 69 | private static String stripCommentLines(String commitMessage) { 70 | StringBuilder result = new StringBuilder(); 71 | for (String line : commitMessage.split("\n")) { //$NON-NLS-1$ 72 | if (!line.trim().startsWith("#")) //$NON-NLS-1$ 73 | result.append(line).append("\n"); //$NON-NLS-1$ 74 | } 75 | if (!commitMessage.endsWith("\n")) //$NON-NLS-1$ 76 | result.deleteCharAt(result.length() - 1); 77 | return result.toString(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/Branches.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | import lombok.NonNull; 9 | import lombok.RequiredArgsConstructor; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Created by tan on 16/8/23. 15 | */ 16 | @Data 17 | @RequiredArgsConstructor(staticName = "of") 18 | public class Branches { 19 | 20 | @NonNull 21 | private String current; 22 | 23 | @NonNull 24 | private List local; 25 | 26 | @NonNull 27 | private List remote; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/CheckoutResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | @Data 12 | public class CheckoutResponse { 13 | 14 | public enum Status { 15 | CONFLICTS, 16 | ERROR, 17 | NONDELETED, 18 | NOT_TRIED, 19 | OK 20 | } 21 | 22 | private Status status; 23 | private List conflictList; 24 | private List modifiedList; 25 | private List removedList; 26 | private List undeletedList; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/CommitStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | import lombok.Getter; 9 | import lombok.NonNull; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class CommitStatus { 15 | /** 16 | * uncommitted files with their git status 17 | */ 18 | @Getter 19 | private List files; 20 | 21 | /** 22 | * {@code true} if no differences exist between the working-tree, the index, and the current HEAD 23 | */ 24 | @Getter 25 | private boolean clean; 26 | 27 | public CommitStatus(boolean clean) { 28 | this.clean = clean; 29 | files = new ArrayList<>(); 30 | } 31 | 32 | public void putFile(String name, GitStatus status) { 33 | files.add(new File(name, status)); 34 | } 35 | 36 | public File getFile(String name) { 37 | for (File file : files) { 38 | if (file.getName().equals(name)) { 39 | return file; 40 | } 41 | } 42 | return null; 43 | } 44 | 45 | @Data 46 | public class File { 47 | private String name; 48 | private GitStatus status; 49 | 50 | public File(String name, GitStatus status) { 51 | this.name = name; 52 | this.status = status; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/ConflictFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by tan on 16-3-15. 11 | */ 12 | @Data 13 | public class ConflictFile { 14 | 15 | private String base; 16 | 17 | private String local; 18 | 19 | private String remote; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/DiffEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by mingshun on 3/23/15. 11 | */ 12 | @Data 13 | public class DiffEntry { 14 | private ChangeType changeType; 15 | private String oldPath; 16 | private String newPath; 17 | 18 | public enum ChangeType { 19 | ADD, 20 | COPY, 21 | DELETE, 22 | MODIFY, 23 | RENAME 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/FileInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import org.joda.time.DateTime; 11 | 12 | 13 | /** 14 | * Created by vangie on 14/11/11. 15 | */ 16 | @Data 17 | @EqualsAndHashCode(of = {"path", "isDir"}) 18 | public class FileInfo { 19 | 20 | private String path; // 包含 name 21 | 22 | private String name; 23 | 24 | private boolean isDir; 25 | 26 | private Integer directoriesCount; 27 | 28 | private Integer filesCount; 29 | 30 | private long size; 31 | 32 | private DateTime lastModified; 33 | 34 | private DateTime lastAccessed; 35 | 36 | private String contentType; 37 | 38 | private GitStatus gitStatus; 39 | 40 | private boolean isSymbolicLink; 41 | 42 | private boolean readable = true; 43 | 44 | private boolean writable = true; 45 | 46 | private String target; 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/FileSearchResultEntry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | import lombok.NonNull; 9 | 10 | /** 11 | * Created by mingshun on 7/23/15. 12 | */ 13 | @Data 14 | public class FileSearchResultEntry { 15 | 16 | @NonNull 17 | private String path; 18 | 19 | @NonNull 20 | private String contentType; 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) return true; 25 | if (o == null || getClass() != o.getClass()) return false; 26 | 27 | FileSearchResultEntry that = (FileSearchResultEntry) o; 28 | 29 | if (!contentType.equals(that.contentType)) return false; 30 | if (!path.equals(that.path)) return false; 31 | 32 | return true; 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | int result = path.hashCode(); 38 | result = 31 * result + contentType.hashCode(); 39 | return result; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/GitBlame.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | 7 | /** 8 | * Created by tan on 03/05/2017. 9 | */ 10 | @Builder 11 | @Data 12 | public class GitBlame { 13 | 14 | private PersonIdent author; 15 | 16 | private String shortName; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/GitLog.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.model; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Created by tan on 2016/9/21. 7 | */ 8 | @Data 9 | public class GitLog { 10 | 11 | private String shortName; 12 | 13 | private String name; 14 | 15 | private String shortMessage; 16 | 17 | private int commitTime; 18 | 19 | private PersonIdent commiterIdent; 20 | 21 | private PersonIdent authorIdent; 22 | 23 | private String[] parents; 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/GitRef.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Created by tan on 19/05/2017. 10 | */ 11 | @Data 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | @Builder 15 | public class GitRef { 16 | 17 | private String name; 18 | 19 | private String id; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/GitStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | 8 | public enum GitStatus { 9 | 10 | CONFLICTION, // the file is conflicting 11 | 12 | ADDED, // the staged file that has been added 13 | CHANGED, // the staged file that has been modified 14 | REMOVED, // the staged file that has been deleted 15 | 16 | MODIFIED, // the unstaged file that has been modified 17 | MISSING, // the unstaged file that has been deleted 18 | 19 | UNTRACKED, // the file has not been tracked 20 | 21 | IGNORED, // the file has been ignored 22 | 23 | CLEAN, // the file has not been changed 24 | 25 | NONE // the directory git status is NONE 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/HttpSessions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | /** 8 | * Created by vangie on 15/5/24. 9 | */ 10 | public interface HttpSessions { 11 | String OPENED_WORKSPACE_LIST = "__opened_workspace_list"; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/Key.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.NoArgsConstructor; 10 | 11 | import javax.persistence.Column; 12 | import javax.persistence.Entity; 13 | import javax.persistence.Table; 14 | 15 | /** 16 | * Created by tan on 16/8/4. 17 | */ 18 | @Data 19 | @NoArgsConstructor 20 | public class Key { 21 | private String privateKey; 22 | 23 | private String publicKey; 24 | 25 | private String fingerprint; 26 | 27 | public Key(String privateKey, String publicKey, String fingerprint) { 28 | this.privateKey = privateKey; 29 | this.publicKey = publicKey; 30 | this.fingerprint = fingerprint; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/ListStashResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by tan on 16-3-17. 14 | */ 15 | @Data 16 | public class ListStashResponse { 17 | 18 | private List stashes = new ArrayList<>(); 19 | 20 | public void addStash(Stash stash) { 21 | this.stashes.add(stash); 22 | } 23 | 24 | @Data 25 | public static class Stash { 26 | private String name; 27 | 28 | private String message; 29 | 30 | private String rev; 31 | 32 | public Stash(String rev, String name, String message) { 33 | this.rev = rev; 34 | this.name = name; 35 | this.message = message; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/MergeResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * Created by tan on 16-3-11. 14 | */ 15 | @Data 16 | public class MergeResponse { 17 | 18 | public enum Status { 19 | FAST_FORWARD, 20 | FAST_FORWARD_SQUASHED, 21 | ALREADY_UP_TO_DATE, 22 | FAILED, 23 | MERGED, 24 | MERGED_SQUASHED, 25 | MERGED_SQUASHED_NOT_COMMITTED, 26 | CONFLICTING, 27 | ABORTED, 28 | MERGED_NOT_COMMITTED, 29 | NOT_SUPPORTED, 30 | CHECKOUT_CONFLICT 31 | } 32 | 33 | private boolean success; 34 | 35 | private Status status; 36 | 37 | private List failingPaths; 38 | 39 | public void addFailingPath(String failingPath) { 40 | if (this.failingPaths == null) { 41 | this.failingPaths = new ArrayList(); 42 | } 43 | 44 | this.failingPaths.add(failingPath); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/Message.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by tan on 16/8/11. 13 | */ 14 | @Data 15 | public class Message { 16 | private String name; 17 | 18 | List args; 19 | 20 | @Data 21 | public static class Arg { 22 | public String id; 23 | 24 | private Integer cols; 25 | 26 | private Integer rows; 27 | 28 | private String output; 29 | 30 | private String input; 31 | 32 | private String cwd; 33 | 34 | private String spaceKey; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/PersonIdent.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * Created by tan on 16/05/2017. 10 | */ 11 | @Data 12 | @Builder 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | public class PersonIdent { 16 | private String name; 17 | 18 | private String emailAddress; 19 | } -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/PushCommits.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import com.google.common.collect.Lists; 8 | import lombok.Data; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Created by mingshun on 3/22/15. 14 | */ 15 | @Data 16 | public class PushCommits { 17 | private String localRef; 18 | private String remote; 19 | private String remoteRef; 20 | private List commits; 21 | 22 | public void addCommit(Commit commit) { 23 | if (this.commits == null) { 24 | this.commits = Lists.newArrayList(); 25 | } 26 | 27 | this.commits.add(commit); 28 | } 29 | 30 | @Data 31 | public static class Commit { 32 | private String shortMessage; 33 | private String fullMessage; 34 | private String sha; 35 | private List diffEntries; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/PushResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | @Data 12 | public class PushResponse { 13 | public enum Status { 14 | AWAITING_REPORT, 15 | NON_EXISTING, 16 | NOT_ATTEMPTED, 17 | OK, 18 | REJECTED_NODELETE, 19 | REJECTED_NONFASTFORWARD, 20 | REJECTED_OTHER_REASON, 21 | REJECTED_REMOTE_CHANGED, 22 | UP_TO_DATE 23 | } 24 | 25 | private boolean nothingToPush; 26 | private boolean ok; 27 | private List updates; 28 | 29 | @Data 30 | public static class Update { 31 | private String remoteRefName; 32 | private String localRefName; 33 | private Status status; 34 | private String message; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/RebaseOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | /** 8 | * Created by tan on 16-4-8. 9 | */ 10 | public enum RebaseOperation { 11 | /** 12 | * Continues after a conflict resolution 13 | */ 14 | CONTINUE, 15 | /** 16 | * Skips the "current" commit 17 | */ 18 | SKIP, 19 | /** 20 | * Aborts and resets the current rebase 21 | */ 22 | ABORT, 23 | /** 24 | * Starts processing steps 25 | * @since 3.2 26 | */ 27 | PROCESS_STEPS 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/RebaseResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | import lombok.RequiredArgsConstructor; 9 | import org.eclipse.jgit.api.RebaseResult; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * Created by tan on 16-4-8. 15 | */ 16 | @Data 17 | public class RebaseResponse { 18 | 19 | /** 20 | * @see RebaseResult.Status 21 | */ 22 | public enum Status { 23 | /** 24 | * Rebase was successful, HEAD points to the new commit 25 | */ 26 | OK, 27 | 28 | /** 29 | * Aborted; the original HEAD was restored 30 | */ 31 | ABORTED, 32 | 33 | /** 34 | * Stopped due to a conflict; must either abort or resolve or skip 35 | */ 36 | STOPPED, 37 | 38 | /** 39 | * Stopped for editing in the context of an interactive rebase 40 | * 41 | * @since 3.2 42 | */ 43 | EDIT, 44 | 45 | /** 46 | * Failed; the original HEAD was restored 47 | */ 48 | FAILED, 49 | 50 | /** 51 | * The repository contains uncommitted changes and the rebase is not a 52 | * fast-forward 53 | * 54 | * @since 3.2 55 | */ 56 | UNCOMMITTED_CHANGES, 57 | 58 | /** 59 | * Conflicts: checkout of target HEAD failed 60 | */ 61 | CONFLICTS, 62 | 63 | /** 64 | * Already up-to-date 65 | */ 66 | UP_TO_DATE, 67 | 68 | /** 69 | * Fast-forward, HEAD points to the new commit 70 | */ 71 | FAST_FORWARD, 72 | 73 | /** 74 | * Continue with nothing left to commit (possibly want skip). 75 | * 76 | * @since 2.0 77 | */ 78 | NOTHING_TO_COMMIT, 79 | 80 | /** 81 | * Interactive rebase has been prepared 82 | * @since 3.2 83 | */ 84 | INTERACTIVE_PREPARED, 85 | 86 | /** 87 | * Applying stash resulted in conflicts 88 | * 89 | * @since 3.2 90 | */ 91 | STASH_APPLY_CONFLICTS, 92 | 93 | /** 94 | * need amend 95 | */ 96 | INTERACTIVE_EDIT 97 | } 98 | 99 | @Data 100 | public static class RebaseTodoLine { 101 | 102 | public static enum Action { 103 | /** Use commit */ 104 | PICK, 105 | 106 | /** Use commit, but edit the commit message */ 107 | REWORD, 108 | 109 | /** Use commit, but stop for amending */ 110 | EDIT, 111 | 112 | /** Use commit, but meld into previous commit */ 113 | SQUASH, 114 | 115 | /** like "squash", but discard this commit's log message */ 116 | FIXUP, 117 | 118 | /** 119 | * A comment in the file. Also blank lines (or lines containing only 120 | * whitespaces) are reported as comments 121 | */ 122 | COMMENT 123 | } 124 | 125 | private Action action; 126 | 127 | private String commit; 128 | 129 | private String shortMessage; 130 | 131 | public RebaseTodoLine(String action, String commit, String shortMessage) { 132 | this.action = Action.valueOf(action); 133 | this.commit = commit; 134 | this.shortMessage = shortMessage; 135 | } 136 | } 137 | 138 | private boolean success; 139 | 140 | private Status status; 141 | 142 | private String message; 143 | 144 | private List rebaseTodoLines; 145 | 146 | public RebaseResponse() { 147 | 148 | } 149 | 150 | public RebaseResponse(RebaseResult rebaseResult) { 151 | this.success = rebaseResult.getStatus().isSuccessful(); 152 | this.status = Status.valueOf(rebaseResult.getStatus().name()); 153 | } 154 | 155 | public RebaseResponse(boolean success, Status status) { 156 | this.success = success; 157 | this.status = status; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/RepositoryState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | /** 8 | * Created by tan on 16-4-11. 9 | * 10 | * @see org.eclipse.jgit.lib.RepositoryState 11 | */ 12 | public enum RepositoryState { 13 | /** Has no work tree and cannot be used for normal editing. */ 14 | BARE, 15 | 16 | /** 17 | * A safe state for working normally 18 | * */ 19 | SAFE, 20 | 21 | /** An unfinished merge. Must resolve or reset before continuing normally 22 | */ 23 | MERGING, 24 | 25 | /** 26 | * An merge where all conflicts have been resolved. The index does not 27 | * contain any unmerged paths. 28 | */ 29 | MERGING_RESOLVED, 30 | 31 | /** An unfinished cherry-pick. Must resolve or reset before continuing normally 32 | */ 33 | CHERRY_PICKING, 34 | 35 | /** 36 | * A cherry-pick where all conflicts have been resolved. The index does not 37 | * contain any unmerged paths. 38 | */ 39 | CHERRY_PICKING_RESOLVED, 40 | 41 | /** An unfinished revert. Must resolve or reset before continuing normally 42 | */ 43 | REVERTING, 44 | 45 | /** 46 | * A revert where all conflicts have been resolved. The index does not 47 | * contain any unmerged paths. 48 | */ 49 | REVERTING_RESOLVED, 50 | 51 | /** 52 | * An unfinished rebase or am. Must resolve, skip or abort before normal work can take place 53 | */ 54 | REBASING, 55 | 56 | /** 57 | * An unfinished rebase. Must resolve, skip or abort before normal work can take place 58 | */ 59 | REBASING_REBASING, 60 | 61 | /** 62 | * An unfinished apply. Must resolve, skip or abort before normal work can take place 63 | */ 64 | APPLY, 65 | 66 | /** 67 | * An unfinished rebase with merge. Must resolve, skip or abort before normal work can take place 68 | */ 69 | REBASING_MERGE, 70 | 71 | /** 72 | * An unfinished interactive rebase. Must resolve, skip or abort before normal work can take place 73 | */ 74 | REBASING_INTERACTIVE, 75 | 76 | /** 77 | * Bisecting being done. Normal work may continue but is discouraged 78 | */ 79 | BISECTING 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/ResetType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | /** 8 | * Created by tan on 16-4-11. 9 | */ 10 | public enum ResetType { 11 | /** 12 | * Just change the ref, the index and workdir are not changed. 13 | */ 14 | SOFT, 15 | 16 | /** 17 | * Change the ref and the index, the workdir is not changed. 18 | */ 19 | MIXED, 20 | 21 | /** 22 | * Change the ref, the index and the workdir 23 | */ 24 | HARD 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * Created by tan on 16/4/25. 11 | */ 12 | @Data 13 | public class User { 14 | 15 | private Long id; 16 | 17 | private String email; 18 | 19 | private String password; 20 | 21 | private String name; 22 | 23 | private String globalKey; 24 | 25 | private String avatar; 26 | 27 | public User(String email, String password) { 28 | this.email = email; 29 | this.password = password; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/GitCloneAuthFailException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/7/21. 9 | */ 10 | public class GitCloneAuthFailException extends RuntimeException { 11 | 12 | public GitCloneAuthFailException(String message, Throwable cause) { 13 | super(message, cause); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/GitCommitMessageNeedEditException.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.model.exception; 2 | 3 | /** 4 | * Created by tan on 28/03/2017. 5 | */ 6 | public class GitCommitMessageNeedEditException extends RuntimeException { 7 | public GitCommitMessageNeedEditException(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/GitInvalidDiffException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by mingshun on 15-9-14. 9 | */ 10 | public class GitInvalidDiffException extends RuntimeException { 11 | public GitInvalidDiffException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/GitInvalidPathException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by tan on 16-3-23. 9 | */ 10 | public class GitInvalidPathException extends RuntimeException { 11 | public GitInvalidPathException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/GitInvalidRefException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by mingshun on 15-9-14. 9 | */ 10 | public class GitInvalidRefException extends RuntimeException { 11 | public GitInvalidRefException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/GitOperationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by mingshun on 4/3/15. 9 | */ 10 | public class GitOperationException extends Exception { 11 | public GitOperationException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/NotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by tan on 16/6/21. 9 | */ 10 | public class NotFoundException extends RuntimeException { 11 | public NotFoundException(String message) { 12 | super(message); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/ProjectTypeConstraintException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | public class ProjectTypeConstraintException extends RuntimeException { 8 | 9 | public ProjectTypeConstraintException(String message) { 10 | super(message); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/TransportProtocolUnsupportedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/7/16. 9 | */ 10 | public class TransportProtocolUnsupportedException extends RuntimeException{ 11 | public TransportProtocolUnsupportedException(String message) { 12 | super(message); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/WorkspaceCreationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/3/9. 9 | */ 10 | public class WorkspaceCreationException extends WorkspaceException { 11 | public WorkspaceCreationException(String msg) { 12 | super(msg); 13 | } 14 | 15 | public WorkspaceCreationException(String msg, Throwable cause) { 16 | super(msg, cause); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/WorkspaceDeletedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/6/3. 9 | */ 10 | public class WorkspaceDeletedException extends WorkspaceMissingException { 11 | 12 | public WorkspaceDeletedException(String msg) { 13 | super(msg); 14 | } 15 | 16 | public WorkspaceDeletedException(String msg, Throwable cause) { 17 | super(msg, cause); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/WorkspaceDeletingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/6/3. 9 | */ 10 | public class WorkspaceDeletingException extends WorkspaceException { 11 | public WorkspaceDeletingException(String msg, Throwable cause) { 12 | super(msg, cause); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/WorkspaceException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/6/3. 9 | */ 10 | public class WorkspaceException extends RuntimeException { 11 | 12 | public WorkspaceException() { 13 | 14 | } 15 | 16 | public WorkspaceException(String msg) { 17 | super(msg); 18 | } 19 | 20 | public WorkspaceException(String msg, Throwable cause) { 21 | super(msg, cause); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/WorkspaceIOException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by mingshun on 15-9-23. 9 | */ 10 | public class WorkspaceIOException extends RuntimeException { 11 | 12 | public WorkspaceIOException(String message) { 13 | super(message); 14 | } 15 | 16 | public WorkspaceIOException(String message, Throwable cause) { 17 | super(message, cause); 18 | } 19 | 20 | public WorkspaceIOException(Throwable cause) { 21 | super(cause); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/WorkspaceMaintainingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/4/14. 9 | */ 10 | public class WorkspaceMaintainingException extends WorkspaceException { 11 | 12 | public WorkspaceMaintainingException(String msg){ 13 | super(msg); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/model/exception/WorkspaceMissingException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.model.exception; 6 | 7 | /** 8 | * Created by vangie on 15/3/9. 9 | */ 10 | public class WorkspaceMissingException extends WorkspaceException { 11 | 12 | public WorkspaceMissingException(String msg) { 13 | super(msg); 14 | } 15 | 16 | public WorkspaceMissingException(String msg, Throwable cause) { 17 | super(msg, cause); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/repository/ConfigRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.repository; 6 | 7 | import net.coding.ide.entity.ConfigEntity; 8 | import org.springframework.data.jpa.repository.Modifying; 9 | import org.springframework.data.jpa.repository.Query; 10 | import org.springframework.data.repository.CrudRepository; 11 | 12 | /** 13 | * Created by vangie on 15/4/8. 14 | */ 15 | public interface ConfigRepository extends CrudRepository { 16 | 17 | @Query("SELECT t.name FROM #{#entityName} t WHERE t.key = ?1") 18 | String getNameByKey(String key); 19 | 20 | @Query("SELECT t.value FROM #{#entityName} t WHERE t.key = ?1") 21 | String getValueByKey(String key); 22 | 23 | ConfigEntity getByKey(String key); 24 | 25 | @Modifying 26 | @Query("UPDATE #{#entityName} r SET r.name = ?2 WHERE r.key = ?1") 27 | void updateName(String key, String newName); 28 | 29 | @Modifying 30 | @Query("UPDATE #{#entityName} r SET r.value = ?2 WHERE r.key = ?1") 31 | void updateValue(String key, String newValue); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/repository/ProjectRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.repository; 6 | 7 | import net.coding.ide.entity.ProjectEntity; 8 | import org.springframework.data.repository.CrudRepository; 9 | 10 | /** 11 | * Created by vangie on 14/12/10. 12 | */ 13 | public interface ProjectRepository extends CrudRepository { 14 | 15 | ProjectEntity findByUrl(String sshUrl); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/repository/WorkspaceRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.repository; 6 | 7 | import net.coding.ide.entity.ProjectEntity; 8 | import net.coding.ide.entity.WorkspaceEntity; 9 | import net.coding.ide.entity.WorkspaceEntity.WsWorkingStatus; 10 | import org.springframework.data.domain.Page; 11 | import org.springframework.data.domain.Pageable; 12 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 13 | import org.springframework.data.jpa.repository.Modifying; 14 | import org.springframework.data.jpa.repository.Query; 15 | import org.springframework.data.repository.PagingAndSortingRepository; 16 | import org.springframework.stereotype.Repository; 17 | import org.springframework.transaction.annotation.Transactional; 18 | 19 | import java.util.List; 20 | 21 | @Repository 22 | public interface WorkspaceRepository extends PagingAndSortingRepository, JpaSpecificationExecutor { 23 | 24 | WorkspaceEntity findBySpaceKey(String spaceKey); 25 | 26 | @Query("SELECT CASE count(w) WHEN 0 THEN FALSE ELSE TRUE END FROM #{#entityName} w WHERE w.spaceKey = ?1") 27 | boolean isSpaceKeyExist(String spaceKey); 28 | 29 | @Query("SELECT CASE w.workingStatus WHEN 'Deleted' THEN TRUE ELSE FALSE END FROM #{#entityName} w WHERE w.spaceKey = ?1") 30 | boolean isDeleted(String spaceKey); 31 | 32 | @Query("SELECT CASE w.workingStatus WHEN 'Online' THEN TRUE ELSE FALSE END FROM #{#entityName} w WHERE w.spaceKey = ?1") 33 | boolean isOnline(String spaceKey); 34 | 35 | @Query("SELECT w.project FROM #{#entityName} w WHERE w.spaceKey = ?1") 36 | ProjectEntity findProjectBySpaceKey(String spaceKey); 37 | 38 | WorkspaceEntity findByProject(ProjectEntity project); 39 | 40 | @Query("SELECT w FROM #{#entityName} w WHERE w.workingStatus != 'Deleted' and w.project=?1") 41 | WorkspaceEntity findNotDeletedByProject(ProjectEntity projectEntity); 42 | 43 | @Query("SELECT CASE count(w) WHEN 0 THEN FALSE ELSE TRUE END FROM #{#entityName} w WHERE w.project = ?1 AND w.workingStatus != 'Deleted'") 44 | boolean isProjectReferred(ProjectEntity project); 45 | 46 | @Query("SELECT w FROM #{#entityName} w WHERE w.workingStatus != 'Deleted'") 47 | List findNotDeleted(); 48 | 49 | @Modifying 50 | @Transactional 51 | @Query("UPDATE #{#entityName} w SET w.workingStatus = ?2 WHERE w.spaceKey = ?1") 52 | void updateWorkingStatus(String spaceKey, WsWorkingStatus wsWorkingStatus); 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/BaseService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.transaction.PlatformTransactionManager; 9 | import org.springframework.transaction.TransactionDefinition; 10 | import org.springframework.transaction.support.TransactionTemplate; 11 | import org.springframework.util.Assert; 12 | 13 | import javax.annotation.PostConstruct; 14 | 15 | /** 16 | * Created by vangie on 15/5/20. 17 | */ 18 | public class BaseService { 19 | 20 | protected TransactionTemplate transactionTemplate; 21 | 22 | @Autowired 23 | private PlatformTransactionManager transactionManager; 24 | 25 | @PostConstruct 26 | private void postConstruct() { 27 | Assert.notNull(transactionManager, "The 'transactionManager' argument must not be null."); 28 | this.transactionTemplate = new TransactionTemplate(transactionManager); 29 | this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED); 30 | this.transactionTemplate.setTimeout(30); // 30 seconds 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/ConfigService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import net.coding.ide.entity.ConfigEntity; 8 | 9 | /** 10 | * Created by vangie on 15/4/8. 11 | */ 12 | public interface ConfigService { 13 | 14 | String getName(String key); 15 | 16 | ConfigEntity getByKey(String key); 17 | 18 | void setName(String key, String name); 19 | 20 | String getValue(String key); 21 | 22 | String getValue(String key, String defaultValue); 23 | 24 | String getValue(String key, String... subs); 25 | 26 | void setValue(String key, String value); 27 | 28 | void setCfg(String key, String name, String value); 29 | 30 | Iterable findAll(); 31 | 32 | void deleteCfg(String key); 33 | 34 | boolean exist(String key); 35 | 36 | String[] getValues(String key, String separatorChar); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/ConfigServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import net.coding.ide.entity.ConfigEntity; 8 | import net.coding.ide.repository.ConfigRepository; 9 | import org.apache.commons.lang3.StringUtils; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | /** 18 | * Created by vangie on 15/4/8. 19 | */ 20 | @Service 21 | public class ConfigServiceImpl extends BaseService implements ConfigService { 22 | 23 | @Autowired 24 | private ConfigRepository cfgRepo; 25 | 26 | @Override 27 | public String getValue(String key) { 28 | String rawCfg = cfgRepo.getValueByKey(key); 29 | if (rawCfg == null) { 30 | return null; 31 | } 32 | StringBuffer result = new StringBuffer(); 33 | Matcher m = Pattern.compile("\\$\\{(.+?)\\}").matcher(rawCfg); 34 | while (m.find()) { 35 | String cfgKey = m.group(1); 36 | if (exist(cfgKey)) { 37 | m.appendReplacement(result, getValue(cfgKey)); 38 | } else { 39 | m.appendReplacement(result, "\\${" + cfgKey + "}"); 40 | } 41 | } 42 | m.appendTail(result); 43 | return result.toString(); 44 | } 45 | 46 | @Override 47 | public String getValue(String key, String defaultValue) { 48 | if (exist(key)) { 49 | return getValue(key); 50 | } else { 51 | return defaultValue; 52 | } 53 | } 54 | 55 | @Override 56 | public String getValue(String key, String... subs) { 57 | String value = getValue(key); 58 | if (value != null) { 59 | for (int i = 0; i < subs.length; i++) { 60 | value = value.replaceAll("\\{" + i + "\\}", subs[i]); 61 | } 62 | return value; 63 | } 64 | return null; 65 | } 66 | 67 | @Override 68 | public String[] getValues(String key, String separatorChar) { 69 | return StringUtils.split(getValue(key), separatorChar); 70 | } 71 | 72 | @Override 73 | @Transactional 74 | public void setValue(String key, String value) { 75 | cfgRepo.updateValue(key, value); 76 | } 77 | 78 | @Override 79 | public String getName(String key) { 80 | return cfgRepo.getNameByKey(key); 81 | } 82 | 83 | @Override 84 | public ConfigEntity getByKey(String key) { 85 | return cfgRepo.getByKey(key); 86 | } 87 | 88 | @Override 89 | @Transactional 90 | public void setName(String key, String name) { 91 | cfgRepo.updateName(key, name); 92 | } 93 | 94 | @Override 95 | @Transactional 96 | public void setCfg(String key, String name, String value) { 97 | ConfigEntity cfg = cfgRepo.getByKey(key); 98 | if (cfg == null) 99 | cfg = new ConfigEntity(); 100 | cfg.setKey(key); 101 | cfg.setName(name); 102 | cfg.setValue(value); 103 | cfgRepo.save(cfg); 104 | } 105 | 106 | 107 | @Override 108 | public Iterable findAll() { 109 | return cfgRepo.findAll(); 110 | } 111 | 112 | @Override 113 | @Transactional 114 | public void deleteCfg(String key) { 115 | cfgRepo.delete(cfgRepo.getByKey(key)); 116 | } 117 | 118 | 119 | @Override 120 | public boolean exist(String key) { 121 | return cfgRepo.getByKey(key) != null; 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/GitManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import net.coding.ide.model.*; 8 | import net.coding.ide.model.exception.GitOperationException; 9 | import org.eclipse.jgit.api.errors.GitAPIException; 10 | import org.eclipse.jgit.revwalk.filter.RevFilter; 11 | import org.springframework.data.domain.Pageable; 12 | 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | import java.nio.file.AccessDeniedException; 16 | import java.nio.file.Path; 17 | import java.util.List; 18 | import java.util.Map; 19 | 20 | /** 21 | * Created by vangie on 14/12/29. 22 | */ 23 | public interface GitManager { 24 | 25 | GitStatus status(Workspace ws, Path path) throws Exception; 26 | 27 | boolean clone(Workspace ws) throws IOException, GitAPIException; 28 | 29 | void config(Workspace ws) throws IOException; 30 | 31 | CommitStatus getStatus(Workspace ws) throws GitAPIException; 32 | 33 | MergeResponse merge(Workspace ws, String branch) throws GitAPIException, IOException; 34 | 35 | void createStash(Workspace ws, boolean includeUntracked, String message) throws GitAPIException, GitOperationException; 36 | 37 | void applyStash(Workspace ws, String stashRef, boolean applyIndex, boolean pop) throws GitAPIException; 38 | 39 | CheckoutResponse checkoutStash(Workspace ws, String stashRef, String branch) throws IOException, GitAPIException, GitOperationException; 40 | 41 | ListStashResponse listStash(Workspace ws) throws GitAPIException; 42 | 43 | void dropStash(Workspace ws, String stashRef) throws GitAPIException; 44 | 45 | void dropAllStash(Workspace ws) throws GitAPIException; 46 | 47 | ConflictFile queryConflictFile(Workspace ws, String path, boolean base64) throws Exception; 48 | 49 | void deleteConflictFile(Workspace ws, String path) throws Exception; 50 | 51 | void resolveConflictFile(Workspace ws, String path, String content, boolean base64) throws Exception; 52 | 53 | List commit(Workspace ws, List files, String message) throws GitAPIException, IOException; 54 | 55 | List commitAll(Workspace ws, String message) throws GitAPIException, IOException; 56 | 57 | List refs(Workspace ws) throws IOException, GitAPIException; 58 | 59 | List log(Workspace ws, String[] ref, String[] path, String[] authors, Long since, Long until, Pageable pageable) throws GitAPIException, IOException; 60 | 61 | List blame(Workspace ws, String path) throws AccessDeniedException, GitAPIException; 62 | 63 | String diff(Workspace ws, String path, String oldRef, String newRef) throws IOException, GitAPIException; 64 | 65 | void sync(Workspace ws) throws GitAPIException; 66 | 67 | PushCommits getPushCommits(Workspace ws) throws IOException, GitAPIException, GitOperationException; 68 | 69 | PushCommits getPushCommits(Workspace ws, String branch) throws IOException, GitAPIException, GitOperationException; 70 | 71 | PushResponse push(Workspace ws) throws GitAPIException, IOException, GitOperationException; 72 | 73 | PushResponse push(Workspace ws, String ref) throws GitAPIException, IOException, GitOperationException; 74 | 75 | PushResponse pushAll(Workspace ws) throws GitAPIException, IOException, GitOperationException; 76 | 77 | boolean pull(Workspace ws) throws GitAPIException, IOException; 78 | 79 | CheckoutResponse checkout(Workspace ws, String name, String startPoint) throws GitAPIException, IOException, GitOperationException; 80 | 81 | void fetch(Workspace ws) throws GitAPIException; 82 | 83 | void fetch(Workspace ws, boolean prune) throws GitAPIException; 84 | 85 | /** 86 | * Retrieve the current branch that HEAD points to. 87 | * 88 | * @return branch name 89 | */ 90 | String getBranch(Workspace ws) throws IOException; 91 | 92 | List getLocalBranches(Workspace ws) throws GitAPIException; 93 | 94 | List getRemoteBranches(Workspace ws) throws GitAPIException; 95 | 96 | Branches getBranches(Workspace ws) throws GitAPIException, IOException; 97 | 98 | void createBranch(Workspace ws, String branchName) throws GitAPIException; 99 | 100 | void deleteBranch(Workspace ws, String branchName) throws GitAPIException, IOException, GitOperationException; 101 | 102 | boolean hasBranch(Workspace ws, String branch) throws GitAPIException; 103 | 104 | List getTags(Workspace ws) throws GitAPIException; 105 | 106 | void createTag(Workspace ws, String tagName, String ref, String message, boolean force) throws GitAPIException, IOException; 107 | 108 | void deleteTag(Workspace ws, String tagName) throws GitAPIException; 109 | 110 | boolean checkIgnore(InputStream in, String path, boolean isDir); 111 | 112 | List getDiffEntryForCommit(Workspace ws, String commitId) throws IOException, GitAPIException; 113 | 114 | RebaseResponse rebase(Workspace ws, String upstream, boolean interactive, boolean preserve) throws GitAPIException, IOException, GitOperationException; 115 | 116 | RebaseResponse rebase(Workspace ws, String branch, String upstream, boolean interactive, boolean preserve) throws GitAPIException, IOException, GitOperationException; 117 | 118 | RebaseResponse updateRebaseTodo(Workspace ws, List lines) throws IOException, GitOperationException, GitAPIException; 119 | 120 | RebaseResponse operateRebase(Workspace ws, RebaseOperation operation) throws GitAPIException, IOException; 121 | 122 | RebaseResponse operateRebase(Workspace ws, RebaseOperation operation, String message) throws GitAPIException, IOException; 123 | 124 | RepositoryState state(Workspace ws); 125 | 126 | String readFileFromRef(Workspace ws, String ref, String path, boolean base64) throws IOException; 127 | 128 | String readFileFromRef(Workspace ws, String ref, String path, String encoding, boolean base64) throws IOException; 129 | 130 | void reset(Workspace ws, String ref, ResetType resetType) throws GitAPIException; 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/KeyManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import net.coding.ide.model.Key; 8 | import net.coding.ide.model.Workspace; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | /** 14 | * Created by vangie on 14/12/24. 15 | */ 16 | public interface KeyManager { 17 | 18 | boolean isKeyExist(); 19 | 20 | boolean isKeyExist(Workspace ws); 21 | 22 | Key generateKey() throws IOException; 23 | 24 | void copyToWorkspace(Workspace ws); 25 | 26 | File getPrivateKeyFile(Workspace ws); 27 | 28 | File getPublicKeyFile(Workspace ws); 29 | 30 | File getKnownHostsFile(Workspace ws); 31 | 32 | Key getKey(); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/KeyManagerImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import com.google.common.io.Files; 8 | import lombok.extern.slf4j.Slf4j; 9 | import net.coding.ide.model.Key; 10 | import net.coding.ide.model.Workspace; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.beans.factory.annotation.Value; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.security.KeyPair; 21 | import java.security.KeyPairGenerator; 22 | import java.security.NoSuchAlgorithmException; 23 | import java.security.interfaces.RSAPrivateKey; 24 | import java.security.interfaces.RSAPublicKey; 25 | 26 | import static com.google.common.collect.Sets.newHashSet; 27 | import static java.lang.String.format; 28 | import static java.nio.charset.Charset.defaultCharset; 29 | import static java.nio.file.Files.setPosixFilePermissions; 30 | import static java.nio.file.attribute.PosixFilePermission.OWNER_READ; 31 | import static java.nio.file.attribute.PosixFilePermission.OWNER_WRITE; 32 | import static net.coding.ide.utils.KeyUtils.fingerprint; 33 | import static net.coding.ide.utils.KeyUtils.keyToString; 34 | import static org.apache.commons.lang.SystemUtils.IS_OS_WINDOWS; 35 | 36 | /** 37 | * Created by vangie on 14/12/24. 38 | */ 39 | @Slf4j 40 | @Service 41 | public class KeyManagerImpl extends BaseService implements KeyManager { 42 | private final static String CODING_PUB_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHOWdwLpkos2CLli6DFvQ36yQE6Pe" + 43 | "/PtFp3XwyirfZCIoGWnedaWI8zkJWVCs0wgOB9/urFepTDfV2wN49KGy1sl2/CCDEH2K/zeoEAZlTcBrhU17bwg1yMHCyJ7IM+zdLzIt" + 44 | "DEKYjgoWqVdUGK1dXQQlwt7GP4W7HqffelQQoVxOMoZ5N50MzD+nvV4y8iq0KwDQNy62iU4hui9ajCSVUDLu/06ucd5IojSI9keRIYAX" + 45 | "vQf52TJ5EbvoBggp9RhjuWNEG8IhnPP6rzPS11Ocmwg/HsP8xOKL28AeDBAh6B6MEBDtlyp5Yfu9cwZJ9CFtU/x5fHFPtANmgIphAfwN1"; 46 | 47 | private final static String PUBLICKEY = "publicKey"; 48 | 49 | private final static String PRIVATEKEY = "privateKey"; 50 | 51 | private final static String FINGERPRINT = "fingerPrint"; 52 | 53 | @Value("${USERNAME}") 54 | private String username; 55 | 56 | @Autowired 57 | private ConfigService configService; 58 | 59 | public boolean isKeyExist() { 60 | return configService.getByKey(PUBLICKEY) != null; 61 | } 62 | 63 | @Override 64 | public boolean isKeyExist(Workspace ws) { 65 | return getPrivateKeyFile(ws).exists() && getPublicKeyFile(ws).exists(); 66 | } 67 | 68 | public Key generateKey() throws IOException { 69 | KeyPairGenerator generator = null; 70 | try { 71 | generator = KeyPairGenerator.getInstance("RSA"); 72 | // or: generator = KeyPairGenerator.getInstance("DSA"); 73 | } catch (NoSuchAlgorithmException e) { 74 | e.printStackTrace(); 75 | } 76 | 77 | generator.initialize(2048); 78 | KeyPair keyPair = generator.genKeyPair(); 79 | final String publicKey = keyToString((RSAPublicKey) keyPair.getPublic(), username); 80 | final String privateKey = keyToString((RSAPrivateKey) keyPair.getPrivate()); 81 | final String fingerprint = fingerprint(publicKey); 82 | 83 | Key key = new Key(privateKey, publicKey, fingerprint); 84 | 85 | saveOrUpdateKey(key); 86 | 87 | return key; 88 | } 89 | 90 | @Transactional 91 | private void saveOrUpdateKey(Key key) { 92 | configService.setCfg(PUBLICKEY, PUBLICKEY, key.getPublicKey()); 93 | configService.setCfg(PRIVATEKEY, PRIVATEKEY, key.getPrivateKey()); 94 | configService.setCfg(FINGERPRINT, FINGERPRINT, key.getFingerprint()); 95 | } 96 | 97 | public Key getKey() { 98 | Key key = new Key(); 99 | 100 | key.setPrivateKey(configService.getValue(PRIVATEKEY)); 101 | key.setPublicKey(configService.getValue(PUBLICKEY)); 102 | key.setFingerprint(configService.getValue(FINGERPRINT)); 103 | 104 | return key; 105 | } 106 | 107 | @Override 108 | public void copyToWorkspace(Workspace ws) { 109 | try { 110 | if (!isKeyExist()) generateKey(); 111 | 112 | Key key = getKey(); 113 | 114 | Files.write(key.getPublicKey(), getPublicKeyFile(ws), defaultCharset()); 115 | Files.write(key.getPrivateKey(), getPrivateKeyFile(ws), defaultCharset()); 116 | Files.write(format("coding.net %s\n", CODING_PUB_KEY), getKnownHostsFile(ws), defaultCharset()); 117 | 118 | //ignore on windows. 119 | if (!IS_OS_WINDOWS) { 120 | //using PosixFilePermission to set file permissions 600 121 | setPosixFilePermissions(getPrivateKeyFile(ws).toPath(), newHashSet(OWNER_READ, OWNER_WRITE)); 122 | } 123 | } catch (IOException e) { 124 | log.error("copy key to workspace failed", e); 125 | } 126 | } 127 | @Override 128 | public File getPrivateKeyFile(Workspace ws) { 129 | return new File(ws.getKeyDir(), "id_rsa"); 130 | } 131 | 132 | @Override 133 | public File getPublicKeyFile(Workspace ws) { 134 | return new File(ws.getKeyDir(), "id_rsa.pub"); 135 | } 136 | 137 | @Override 138 | public File getKnownHostsFile(Workspace ws) { 139 | return new File(ws.getKeyDir(), "known_hosts"); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/PackageService.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.service; 2 | 3 | 4 | import net.coding.ide.dto.Package; 5 | 6 | import java.io.IOException; 7 | import java.util.List; 8 | 9 | public interface PackageService { 10 | 11 | List findAll(); 12 | 13 | String readPackageFile(String name, String version, String file) throws IOException; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/PackageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.service; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.gson.Gson; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.stream.JsonReader; 7 | import net.coding.ide.dto.Package; 8 | import net.coding.ide.model.exception.NotFoundException; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.io.File; 13 | import java.io.FileReader; 14 | import java.io.IOException; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.util.List; 18 | import java.util.stream.Collectors; 19 | 20 | import static java.lang.String.format; 21 | 22 | @Service 23 | public class PackageServiceImpl implements PackageService { 24 | 25 | private Path packagesDir; 26 | 27 | private Long lastModified; 28 | 29 | private Gson gson = new Gson(); 30 | 31 | public PackageServiceImpl(@Value("${PACKAGE_HOME}") File packagesDir) { 32 | this.packagesDir = packagesDir.toPath(); 33 | } 34 | 35 | private Package toPackage(Path path) { 36 | JsonReader reader = null; 37 | try { 38 | reader = new JsonReader(new FileReader(path.toFile())); 39 | JsonObject json = gson.fromJson(reader, JsonObject.class); 40 | Package p = gson.fromJson(json.get("meta"), Package.class); 41 | p.setRequirement(Package.Requirement.Required); 42 | return p; 43 | } catch (Exception e) { 44 | return null; 45 | } 46 | } 47 | 48 | private List packages = Lists.newArrayList(); 49 | 50 | public synchronized List findAll() { 51 | 52 | try { 53 | if (lastModified == null 54 | || lastModified.longValue() != packagesDir.toFile().lastModified()) { 55 | lastModified = packagesDir.toFile().lastModified(); 56 | 57 | 58 | packages = Files.walk(packagesDir) 59 | .filter(path -> path.toFile().getName().endsWith("manifest.json")) 60 | .map(this::toPackage) 61 | .filter(p -> p != null) 62 | .collect(Collectors.toList()); 63 | } 64 | } catch (IOException e) { 65 | return Lists.newArrayList(); 66 | } 67 | 68 | return packages; 69 | } 70 | 71 | public String readPackageFile(String name, String version, String file) throws IOException { 72 | 73 | Path path = new File(packagesDir.toFile(), format("%s/%s/%s", name, version, file)).toPath(); 74 | 75 | if ( ! path.toFile().exists() ) { 76 | throw new NotFoundException(format("file %s could not be found", path.toFile().getAbsolutePath())); 77 | } 78 | 79 | byte[] bytes = Files.readAllBytes(path); 80 | 81 | return new String(bytes); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/ProjectService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import net.coding.ide.entity.ProjectEntity; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by vangie on 16/1/27. 13 | */ 14 | public interface ProjectService { 15 | 16 | List projects(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/ProjectServiceImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import com.google.common.collect.Lists; 8 | import lombok.extern.slf4j.Slf4j; 9 | import net.coding.ide.entity.ProjectEntity; 10 | import net.coding.ide.repository.ProjectRepository; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.transaction.annotation.Transactional; 16 | 17 | import java.util.List; 18 | 19 | /** 20 | * Created by vangie on 16/1/27. 21 | */ 22 | @Service 23 | public class ProjectServiceImpl extends BaseService implements ProjectService { 24 | @Autowired 25 | private ProjectRepository prjRepo; 26 | 27 | @Override 28 | @Transactional 29 | public List projects() { 30 | return Lists.newArrayList(prjRepo.findAll()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/WatchedPathStore.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.service; 2 | 3 | import com.google.common.collect.*; 4 | import org.springframework.stereotype.Component; 5 | 6 | import static com.google.common.collect.Multimaps.synchronizedSetMultimap; 7 | 8 | @Component 9 | public class WatchedPathStore { 10 | 11 | private SetMultimap watchedPath = synchronizedSetMultimap(HashMultimap.create()); 12 | 13 | public void add(String spaceKey, String path) { 14 | watchedPath.put(spaceKey, path); 15 | } 16 | 17 | public void remove(String spaceKey, String path) { 18 | watchedPath.remove(spaceKey, path); 19 | } 20 | 21 | public void clear(String spaceKey) { 22 | watchedPath.removeAll(spaceKey); 23 | } 24 | 25 | public boolean hasWatched(String spaceKey, String path) { 26 | if (!path.startsWith("/")) { 27 | throw new IllegalArgumentException("path should start with '/'."); 28 | } 29 | 30 | int lastSlash = path.lastIndexOf('/'); 31 | String p = path.substring(0, lastSlash + 1); 32 | 33 | return watchedPath.containsEntry(spaceKey, p); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/WorkspaceManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import net.coding.ide.dto.FileDTO; 8 | import net.coding.ide.entity.WorkspaceEntity; 9 | import net.coding.ide.model.FileInfo; 10 | import net.coding.ide.model.FileSearchResultEntry; 11 | import net.coding.ide.model.Workspace; 12 | import net.coding.ide.model.exception.GitCloneAuthFailException; 13 | import org.eclipse.jgit.api.errors.GitAPIException; 14 | import org.springframework.data.domain.Page; 15 | import org.springframework.data.domain.Pageable; 16 | 17 | import java.io.IOException; 18 | import java.util.List; 19 | 20 | /** 21 | * Created by vangie on 14/11/11. 22 | */ 23 | public interface WorkspaceManager { 24 | 25 | Workspace setup(String spaceKey); 26 | 27 | Workspace createFromUrl(String gitUrl) throws GitCloneAuthFailException; 28 | 29 | void delete(String spaceKey); 30 | 31 | Workspace getWorkspace(String spaceKey); 32 | 33 | List list(); 34 | 35 | WorkspaceEntity getWorkspaceEntity(String spaceKey); 36 | 37 | FileDTO readFile(Workspace ws, String path, String encoding, boolean base64) throws IOException, GitAPIException, Exception; 38 | 39 | FileInfo getFileInfo(Workspace ws, String path) throws Exception; 40 | 41 | List listFiles(Workspace ws, String path, boolean order, boolean group) throws Exception; 42 | 43 | List search(Workspace ws, String keyword, boolean includeNonProjectItems) throws IOException; 44 | 45 | boolean isOnline(String spaceKey); 46 | 47 | boolean isDeleted(String spaceKey); 48 | 49 | void setEncoding(Workspace ws, String charSet); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/service/WorkspaceWatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import com.google.common.collect.Lists; 8 | import lombok.extern.slf4j.Slf4j; 9 | import net.coding.ide.event.FileChangeEvent; 10 | import net.coding.ide.event.FileCreateEvent; 11 | import net.coding.ide.event.FileDeleteEvent; 12 | import net.coding.ide.event.FileModifyEvent; 13 | import net.coding.ide.model.FileInfo; 14 | import net.coding.ide.model.Workspace; 15 | import net.coding.ide.utils.Debouncer; 16 | import org.springframework.context.ApplicationEventPublisher; 17 | 18 | import java.io.IOException; 19 | import java.nio.file.*; 20 | import java.nio.file.attribute.BasicFileAttributes; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | import static java.nio.file.Files.isDirectory; 26 | import static java.nio.file.LinkOption.NOFOLLOW_LINKS; 27 | import static java.nio.file.StandardWatchEventKinds.*; 28 | 29 | /** 30 | * Created by phy on 2015/1/30. 31 | */ 32 | @Slf4j 33 | public class WorkspaceWatcher extends Thread { 34 | private volatile boolean stopWatching = false; 35 | private Workspace ws; 36 | private WatchService watcher; 37 | private Path workingDir; 38 | private Map keys = new HashMap<>(); 39 | 40 | private Debouncer debouncer; 41 | 42 | private static String[] ignoreDirs = new String[]{"/.git/objects/"}; 43 | 44 | private WatchedPathStore watchedPathStore; 45 | 46 | private WorkspaceManager wsMgr; 47 | 48 | private List ignorePaths = Lists.newArrayList(); 49 | 50 | { 51 | try { 52 | this.watcher = FileSystems.getDefault().newWatchService(); 53 | } catch (IOException e) { 54 | e.printStackTrace(); 55 | } 56 | } 57 | 58 | public WorkspaceWatcher(WorkspaceManager wsMgr, Workspace ws, WatchedPathStore watchedPathStore, final ApplicationEventPublisher publisher) { 59 | this.ws = ws; 60 | this.wsMgr = wsMgr; 61 | this.workingDir = ws.getWorkingDir().toPath(); 62 | this.watchedPathStore = watchedPathStore; 63 | 64 | this.debouncer = new Debouncer<>(event -> { 65 | log.info("publish file change event: {}", event); 66 | publisher.publishEvent(event); 67 | }, 50); 68 | 69 | for (String dir : ignoreDirs) { 70 | try { 71 | ignorePaths.add(ws.getPath(dir)); 72 | watchedPathStore.add(ws.getSpaceKey(), ws.getPath(dir).toString()); 73 | } catch (AccessDeniedException e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | watchedPathStore.add(ws.getSpaceKey(), "/"); 79 | watchedPathStore.add(ws.getSpaceKey(), "/.git/"); // for .git/HEAD 80 | } 81 | 82 | public void stopWatching() { 83 | this.stopWatching = true; 84 | try { 85 | watcher.close(); 86 | } catch (IOException e) { 87 | e.printStackTrace(); 88 | } 89 | } 90 | 91 | 92 | private void register(Path dir) throws IOException { 93 | WatchKey key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); 94 | keys.put(key, dir); 95 | } 96 | 97 | private void registerAll(final Path start) { 98 | // register directory and sub-directories 99 | try { 100 | Files.walkFileTree(start, new SimpleFileVisitor() { 101 | @Override 102 | public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) 103 | throws IOException { 104 | if (ignorePaths.contains(dir)) { 105 | return FileVisitResult.SKIP_SUBTREE; 106 | } else { 107 | register(dir); 108 | return FileVisitResult.CONTINUE; 109 | } 110 | } 111 | }); 112 | } catch (IOException e) { 113 | // ignore to keep sample readable 114 | } 115 | } 116 | 117 | public void run() { 118 | 119 | registerAll(workingDir); 120 | 121 | while ( ! stopWatching && ! isInterrupted() ) { 122 | 123 | try { 124 | WatchKey watchKey = watcher.take(); 125 | 126 | Path dir = keys.get(watchKey); 127 | 128 | if (dir == null) { 129 | continue; 130 | } 131 | 132 | for (WatchEvent event : watchKey.pollEvents()) { 133 | 134 | WatchEvent ev = (WatchEvent) event; 135 | Path fileName = ev.context(); 136 | Path filePath = dir.resolve(fileName); 137 | 138 | Path relativePath = workingDir.relativize(filePath); 139 | String path = ws.getNormalizePath(relativePath.toString()).toString(); 140 | 141 | if ( ! path.startsWith("/.git/refs/heads/") 142 | && ! watchedPathStore.hasWatched(ws.getSpaceKey(), path) ) { 143 | log.debug("not watched {} on workspace {}", path, ws.getSpaceKey()); 144 | continue; 145 | } 146 | 147 | FileChangeEvent fileChangeEvent = null; 148 | 149 | if (event.kind() == ENTRY_CREATE) { 150 | 151 | 152 | if (isDirectory(filePath, NOFOLLOW_LINKS)) { 153 | registerAll(filePath); 154 | } 155 | 156 | try { 157 | FileInfo fileInfo = wsMgr.getFileInfo(ws, relativePath.toString()); 158 | fileChangeEvent = new FileCreateEvent(ws.getSpaceKey(), fileInfo); 159 | 160 | } catch (NoSuchFileException e) { 161 | log.debug("file " + fileName + " not found.", e); 162 | } 163 | 164 | 165 | } else if (event.kind() == ENTRY_MODIFY) { 166 | 167 | try { 168 | FileInfo fileInfo = wsMgr.getFileInfo(ws, relativePath.toString()); 169 | fileChangeEvent = new FileModifyEvent(ws.getSpaceKey(), fileInfo); 170 | } catch (NoSuchFileException e) { 171 | log.debug("file " + fileName + " not found.", e); 172 | } 173 | 174 | 175 | } else if (event.kind() == ENTRY_DELETE) { 176 | 177 | FileInfo fileInfo = new FileInfo(); 178 | fileInfo.setName(fileName.toString()); 179 | fileInfo.setPath(path); 180 | fileChangeEvent = new FileDeleteEvent(ws.getSpaceKey(), fileInfo); 181 | 182 | watchedPathStore.remove(ws.getSpaceKey(), path); 183 | 184 | } 185 | 186 | if (fileChangeEvent != null) { 187 | synchronized (debouncer) { 188 | debouncer.call(fileChangeEvent); 189 | } 190 | } 191 | 192 | 193 | } 194 | // reset key and remove from set if directory no longer accessible 195 | boolean valid = watchKey.reset(); 196 | if (!valid) { 197 | keys.remove(watchKey); 198 | // all directories are inaccessible 199 | if (keys.isEmpty()) { 200 | // 此处有问题 201 | break; 202 | } 203 | } 204 | 205 | } catch (ClosedWatchServiceException e) { 206 | // ignore 207 | } catch (Exception e) { 208 | e.printStackTrace(); 209 | } 210 | } 211 | this.debouncer.terminate(); 212 | 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/tty/ProcessTtyConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.tty; 6 | 7 | import com.pty4j.PtyProcess; 8 | import com.pty4j.WinSize; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.InputStreamReader; 13 | import java.io.OutputStream; 14 | import java.nio.charset.Charset; 15 | 16 | /** 17 | * Created by tan on 16/8/9. 18 | */ 19 | public class ProcessTtyConnector implements TtyConnector { 20 | 21 | private final InputStreamReader reader; 22 | 23 | private final InputStream inputStream; 24 | private final OutputStream outputStream; 25 | 26 | private PtyProcess process; 27 | 28 | protected Charset charset; 29 | 30 | public ProcessTtyConnector(PtyProcess process, Charset charset) { 31 | this.process = process; 32 | this.charset = charset; 33 | this.inputStream = process.getInputStream(); 34 | this.outputStream = process.getOutputStream(); 35 | this.reader = new InputStreamReader(inputStream, charset); 36 | } 37 | 38 | public void write(byte[] bytes) throws IOException { 39 | outputStream.write(bytes); 40 | outputStream.flush(); 41 | } 42 | 43 | public void write(String s) throws IOException { 44 | write(s.getBytes(charset)); 45 | } 46 | 47 | public String read() throws IOException { 48 | StringBuilder sb = new StringBuilder(); 49 | 50 | char[] buf = new char[1024]; 51 | 52 | do { 53 | int len = reader.read(buf, 0, buf.length); 54 | 55 | if (len > 0) { 56 | sb.append(buf, 0, len); 57 | } 58 | } while (reader.ready()); 59 | 60 | return sb.toString(); 61 | } 62 | 63 | public void resize(Integer cols, Integer rows) { 64 | process.setWinSize(new WinSize(cols, rows)); 65 | } 66 | 67 | @Override 68 | public void close() { 69 | process.destroy(); 70 | try { 71 | outputStream.close(); 72 | } 73 | catch (IOException ignored) { } 74 | try { 75 | inputStream.close(); 76 | } 77 | catch (IOException ignored) { } 78 | } 79 | 80 | @Override 81 | public boolean isConnected() { 82 | return process.isRunning(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/tty/SocketIOHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.tty; 6 | 7 | import com.google.common.collect.HashMultimap; 8 | import com.google.common.collect.Maps; 9 | import com.google.common.collect.Multimap; 10 | import com.google.gson.Gson; 11 | import lombok.extern.slf4j.Slf4j; 12 | import net.coding.ide.model.Message; 13 | import org.atmosphere.cache.UUIDBroadcasterCache; 14 | import org.atmosphere.config.service.AtmosphereHandlerService; 15 | import org.atmosphere.cpr.AtmosphereResource; 16 | import org.atmosphere.socketio.SocketIOSessionOutbound; 17 | import org.atmosphere.socketio.cache.SocketIOBroadcasterCache; 18 | import org.atmosphere.socketio.cpr.SocketIOAtmosphereHandler; 19 | import org.atmosphere.socketio.cpr.SocketIOAtmosphereInterceptor; 20 | import org.atmosphere.socketio.transport.DisconnectReason; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | import java.io.File; 25 | import java.io.IOException; 26 | import java.util.Collection; 27 | import java.util.Map; 28 | import java.util.concurrent.ConcurrentHashMap; 29 | import java.util.concurrent.ConcurrentMap; 30 | 31 | import static java.lang.String.format; 32 | 33 | /** 34 | * Simple SocketIOAtmosphereHandler that implements the logic to build a 35 | * SocketIO Chat application. 36 | */ 37 | @Slf4j 38 | @AtmosphereHandlerService( 39 | path = "/*", 40 | supportSession = true, 41 | broadcasterCache = UUIDBroadcasterCache.class, 42 | interceptors = SocketIOAtmosphereInterceptor.class) 43 | public class SocketIOHandler extends SocketIOAtmosphereHandler { 44 | private final Gson gson = new Gson(); 45 | private ConcurrentMap connectors = new ConcurrentHashMap<>(); 46 | private Multimap sessions = HashMultimap.create(); 47 | 48 | public void onConnect(AtmosphereResource r, SocketIOSessionOutbound outbound) throws IOException { 49 | log.debug("onConnect"); 50 | outbound.sendMessage("0{\"sid\":\"" + outbound.getSessionId() + "\",\"upgrades\":[],\"pingInterval\":25000,\"pingTimeout\":60000}"); 51 | } 52 | 53 | public void onMessage(AtmosphereResource r, SocketIOSessionOutbound outbound, String message) { 54 | if (outbound == null || message == null || message.length() == 0) { 55 | return; 56 | } 57 | 58 | try { 59 | Message msg = gson.fromJson(message, Message.class); 60 | 61 | String name = msg.getName(); 62 | 63 | String spaceHome = r.getAtmosphereConfig().getInitParameter("SPACE_HOME", null); 64 | 65 | if (spaceHome == null) { 66 | log.error("SocketIOHandler on message error: SPACE_HOME must be setted in env"); 67 | return; 68 | } 69 | 70 | Message.Arg arg = msg.getArgs().get(0); 71 | 72 | switch (name) { 73 | case "term.open": 74 | itermOpen(outbound, spaceHome, arg); 75 | break; 76 | case "term.input": 77 | itermInput(outbound, arg); 78 | break; 79 | case "term.resize": 80 | itermResize(outbound, arg); 81 | break; 82 | case "term.close": 83 | itermClose(outbound, arg); 84 | break; 85 | default: 86 | System.out.println("command " + name + " not found."); 87 | break; 88 | } 89 | } catch (Exception e) { 90 | log.error("SocketIOHandler onMessage error, message => {}, error => {}", message, e.getMessage()); 91 | } 92 | } 93 | 94 | private void itermClose(SocketIOSessionOutbound outbound, Message.Arg arg) { 95 | String key = makeConnectorKey(outbound.getSessionId(), arg.getId()); 96 | 97 | TerminalEmulator emulator = connectors.get(key); 98 | if (emulator != null) { 99 | emulator.close(); 100 | } 101 | 102 | connectors.remove(key); 103 | sessions.remove(outbound.getSessionId(), arg.getId()); 104 | } 105 | 106 | private void itermResize(SocketIOSessionOutbound outbound, Message.Arg arg) { 107 | String key = makeConnectorKey(outbound.getSessionId(), arg.getId()); 108 | TerminalEmulator emulator = connectors.get(key); 109 | 110 | if (emulator != null) { 111 | emulator.resize(arg.getCols(), arg.getRows()); 112 | } 113 | } 114 | 115 | private String getWorkdingDir(String spaceHome, String spaceKey) { 116 | return format("%s/%s/%s", spaceHome, spaceKey, "/working-dir"); 117 | } 118 | 119 | private String makeConnectorKey(String session, String termId) { 120 | return session + "-" + termId; 121 | } 122 | 123 | public void itermOpen(SocketIOSessionOutbound outbound, String spaceHome, Message.Arg arg) { 124 | try { 125 | Map envs = Maps.newHashMap(System.getenv()); 126 | 127 | 128 | String shell = envs.get("SHELL"); 129 | 130 | if (shell == null) { 131 | shell = "/bin/bash"; 132 | } 133 | 134 | String[] command = new String[]{shell, "--login"}; 135 | envs.put("TERM", "xterm"); 136 | 137 | TerminalEmulator emulator = new TerminalEmulator(arg.getId(), 138 | outbound, 139 | command, 140 | envs, 141 | getWorkdingDir(spaceHome, arg.getSpaceKey())); 142 | 143 | String key = makeConnectorKey(outbound.getSessionId(), arg.getId()); 144 | 145 | connectors.put(key, emulator); 146 | sessions.put(outbound.getSessionId(), arg.getId()); 147 | 148 | emulator.start(); 149 | } catch (Exception e) { 150 | e.printStackTrace(); 151 | } 152 | } 153 | 154 | public void itermInput(SocketIOSessionOutbound outbound, Message.Arg arg) throws IOException { 155 | String key = makeConnectorKey(outbound.getSessionId(), arg.getId()); 156 | TerminalEmulator emulator = connectors.get(key); 157 | 158 | if (emulator != null) { 159 | connectors.get(key).write(arg.getInput()); 160 | } 161 | } 162 | 163 | public void onDisconnect(AtmosphereResource r, SocketIOSessionOutbound outbound, DisconnectReason reason) { 164 | log.debug("ondisconnect, reason is :" + reason); 165 | 166 | String sessionId = outbound.getSessionId(); 167 | 168 | Collection termIds = sessions.get(sessionId); 169 | 170 | for (String termId : termIds) { 171 | String key = makeConnectorKey(sessionId, termId); 172 | 173 | TerminalEmulator emulator = connectors.get(key); 174 | if (emulator != null) { 175 | emulator.close(); 176 | connectors.remove(key); 177 | } 178 | } 179 | 180 | sessions.removeAll(sessionId); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/tty/TerminalEmulator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.tty; 6 | 7 | import com.google.gson.Gson; 8 | import com.pty4j.PtyProcess; 9 | import lombok.extern.log4j.Log4j; 10 | import net.coding.ide.model.Message; 11 | import org.atmosphere.socketio.SocketIOException; 12 | import org.atmosphere.socketio.SocketIOSessionOutbound; 13 | import org.atmosphere.socketio.transport.SocketIOPacketImpl; 14 | 15 | import java.io.IOException; 16 | import java.nio.charset.Charset; 17 | import java.util.Arrays; 18 | import java.util.Map; 19 | 20 | /** 21 | * Created by tan on 16/8/11. 22 | */ 23 | @Log4j 24 | public class TerminalEmulator extends Thread { 25 | 26 | Charset charset = Charset.forName("UTF-8"); 27 | 28 | private TtyConnector ttyConnector; 29 | 30 | private String termId; 31 | 32 | private SocketIOSessionOutbound outbound; 33 | 34 | private Gson gson = new Gson(); 35 | 36 | public TerminalEmulator(String termId, SocketIOSessionOutbound outbound, String[]command, Map envs, String workingDirectory) throws IOException { 37 | PtyProcess pty = PtyProcess.exec(command, envs, workingDirectory);// working dir 38 | 39 | this.termId = termId; 40 | this.outbound = outbound; 41 | this.ttyConnector = new ProcessTtyConnector(pty, charset); 42 | } 43 | 44 | public void run() { 45 | while (!Thread.interrupted() && ttyConnector.isConnected()) { 46 | try { 47 | String s = ttyConnector.read(); 48 | 49 | Message message = new Message(); 50 | message.setName("shell.output"); 51 | 52 | Message.Arg a = new Message.Arg(); 53 | a.setOutput(s); 54 | a.setId(termId); 55 | 56 | message.setArgs(Arrays.asList(a)); 57 | 58 | outbound.sendMessage(new SocketIOPacketImpl(SocketIOPacketImpl.PacketType.EVENT, gson.toJson(message))); 59 | } catch (Exception e) { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | ttyConnector.close(); 65 | 66 | log.info("pty exit success"); 67 | } 68 | 69 | public void write(String msg) throws IOException { 70 | ttyConnector.write(msg); 71 | } 72 | 73 | public void resize(Integer cols, Integer rows) { 74 | ttyConnector.resize(cols, rows); 75 | } 76 | 77 | public void close() { 78 | interrupt(); 79 | 80 | Message message = new Message(); 81 | message.setName("shell.exit"); 82 | 83 | Message.Arg a = new Message.Arg(); 84 | a.setId(termId); 85 | 86 | message.setArgs(Arrays.asList(a)); 87 | 88 | try { 89 | outbound.sendMessage(new SocketIOPacketImpl(SocketIOPacketImpl.PacketType.EVENT, gson.toJson(message))); 90 | } catch (SocketIOException e) { 91 | log.error("TerminalEmulator occurs error when closing", e); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/tty/TtyConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.tty; 6 | 7 | import java.io.IOException; 8 | 9 | /** 10 | * Created by tan on 16/8/9. 11 | */ 12 | public interface TtyConnector { 13 | 14 | void write(byte[] bytes) throws IOException; 15 | 16 | void write(String s) throws IOException; 17 | 18 | String read() throws IOException; 19 | 20 | void close(); 21 | 22 | void resize(Integer cols, Integer rows); 23 | 24 | boolean isConnected(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/AsyncResult.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | /** 8 | * Created by mingshun on 3/30/15. 9 | */ 10 | public class AsyncResult { 11 | private final Object lock = new Object(); 12 | private T result; 13 | private boolean done; 14 | 15 | public T get() { 16 | synchronized (this.lock) { 17 | try { 18 | this.lock.wait(30000); 19 | } catch (InterruptedException e) { 20 | // Ignore 21 | } 22 | 23 | return result; 24 | } 25 | } 26 | 27 | public void set(T result) { 28 | synchronized (this.lock) { 29 | this.result = result; 30 | this.done = true; 31 | this.lock.notify(); 32 | } 33 | } 34 | 35 | public boolean isDone() { 36 | synchronized (this.lock) { 37 | return this.done; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/Callback.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | /** 8 | * Created by phy on 2015/3/7. 9 | */ 10 | public interface Callback { 11 | public void call(T arg); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/CookieStoreHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import org.apache.http.client.CookieStore; 8 | import org.apache.http.impl.client.BasicCookieStore; 9 | 10 | /** 11 | * Created by vangie on 14/12/23. 12 | */ 13 | public class CookieStoreHolder { 14 | 15 | private static final ThreadLocal cookieStoreHolder = new ThreadLocal(); 16 | 17 | public static void clearCookieStore() { 18 | cookieStoreHolder.remove(); 19 | } 20 | 21 | public static CookieStore getCookieStore(boolean createIfNeed) { 22 | CookieStore cookieStore = cookieStoreHolder.get(); 23 | 24 | if(createIfNeed && cookieStore == null){ 25 | cookieStore = new BasicCookieStore(); 26 | cookieStoreHolder.set(cookieStore); 27 | } 28 | 29 | return cookieStore; 30 | } 31 | 32 | public static void setCookieStore(CookieStore cookieStore) { 33 | cookieStoreHolder.set(cookieStore); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/Debouncer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | 8 | 9 | import java.util.concurrent.ConcurrentHashMap; 10 | import java.util.concurrent.Executors; 11 | import java.util.concurrent.ScheduledExecutorService; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | /** 15 | * Created by phy on 2015/3/7. 16 | */ 17 | 18 | public class Debouncer implements Callback{ 19 | private final ScheduledExecutorService sched = Executors.newScheduledThreadPool(1); 20 | private final ConcurrentHashMap delayedMap = new ConcurrentHashMap(); 21 | private final Callback callback; 22 | private final int interval; 23 | 24 | public Debouncer(Callback c, int interval) { 25 | this.callback = c; 26 | this.interval = interval; 27 | } 28 | 29 | @Override 30 | public void call(T key) { 31 | TimerTask task = new TimerTask(key); 32 | 33 | TimerTask prev; 34 | do { 35 | prev = delayedMap.putIfAbsent(key, task); 36 | if (prev == null) 37 | sched.schedule(task, interval, TimeUnit.MILLISECONDS); 38 | } 39 | while (prev != null && !prev.extend()); // Exit only if new task was added to map, or existing task was extended successfully 40 | } 41 | 42 | public void terminate() { 43 | sched.shutdownNow(); 44 | } 45 | 46 | 47 | // The task that wakes up when the wait time elapses 48 | private class TimerTask implements Runnable { 49 | private final T key; 50 | private long dueTime; 51 | private final Object lock = new Object(); 52 | 53 | public TimerTask(T key) { 54 | this.key = key; 55 | extend(); 56 | } 57 | 58 | public boolean extend() { 59 | synchronized (lock) { 60 | if (dueTime < 0) // Task has been shutdown 61 | return false; 62 | dueTime = System.currentTimeMillis() + interval; 63 | return true; 64 | } 65 | } 66 | 67 | public void run() { 68 | synchronized (lock) { 69 | long remaining = dueTime - System.currentTimeMillis(); 70 | if (remaining > 0) { // Re-schedule task 71 | sched.schedule(this, remaining, TimeUnit.MILLISECONDS); 72 | } else { // Mark as terminated and invoke callback 73 | dueTime = -1; 74 | try { 75 | callback.call(key); 76 | } finally { 77 | delayedMap.remove(key); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import com.google.common.collect.Maps; 8 | import org.apache.tika.Tika; 9 | 10 | import javax.activation.MimetypesFileTypeMap; 11 | import java.io.File; 12 | import java.util.Map; 13 | 14 | /** 15 | * Created by phy on 2015/1/27. 16 | */ 17 | public abstract class FileUtil{ 18 | private static MimetypesFileTypeMap mfm = new MimetypesFileTypeMap(); 19 | 20 | private static Map contentTypeMap = Maps.newHashMap(); 21 | 22 | private static Tika tika = new Tika(); 23 | 24 | static { 25 | contentTypeMap.put(".gitignore", "text/plain"); 26 | contentTypeMap.put(".bowerrc", "text/plain"); 27 | contentTypeMap.put(".editorconfig", "text/plain"); 28 | contentTypeMap.put(".gitattributes", "text/plain"); 29 | contentTypeMap.put("Rakefile", "text/x-extension-rake"); 30 | contentTypeMap.put("Dockerfile", "text/x-extension-docker"); 31 | contentTypeMap.put("Gemfile", "text/x-ruby-bundler-gemfile"); 32 | contentTypeMap.put("Gemfile.lock", "text/x-ruby-bundler-gemfile-lock"); 33 | } 34 | 35 | public static String getContentType(File file) { 36 | 37 | if (!file.isDirectory()){ 38 | String filename = file.getName(); 39 | 40 | if (contentTypeMap.containsKey(filename)) { 41 | return contentTypeMap.get(filename); 42 | } else if (filename.indexOf('.') != -1){ 43 | String contentType = mfm.getContentType(filename); 44 | if (!contentType.equals("application/octet-stream")){ 45 | return contentType; 46 | } else return detectContentTypeByContent(file); 47 | } else{ 48 | return detectContentTypeByContent(file); 49 | } 50 | } else { 51 | return null; 52 | } 53 | 54 | } 55 | 56 | private static String detectContentTypeByContent(File file) { 57 | if (file.length() == 0){ 58 | return "text/plain"; 59 | } else { 60 | try { 61 | return tika.detect(file); 62 | } catch (Exception e) { 63 | return "application/octet-stream"; 64 | } 65 | } 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/HostMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * Created by vangie on 15/1/28. 12 | */ 13 | public class HostMatcher { 14 | 15 | private Pattern hostPattern; 16 | 17 | public HostMatcher(String host) { 18 | this.hostPattern = buildPattern(host); 19 | } 20 | 21 | public boolean matches(HttpServletRequest request) { 22 | String host = request.getHeader("Host"); 23 | if(host == null){ 24 | return false; 25 | } 26 | return hostPattern.matcher(host).matches(); 27 | } 28 | 29 | private Pattern buildPattern(String host){ 30 | return Pattern.compile(host.replaceAll("[.]","[.]").replaceAll("[*]",".+")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/KeyUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import com.google.common.collect.Lists; 8 | import com.google.common.hash.HashCode; 9 | import com.google.gson.JsonArray; 10 | import com.google.gson.JsonElement; 11 | import com.google.gson.JsonObject; 12 | import lombok.extern.slf4j.Slf4j; 13 | import net.coding.ide.service.KeyManagerImpl; 14 | import org.apache.commons.codec.binary.Base64; 15 | import org.apache.commons.lang.StringUtils; 16 | import org.bouncycastle.openssl.jcajce.JcaPEMWriter; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | 20 | import java.io.ByteArrayOutputStream; 21 | import java.io.CharArrayWriter; 22 | import java.io.DataOutputStream; 23 | import java.io.IOException; 24 | import java.security.interfaces.RSAPrivateKey; 25 | import java.security.interfaces.RSAPublicKey; 26 | import java.util.List; 27 | import java.util.function.Function; 28 | 29 | import static com.google.common.base.Joiner.on; 30 | import static com.google.common.base.Splitter.fixedLength; 31 | import static com.google.common.hash.Hashing.md5; 32 | import static com.google.common.io.BaseEncoding.base16; 33 | import static com.google.common.io.BaseEncoding.base64; 34 | import static java.lang.String.format; 35 | 36 | /** 37 | * Created by tan on 16-2-26. 38 | */ 39 | @Slf4j 40 | public class KeyUtils { 41 | /** 42 | * href="http://tools.ietf.org/html/draft-friedl-secsh-fingerprint-00" >spec 43 | * 44 | * @return hex fingerprint ex. {@code 2b:a9:62:95:5b:8b:1d:61:e0:92:f7:03:10:e9:db:d9} 45 | */ 46 | public static String fingerprint(String publicKey) { 47 | String[] contents = StringUtils.split(publicKey, " "); 48 | byte[] contentBase64 = base64().decode(contents[1]); 49 | HashCode hc = md5().hashBytes(contentBase64); 50 | return on(":").join(fixedLength(2).split(base16().lowerCase().encode(hc.asBytes()))); 51 | } 52 | 53 | public static String keyToString(RSAPublicKey pubKey, String username) throws IOException { 54 | ByteArrayOutputStream byteOs = new ByteArrayOutputStream(); 55 | DataOutputStream dos = new DataOutputStream(byteOs); 56 | 57 | List bytesList = Lists.newArrayList( 58 | "ssh-rsa".getBytes(), 59 | pubKey.getPublicExponent().toByteArray(), 60 | pubKey.getModulus().toByteArray() 61 | ); 62 | 63 | for (byte[] bytes : bytesList) { 64 | dos.writeInt(bytes.length); 65 | dos.write(bytes); 66 | } 67 | 68 | dos.close(); 69 | 70 | String publicKeyEncoded = new String(Base64.encodeBase64(byteOs.toByteArray())); 71 | 72 | return format("ssh-rsa %s %s@WebIDE", publicKeyEncoded, username); 73 | } 74 | 75 | public static String keyToString(RSAPrivateKey privateKey) { 76 | CharArrayWriter caw = new CharArrayWriter(); 77 | JcaPEMWriter pemWriter = new JcaPEMWriter(caw); 78 | try { 79 | pemWriter.writeObject(privateKey); 80 | } catch (IOException e) { 81 | log.error("", e); 82 | } finally { 83 | try { 84 | pemWriter.close(); 85 | } catch (IOException e) { 86 | log.error("", e); 87 | } 88 | } 89 | return caw.toString().trim(); 90 | } 91 | 92 | public static boolean isKeyExist(JsonArray keys, String key, Function keyExtractor) { 93 | 94 | if (key == null) return false; 95 | 96 | for (JsonElement k : keys) { 97 | if (key.equals(keyExtractor.apply(k.getAsJsonObject()))) { 98 | return true; 99 | } 100 | } 101 | 102 | return false; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/ProjectUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | /** 8 | * Created by vangie on 16/2/2. 9 | */ 10 | public abstract class ProjectUtil { 11 | 12 | public static String randomIcon(){ 13 | int min = 1, max = 24; 14 | 15 | return String.format("https://coding.net/static/project_icon/scenery-%d.png", 16 | min + (int) (Math.random() * ((max - min) + 1))); 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/RandomGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | /** 8 | * Created by phy on 2014/12/9. 9 | */ 10 | public interface RandomGenerator { 11 | String generate(Object... args); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/RebaseStateUtils.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.utils; 2 | 3 | import org.eclipse.jgit.api.RebaseCommand; 4 | import org.eclipse.jgit.lib.Constants; 5 | import org.eclipse.jgit.lib.Repository; 6 | import org.eclipse.jgit.util.IO; 7 | import org.eclipse.jgit.util.RawParseUtils; 8 | 9 | import java.io.File; 10 | import java.io.FileOutputStream; 11 | import java.io.IOException; 12 | 13 | /** 14 | * Created by tan on 28/03/2017. 15 | * 16 | * @see RebaseCommand.RebaseState 17 | */ 18 | public class RebaseStateUtils { 19 | 20 | /** 21 | * The name of the "rebase-apply" folder for non-interactive rebases. 22 | */ 23 | private static final String REBASE_APPLY = "rebase-apply"; //$NON-NLS-1$ 24 | 25 | /** 26 | * The name of the "rebase-merge" folder for interactive rebases. 27 | */ 28 | private static final String REBASE_MERGE = "rebase-merge"; //$NON-NLS-1$ 29 | 30 | public static final File getRebaseStateDir(Repository repository) { 31 | File file = null; 32 | 33 | File rebaseApply = new File(repository.getDirectory(), REBASE_APPLY); 34 | if (rebaseApply.exists()) { 35 | file = rebaseApply; 36 | } else { 37 | file = new File(repository.getDirectory(), REBASE_MERGE); 38 | } 39 | 40 | return file; 41 | } 42 | 43 | public static File getRebaseFile(Repository repository, String name) { 44 | return new File(getRebaseStateDir(repository), name); 45 | } 46 | 47 | public static String readFile(File directory, String fileName) 48 | throws IOException { 49 | byte[] content = IO.readFully(new File(directory, fileName)); 50 | // strip off the last LF 51 | int end = RawParseUtils.prevLF(content, content.length); 52 | return RawParseUtils.decode(content, 0, end + 1); 53 | } 54 | 55 | public static String getRebasePath(Repository repository, String name) { 56 | return (getRebaseStateDir(repository).getName() + "/" + name); //$NON-NLS-1$ 57 | } 58 | 59 | public static void createFile(Repository repository, String name, String content) throws IOException { 60 | createFile(getRebaseStateDir(repository), name, content); 61 | } 62 | 63 | public static void createFile(File parentDir, String name, 64 | String content) 65 | throws IOException { 66 | File file = new File(parentDir, name); 67 | FileOutputStream fos = new FileOutputStream(file); 68 | try { 69 | fos.write(content.getBytes(Constants.CHARACTER_ENCODING)); 70 | fos.write('\n'); 71 | } finally { 72 | fos.close(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/RebaseTodoUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import net.coding.ide.model.RebaseResponse; 8 | import org.eclipse.jgit.lib.AbbreviatedObjectId; 9 | import org.eclipse.jgit.lib.RebaseTodoLine; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by tan on 16-4-8. 16 | */ 17 | public class RebaseTodoUtils { 18 | 19 | public static List loadFrom(List lines) { 20 | List rebaseTodoLines = new ArrayList<>(); 21 | 22 | for (RebaseTodoLine line : lines) { 23 | RebaseResponse.RebaseTodoLine rebaseTodoLine = new RebaseResponse.RebaseTodoLine( 24 | line.getAction().name(), 25 | line.getCommit().name(), 26 | line.getShortMessage()); 27 | 28 | rebaseTodoLines.add(rebaseTodoLine); 29 | } 30 | 31 | return rebaseTodoLines; 32 | } 33 | 34 | public static List parseLines(List lines) { 35 | List rebaseTodoLines = new ArrayList<>(); 36 | 37 | for (RebaseResponse.RebaseTodoLine line : lines) { 38 | RebaseTodoLine rebaseTodoLine = new RebaseTodoLine( 39 | RebaseTodoLine.Action.valueOf(line.getAction().name()), 40 | AbbreviatedObjectId.fromString(line.getCommit()), 41 | line.getShortMessage()); 42 | 43 | rebaseTodoLines.add(rebaseTodoLine); 44 | } 45 | 46 | return rebaseTodoLines; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/ShortKeyGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import org.apache.commons.codec.digest.DigestUtils; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.Random; 11 | import java.util.UUID; 12 | 13 | /** 14 | * Created by phy on 2014/12/3. 15 | */ 16 | @Component 17 | public class ShortKeyGenerator implements RandomGenerator { 18 | 19 | private String[] chars = new String[]{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", 20 | "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" 21 | }; 22 | private int[] primes = new int[]{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 23 | 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 24 | 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 25 | 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 26 | 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 27 | 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 28 | 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 29 | 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 30 | 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 31 | 967, 971, 977, 983, 991, 997}; 32 | 33 | public String generate(Object... args) { 34 | int len = args.length; 35 | 36 | long[] seeds = new long[len]; 37 | 38 | for (int i = 0; i < len; i++) { 39 | Object arg = args[i]; 40 | if (arg instanceof Number) { 41 | seeds[i] = ((Number) arg).longValue(); 42 | } else { 43 | seeds[i] = arg.hashCode(); 44 | } 45 | } 46 | 47 | long sum = 0; 48 | for (int i = 0; i < args.length; i++) { 49 | sum += seeds[i] * prime(); 50 | } 51 | String salt = String.valueOf(UUID.randomUUID()); 52 | String source = String.valueOf(sum) + salt; 53 | String encryptResult = DigestUtils.md5Hex(source); 54 | String subString = encryptResult.substring(0, 12); 55 | Long hexLong = Long.parseLong(subString, 16); 56 | 57 | String result = ""; 58 | for (int i = 0; i < 6; i++) { 59 | // long index = 0x00000033 & hexLong; 60 | long index = hexLong % 26; 61 | result += chars[(int) index]; 62 | hexLong = hexLong >> 8; 63 | } 64 | return result; 65 | } 66 | 67 | private int prime() { 68 | Random random = new Random(); 69 | int idx = random.nextInt(primes.length); 70 | return primes[idx]; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/TemporaryFileFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import com.google.common.base.Predicate; 8 | import com.google.common.collect.Iterables; 9 | import com.google.common.collect.Lists; 10 | import net.coding.ide.model.FileInfo; 11 | 12 | import java.util.List; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * Created by mingshun on 15-8-26. 17 | */ 18 | public class TemporaryFileFilter { 19 | private static final Pattern TEMPORARY_FILE_PATTERNS[] = { 20 | Pattern.compile("^\\..+\\.sw[a-z]$"), 21 | Pattern.compile("^\\.sw[a-z]$") 22 | }; 23 | 24 | public static List filter(List fileInfos) { 25 | return Lists.newArrayList(Iterables.filter(fileInfos, new Predicate() { 26 | 27 | @Override 28 | public boolean apply(FileInfo fileInfo) { 29 | String name = fileInfo.getName(); 30 | for (Pattern pattern : TEMPORARY_FILE_PATTERNS) { 31 | if (pattern.matcher(name).matches()) { 32 | return false; 33 | } 34 | } 35 | return true; 36 | } 37 | 38 | })); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/utils/WildcardMatcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | /** 11 | * Created by mingshun on 7/24/15. 12 | */ 13 | public class WildcardMatcher { 14 | private static final String metaCharacters = "(){}[]-^$.?*+|\\"; 15 | 16 | private static String toRegexPattern(String wildcardPattern) { 17 | StringBuilder sb = new StringBuilder(); 18 | for (char p : wildcardPattern.toCharArray()) { 19 | boolean contain = false; 20 | for (char m : metaCharacters.toCharArray()) { 21 | if (p == m) { 22 | contain = true; 23 | break; 24 | } 25 | } 26 | 27 | if (contain) { 28 | switch (p) { 29 | case '*': 30 | sb.append(".*"); 31 | break; 32 | 33 | default: 34 | sb.append('\\').append(p); 35 | } 36 | } else { 37 | sb.append(p); 38 | } 39 | } 40 | 41 | return sb.toString(); 42 | } 43 | 44 | public static boolean match(String text, String wildcardPattern) { 45 | Pattern pattern = Pattern.compile(toRegexPattern(wildcardPattern), Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); 46 | Matcher matcher = pattern.matcher(text); 47 | return matcher.find(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/controller/PackageController.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.web.controller; 2 | 3 | import net.coding.ide.dto.Package; 4 | import net.coding.ide.service.PackageService; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import java.io.IOException; 12 | import java.util.List; 13 | 14 | /** 15 | * Created by tan on 09/03/2017. 16 | */ 17 | @RequestMapping("/packages") 18 | @RestController 19 | public class PackageController { 20 | 21 | @Autowired 22 | private PackageService packageService; 23 | 24 | @RequestMapping(value = "/{name}/{version}/{fileName:.+}", method = RequestMethod.GET, produces = "text/plain") 25 | public String readPackageFile(@PathVariable String name, 26 | @PathVariable String version, 27 | @PathVariable String fileName) throws IOException { 28 | return packageService.readPackageFile(name, version, fileName); 29 | } 30 | 31 | @RequestMapping(value = "", method = RequestMethod.GET) 32 | public List packages() { 33 | return packageService.findAll(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/controller/ProjectController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.controller; 6 | 7 | import com.google.gson.JsonObject; 8 | import io.swagger.annotations.ApiOperation; 9 | import net.coding.ide.dto.ProjectDTO; 10 | import net.coding.ide.service.ProjectService; 11 | import org.modelmapper.ModelMapper; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.web.bind.annotation.RequestMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import java.util.List; 17 | import java.util.stream.Collectors; 18 | 19 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 20 | import static org.springframework.web.bind.annotation.RequestMethod.GET; 21 | 22 | /** 23 | * Created by vangie on 15/12/23. 24 | */ 25 | 26 | @RestController 27 | public class ProjectController { 28 | 29 | @Autowired 30 | private ModelMapper mapper; 31 | 32 | @Autowired 33 | private ProjectService prjSrv; 34 | 35 | @ApiOperation(value = "返回用户的所有项目", httpMethod = "GET", response = JsonObject.class, notes = "返回用户所有的项目") 36 | @RequestMapping(value = "/projects", method = GET, produces = APPLICATION_JSON_VALUE) 37 | public List projects() { 38 | 39 | return prjSrv.projects().stream() 40 | .map(p -> mapper.map(p, ProjectDTO.class)) 41 | .collect(Collectors.toList()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/controller/UserController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.controller; 6 | 7 | import net.coding.ide.dto.KeyDTO; 8 | import net.coding.ide.dto.UserDTO; 9 | import net.coding.ide.model.Key; 10 | import net.coding.ide.service.KeyManager; 11 | import org.modelmapper.ModelMapper; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.beans.factory.annotation.Value; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RestController; 16 | 17 | import java.io.IOException; 18 | 19 | import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; 20 | import static org.springframework.web.bind.annotation.RequestMethod.GET; 21 | 22 | /** 23 | * Created by vangie on 15/7/21. 24 | */ 25 | 26 | @RestController 27 | @RequestMapping(value = "/user", produces = APPLICATION_JSON_VALUE) 28 | public class UserController { 29 | 30 | @Autowired 31 | private KeyManager keyMgr; 32 | 33 | @Autowired 34 | private ModelMapper mapper; 35 | 36 | @Value("${USERNAME}") 37 | private String username; 38 | 39 | @Value("${AVATAR}") 40 | private String avatar; 41 | 42 | @RequestMapping(method = GET, params = "public_key") 43 | public KeyDTO publicKey() throws IOException { 44 | 45 | Key key; 46 | 47 | if (!keyMgr.isKeyExist()) { 48 | key = keyMgr.generateKey(); 49 | } else { 50 | key = keyMgr.getKey(); 51 | } 52 | 53 | return mapper.map(key, KeyDTO.class); 54 | } 55 | 56 | @RequestMapping(value = "", method = GET) 57 | public UserDTO currentUser() { 58 | return UserDTO.of(username, avatar); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/controller/WorkspaceAdvice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.controller; 6 | 7 | import net.coding.ide.entity.WorkspaceEntity; 8 | import net.coding.ide.model.Workspace; 9 | import net.coding.ide.model.exception.WorkspaceMissingException; 10 | import net.coding.ide.service.WorkspaceManager; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.WebDataBinder; 13 | import org.springframework.web.bind.annotation.ControllerAdvice; 14 | import org.springframework.web.bind.annotation.InitBinder; 15 | 16 | import java.beans.PropertyEditorSupport; 17 | 18 | /** 19 | * Created by vangie on 15/3/7. 20 | */ 21 | @ControllerAdvice 22 | public class WorkspaceAdvice { 23 | 24 | @Autowired 25 | private WorkspaceManager wsMgr; 26 | 27 | @InitBinder 28 | public void initBinder(WebDataBinder binder) { 29 | binder.registerCustomEditor(Workspace.class, new WorkspaceEditor(wsMgr)); 30 | } 31 | 32 | public static class WorkspaceEditor extends PropertyEditorSupport { 33 | 34 | private final WorkspaceManager wsMgr; 35 | 36 | public WorkspaceEditor(WorkspaceManager wsMgr) { 37 | this.wsMgr = wsMgr; 38 | } 39 | 40 | @Override 41 | public void setAsText(String spaceKey) throws IllegalArgumentException { 42 | WorkspaceEntity wsEntity = wsMgr.getWorkspaceEntity(spaceKey); 43 | 44 | if (wsEntity == null) { 45 | throw new WorkspaceMissingException(String.format("workspace '%s' is not found.", spaceKey)); 46 | } 47 | 48 | Workspace ws = wsMgr.getWorkspace(spaceKey); 49 | 50 | if (ws == null) { 51 | ws = wsMgr.setup(spaceKey); 52 | } 53 | 54 | this.setValue(ws); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/mapping/PersonIdentConverter.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.web.mapping; 2 | 3 | import net.coding.ide.model.PersonIdent; 4 | import org.modelmapper.AbstractConverter; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * Created by tan on 17/05/2017. 9 | */ 10 | @Component 11 | public class PersonIdentConverter extends AbstractConverter { 12 | 13 | @Override 14 | protected PersonIdent convert(org.eclipse.jgit.lib.PersonIdent source) { 15 | 16 | return PersonIdent.builder() 17 | .name(source.getName()) 18 | .emailAddress(source.getEmailAddress()) 19 | .build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/message/EventExchange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.message; 6 | 7 | import com.fatboyindustrial.gsonjodatime.Converters; 8 | import com.google.gson.Gson; 9 | import com.google.gson.GsonBuilder; 10 | import com.google.gson.JsonObject; 11 | import lombok.extern.slf4j.Slf4j; 12 | import net.coding.ide.event.*; 13 | import net.coding.ide.model.FileInfo; 14 | import net.coding.ide.model.Workspace; 15 | import org.springframework.beans.BeansException; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.context.*; 18 | import org.springframework.context.event.EventListener; 19 | import org.springframework.messaging.Message; 20 | import org.springframework.messaging.simp.SimpMessagingTemplate; 21 | import org.springframework.messaging.simp.stomp.StompHeaderAccessor; 22 | import org.springframework.stereotype.Component; 23 | import org.springframework.web.socket.messaging.AbstractSubProtocolEvent; 24 | import org.springframework.web.socket.messaging.SessionConnectEvent; 25 | import org.springframework.web.socket.messaging.SessionDisconnectEvent; 26 | 27 | /** 28 | * Created by vangie on 15/2/4. 29 | */ 30 | @Slf4j 31 | @Component 32 | public class EventExchange { 33 | 34 | @Autowired 35 | private WebSocketSessionStore webSocketSessionStore; 36 | 37 | @Autowired 38 | private OnlineWorkspaceStore onlineWorkspaceStore; 39 | 40 | @Autowired 41 | private SimpMessagingTemplate simpMessagingTemplate; 42 | 43 | @Autowired 44 | private ApplicationEventPublisher eventPublisher; 45 | 46 | private Gson gson = Converters.registerDateTime(new GsonBuilder()).create(); 47 | 48 | @EventListener 49 | public void onSessionConnected(SessionConnectEvent event) { 50 | Message msg = event.getMessage(); 51 | StompHeaderAccessor accessor = StompHeaderAccessor.wrap(msg); 52 | String sessionId = accessor.getSessionId(); 53 | 54 | String spaceKey = (String) webSocketSessionStore.getAttribute(sessionId, "spaceKey"); 55 | 56 | log.debug("Session connect: spaceKey => {}, sessionId => {} ", spaceKey, sessionId); 57 | 58 | if (spaceKey == null) { 59 | return; 60 | } 61 | 62 | boolean isToBeOnline = onlineWorkspaceStore.isEmpty(spaceKey); 63 | onlineWorkspaceStore.addSession(spaceKey, sessionId); 64 | 65 | if (isToBeOnline) { 66 | eventPublisher.publishEvent(new WorkspaceOnlineEvent(event, spaceKey)); 67 | } 68 | } 69 | 70 | @EventListener 71 | public void onSessionDisConnected(SessionDisconnectEvent event) { 72 | Message msg = event.getMessage(); 73 | StompHeaderAccessor accessor = StompHeaderAccessor.wrap(msg); 74 | String sessionId = accessor.getSessionId(); 75 | 76 | String spaceKey = (String) webSocketSessionStore.getAttribute(sessionId, "spaceKey"); 77 | 78 | log.debug("Session disconnect: spaceKey => {}, sessionId => {} ", spaceKey, sessionId); 79 | 80 | if (spaceKey == null) { 81 | return; 82 | } 83 | 84 | onlineWorkspaceStore.removeSession(spaceKey, sessionId); 85 | boolean isToBeOffline = onlineWorkspaceStore.isEmpty(spaceKey); 86 | 87 | if (isToBeOffline) { 88 | eventPublisher.publishEvent(new WorkspaceOfflineEvent(event, spaceKey)); 89 | } 90 | 91 | } 92 | 93 | @EventListener 94 | public void onFileChanged(FileChangeEvent event) { 95 | String spaceKey = event.getSpaceKey(); 96 | FileInfo fileInfo = event.getFileInfo(); 97 | 98 | JsonObject jsonObj = new JsonObject(); 99 | jsonObj.add("fileInfo", gson.toJsonTree(fileInfo)); 100 | 101 | if (event instanceof FileCreateEvent) { 102 | jsonObj.addProperty("changeType", "create"); 103 | } else if (event instanceof FileModifyEvent) { 104 | jsonObj.addProperty("changeType", "modify"); 105 | } else if (event instanceof FileDeleteEvent) { 106 | jsonObj.addProperty("changeType", "delete"); 107 | } 108 | 109 | if (fileInfo.getLastModified() != null) { 110 | jsonObj.addProperty("lastModified", fileInfo.getLastModified().getMillis()); 111 | } 112 | 113 | log.debug("send file change event: {}", event); 114 | simpMessagingTemplate.convertAndSend("/topic/ws/" + spaceKey + "/change", jsonObj); 115 | } 116 | 117 | @EventListener 118 | public void onGitCheckout(GitCheckoutEvent event) { 119 | Workspace ws = event.getWorkspace(); 120 | String spaceKey = ws.getSpaceKey(); 121 | 122 | String branch = event.getBranch(); 123 | 124 | JsonObject jsonObj = new JsonObject(); 125 | jsonObj.addProperty("branch", branch); 126 | 127 | simpMessagingTemplate.convertAndSend("/topic/git/" + spaceKey + "/checkout", jsonObj); 128 | } 129 | 130 | @EventListener 131 | public void onWorkspaceDeleted(WorkspaceDeleteEvent event) { 132 | String spaceKey = event.getSpaceKey(); 133 | 134 | simpMessagingTemplate.convertAndSend("/topic/ws/" + spaceKey + "/delete", (Object) null); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/message/GsonMessageConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.message; 6 | 7 | import com.google.common.collect.Lists; 8 | import com.google.gson.Gson; 9 | import com.google.gson.JsonParseException; 10 | import com.google.gson.reflect.TypeToken; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.messaging.Message; 13 | import org.springframework.messaging.MessageHeaders; 14 | import org.springframework.messaging.converter.AbstractMessageConverter; 15 | import org.springframework.messaging.converter.MessageConversionException; 16 | import org.springframework.util.Assert; 17 | import org.springframework.util.MimeType; 18 | 19 | import java.io.*; 20 | import java.lang.reflect.Type; 21 | import java.nio.charset.Charset; 22 | import java.util.List; 23 | 24 | /** 25 | * Created by vangie on 15/2/12. 26 | */ 27 | public class GsonMessageConverter extends AbstractMessageConverter { 28 | 29 | public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); 30 | 31 | private static List mimeTypeList = Lists.newArrayList(); 32 | 33 | static { 34 | mimeTypeList.add(new MediaType("application", "json", DEFAULT_CHARSET)); 35 | mimeTypeList.add(new MediaType("application", "*+json", DEFAULT_CHARSET)); 36 | } 37 | 38 | private Gson gson = new Gson(); 39 | 40 | 41 | public GsonMessageConverter() { 42 | super(mimeTypeList); 43 | } 44 | 45 | 46 | /** 47 | * Set the {@code Gson} instance to use. 48 | * If not set, a default {@link Gson#Gson() Gson} instance is used. 49 | *

Setting a custom-configured {@code Gson} is one way to take further 50 | * control of the JSON serialization process. 51 | */ 52 | public void setGson(Gson gson) { 53 | Assert.notNull(gson, "'gson' is required"); 54 | this.gson = gson; 55 | } 56 | 57 | /** 58 | * Return the configured {@code Gson} instance for this converter. 59 | */ 60 | public Gson getGson() { 61 | return this.gson; 62 | } 63 | 64 | 65 | @Override 66 | protected boolean supports(Class clazz) { 67 | return true; 68 | } 69 | 70 | @Override 71 | public Object convertFromInternal(Message message, Class targetClass) { 72 | 73 | TypeToken token = getTypeToken(targetClass); 74 | 75 | Object payload = message.getPayload(); 76 | 77 | Charset charset = getCharset(getMimeType(message.getHeaders())); 78 | 79 | Reader reader; 80 | 81 | if (payload instanceof byte[]) { 82 | reader = new InputStreamReader(new ByteArrayInputStream((byte[]) payload), charset); 83 | } else { 84 | reader = new StringReader((String) payload); 85 | } 86 | 87 | try { 88 | 89 | return this.gson.fromJson(reader, token.getType()); 90 | 91 | } catch (JsonParseException ex) { 92 | throw new MessageConversionException(message, "Could not read JSON: " + ex.getMessage(), ex); 93 | } 94 | } 95 | 96 | 97 | @Override 98 | public Object convertToInternal(Object payload, MessageHeaders headers) { 99 | 100 | Charset charset = getCharset(getMimeType(headers)); 101 | try { 102 | if (byte[].class.equals(getSerializedPayloadClass())) { 103 | ByteArrayOutputStream out = new ByteArrayOutputStream(1024); 104 | OutputStreamWriter writer = new OutputStreamWriter(out, charset); 105 | this.gson.toJson(payload, writer); 106 | writer.close(); 107 | payload = out.toByteArray(); 108 | } else { 109 | Writer writer = new StringWriter(); 110 | this.gson.toJson(payload, writer); 111 | payload = writer.toString(); 112 | } 113 | } catch (IOException ex) { 114 | throw new MessageConversionException("Could not write JSON: " + ex.getMessage(), ex); 115 | } 116 | 117 | return payload; 118 | } 119 | 120 | protected TypeToken getTypeToken(Type type) { 121 | return TypeToken.get(type); 122 | } 123 | 124 | private Charset getCharset(MimeType contentType) { 125 | 126 | if ((contentType != null) && (contentType.getCharSet() != null)) { 127 | return contentType.getCharSet(); 128 | } 129 | 130 | return DEFAULT_CHARSET; 131 | 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/message/OnlineWorkspaceStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.message; 6 | 7 | import com.google.common.collect.HashMultimap; 8 | import com.google.common.collect.Multimap; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.Collection; 12 | 13 | /** 14 | * Created by vangie on 15/2/5. 15 | */ 16 | @Component 17 | public class OnlineWorkspaceStore { 18 | 19 | private Multimap onlineWorkspaceSessions = HashMultimap.create(); 20 | 21 | public Collection getSessions(String spaceKey) { 22 | return onlineWorkspaceSessions.get(spaceKey); 23 | } 24 | 25 | public void removeSession(String spaceKey, String sessionId) { 26 | onlineWorkspaceSessions.remove(spaceKey, sessionId); 27 | } 28 | 29 | public void addSession(String spaceKey, String sessionId) { 30 | onlineWorkspaceSessions.put(spaceKey, sessionId); 31 | } 32 | 33 | public boolean isEmpty(String spaceKey) { 34 | return !onlineWorkspaceSessions.containsKey(spaceKey) || onlineWorkspaceSessions.get(spaceKey).isEmpty(); 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/message/SessionCacheWebSocketHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.message; 6 | 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.web.socket.CloseStatus; 11 | import org.springframework.web.socket.WebSocketHandler; 12 | import org.springframework.web.socket.WebSocketSession; 13 | import org.springframework.web.socket.handler.WebSocketHandlerDecorator; 14 | import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor; 15 | 16 | /** 17 | * Created by vangie on 15/2/27. 18 | * 19 | */ 20 | @Slf4j 21 | public class SessionCacheWebSocketHandler extends WebSocketHandlerDecorator { 22 | private WebSocketSessionStore webSocketSessionStore; 23 | 24 | public SessionCacheWebSocketHandler(WebSocketHandler delegate, WebSocketSessionStore store) { 25 | super(delegate); 26 | this.webSocketSessionStore = store; 27 | } 28 | 29 | 30 | @Override 31 | public void afterConnectionEstablished(WebSocketSession session) throws Exception { 32 | log.debug("after WebSocket connection established, http session id => {}, spaceKey => {}", 33 | session.getAttributes().get(HttpSessionHandshakeInterceptor.HTTP_SESSION_ID_ATTR_NAME), 34 | session.getAttributes().get("spaceKey")); 35 | 36 | webSocketSessionStore.addSession(session.getId(), session); 37 | super.afterConnectionEstablished(session); 38 | } 39 | 40 | @Override 41 | public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { 42 | log.debug("after WebSocket connection closed"); 43 | 44 | super.afterConnectionClosed(session, closeStatus); 45 | webSocketSessionStore.removeSession(session.getId()); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/message/SpaceKeyHandshakeInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.message; 6 | 7 | import org.springframework.http.server.ServerHttpRequest; 8 | import org.springframework.http.server.ServerHttpResponse; 9 | import org.springframework.http.server.ServletServerHttpRequest; 10 | import org.springframework.util.StringUtils; 11 | import org.springframework.web.servlet.HandlerMapping; 12 | import org.springframework.web.socket.WebSocketHandler; 13 | import org.springframework.web.socket.server.HandshakeInterceptor; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import java.util.Map; 17 | 18 | /** 19 | * Created by vangie on 15/5/24. 20 | */ 21 | public class SpaceKeyHandshakeInterceptor implements HandshakeInterceptor { 22 | 23 | @Override 24 | public boolean beforeHandshake(ServerHttpRequest request, 25 | ServerHttpResponse response, 26 | WebSocketHandler wsHandler, 27 | Map attributes) throws Exception { 28 | 29 | String sockJsPath = getSockJsPath(((ServletServerHttpRequest)request).getServletRequest()); 30 | 31 | String[] pathSegments = StringUtils.tokenizeToStringArray(sockJsPath.substring(1), "/"); 32 | 33 | attributes.put("spaceKey", pathSegments[0]); 34 | 35 | return true; 36 | } 37 | 38 | @Override 39 | public void afterHandshake(ServerHttpRequest request, 40 | ServerHttpResponse response, 41 | WebSocketHandler wsHandler, 42 | Exception exception) { 43 | // Empty 44 | } 45 | 46 | private String getSockJsPath(HttpServletRequest servletRequest) { 47 | String attribute = HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE; 48 | String path = (String) servletRequest.getAttribute(attribute); 49 | return ((path.length() > 0) && (path.charAt(0) != '/')) ? "/" + path : path; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/message/SpringfoxJsonToGsonAdapter.java: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (c) 2014-2016 CODING. 4 | */ 5 | 6 | package net.coding.ide.web.message; 7 | 8 | import com.google.gson.JsonElement; 9 | import com.google.gson.JsonParser; 10 | import com.google.gson.JsonSerializationContext; 11 | import com.google.gson.JsonSerializer; 12 | import springfox.documentation.spring.web.json.Json; 13 | 14 | import java.lang.reflect.Type; 15 | 16 | /** 17 | * 使 gson 支持 swagger json 18 | * 19 | */ 20 | public class SpringfoxJsonToGsonAdapter implements JsonSerializer { 21 | 22 | @Override 23 | public JsonElement serialize(Json json, Type type, JsonSerializationContext context) { 24 | final JsonParser parser = new JsonParser(); 25 | return parser.parse(json.value()); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/net/coding/ide/web/message/WebSocketSessionStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.web.message; 6 | 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.socket.WebSocketSession; 9 | 10 | import java.util.Map; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | 13 | /** 14 | * Created by vangie on 15/2/28. 15 | */ 16 | @Component 17 | public class WebSocketSessionStore { 18 | 19 | private final Map sessions = new ConcurrentHashMap(); 20 | 21 | public void addSession(String id, WebSocketSession session){ 22 | sessions.put(id, session); 23 | } 24 | 25 | public WebSocketSession getSession(String id){ 26 | return sessions.get(id); 27 | } 28 | 29 | public WebSocketSession removeSession(String id){ 30 | return sessions.remove(id); 31 | } 32 | 33 | public Object getAttribute(String id, String key){ 34 | WebSocketSession session = sessions.get(id); 35 | if(session == null){ 36 | return null; 37 | } 38 | return session.getAttributes().get(key); 39 | } 40 | 41 | public Object setAttribute(String id, String key, Object value) { 42 | WebSocketSession session = sessions.get(id); 43 | if(session == null){ 44 | return null; 45 | } 46 | return session.getAttributes().put(key, value); 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | # Environment for running the application, either "prod" or "dev" 2 | RUN_ENV=dev 3 | 4 | # Server Port 5 | server.port=8080 6 | 7 | # USER INFO 8 | USERNAME=coding 9 | EMAIL=coding@coding.net 10 | AVATAR=https://dn-coding-net-production-static.qbox.me/512b2a62-956b-4ef8-8e84-b3c66e71468f.png?imageMogr2/auto-orient/format/png/crop/!300x300a0a0 11 | 12 | # CORS 13 | ALLOWED_ORIGINS=* 14 | 15 | # Home Directory 16 | CODING_IDE_HOME=#{systemProperties['user.home']}/.coding-ide 17 | SPACE_HOME=${CODING_IDE_HOME}/workspace 18 | PACKAGE_HOME=${CODING_IDE_HOME}/packages 19 | 20 | # PTY_LIB_FOLDER 21 | PTY_LIB_FOLDER=src/main/resources/lib 22 | 23 | # DATA SOURCE 24 | spring.datasource.url=jdbc:sqlite:${CODING_IDE_HOME}/ide.db 25 | spring.datasource.driver-class-name=org.sqlite.JDBC 26 | spring.datasource.type=com.zaxxer.hikari.HikariDataSource 27 | 28 | spring.datasource.hikari.leak-detection-threshold=15000 29 | spring.datasource.hikari.minimum-idle=2 30 | spring.datasource.hikari.maximum-pool-size=10 31 | 32 | # JPA 33 | spring.jpa.hibernate.ddl-auto=update 34 | spring.jpa.properties.hibernate.dialect=com.enigmabridge.hibernate.dialect.SQLiteDialect 35 | spring.jpa.show-sql=false 36 | 37 | # spring static resources 38 | 39 | spring.resources.static-locations=classpath:/resources/,classpath:/META-INF/resources/,classpath:/META-INF/resources/webjars/ 40 | 41 | spring.http.multipart.max-file-size=1024MB 42 | spring.http.multipart.max-request-size=1024MB -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ${AnsiColor.BRIGHT_BLUE} ,----.. 2 | ${AnsiColor.BRIGHT_BLUE} / / \ ,---, ,--, 3 | ${AnsiColor.BRIGHT_BLUE}| : : ,---. ,---.'|,--.'| ,---, 4 | ${AnsiColor.BRIGHT_BLUE}. | ;. / ' ,'\ | | :| |, ,-+-. / | ,----._,. 5 | ${AnsiColor.BRIGHT_BLUE}. ; /--` / / | | | |`--'_ ,--.'|' | / / ' / 6 | ${AnsiColor.BRIGHT_BLUE}; | ; . ; ,. : ,--.__| |,' ,'| | | ,"' || : | 7 | ${AnsiColor.BRIGHT_BLUE}| : | ' | |: : / ,' |' | | | | / | || | .\ . 8 | ${AnsiColor.BRIGHT_BLUE}. | '___' | .; :. ' / || | : | | | | |. ; '; | 9 | ${AnsiColor.BRIGHT_BLUE}' ; : .'| : |' ; |: |' : |__ | | | |/ ' . . | 10 | ${AnsiColor.BRIGHT_BLUE}' | '/ :\ \ / | | '/ '| | '.'|| | |--' `---`-'| | 11 | ${AnsiColor.BRIGHT_BLUE}| : / `----' | : :|; : ;| |/ .'__/\_: | 12 | ${AnsiColor.BRIGHT_BLUE} \ \ .' \ \ / | , / '---' | : : 13 | ${AnsiColor.BRIGHT_BLUE} `---` `----' ---`-' \ \ / 14 | 15 | ${AnsiColor.BRIGHT_GREEN} .---. ,---, ,---, ,---,. 16 | ${AnsiColor.BRIGHT_GREEN} /. ./| ,---, ,`--.' | .' .' `\ ,' .' | 17 | ${AnsiColor.BRIGHT_GREEN} .--'. ' ; ,---.'| | : :,---.' \ ,---.' | 18 | ${AnsiColor.BRIGHT_GREEN} /__./ \ : | | | : : | '| | .`\ || | .' 19 | ${AnsiColor.BRIGHT_GREEN} .--'. ' \' . ,---. : : : | : |: : | ' |: : |-, 20 | ${AnsiColor.BRIGHT_GREEN} /___/ \ | ' ' / \ : |,-.' ' ;| ' ' ; :: | ;/| 21 | ${AnsiColor.BRIGHT_GREEN} ; \ \; : / / || : ' || | |' | ; . || : .' 22 | ${AnsiColor.BRIGHT_GREEN} \ ; ` |. ' / || | / :' : ;| | : | '| | |-, 23 | ${AnsiColor.BRIGHT_GREEN} . \ .\ ;' ; /|' : |: || | '' : | / ; ' : ;/| 24 | ${AnsiColor.BRIGHT_GREEN} \ \ ' \ |' | / || | '/ :' : || | '` ,/ | | \ 25 | ${AnsiColor.BRIGHT_GREEN} : ' |--" | : || : |; |.' ; : .' | : .' 26 | ${AnsiColor.BRIGHT_GREEN} \ \ ; \ \ / / \ / '---' | ,.' | | ,' 27 | ${AnsiColor.BRIGHT_GREEN} '---" `----' `-'----' '---' `----' -------------------------------------------------------------------------------- /src/main/resources/lib/linux/x86/libpty.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/src/main/resources/lib/linux/x86/libpty.so -------------------------------------------------------------------------------- /src/main/resources/lib/linux/x86_64/libpty.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/src/main/resources/lib/linux/x86_64/libpty.so -------------------------------------------------------------------------------- /src/main/resources/lib/macosx/x86/libpty.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/src/main/resources/lib/macosx/x86/libpty.dylib -------------------------------------------------------------------------------- /src/main/resources/lib/macosx/x86_64/libpty.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/src/main/resources/lib/macosx/x86_64/libpty.dylib -------------------------------------------------------------------------------- /src/main/resources/lib/win/x86/libwinpty.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/src/main/resources/lib/win/x86/libwinpty.dll -------------------------------------------------------------------------------- /src/main/resources/lib/win/x86/winpty-agent.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coding/WebIDE-Backend/ddb5008e5f807d32217362697d09d4f78c61b75a/src/main/resources/lib/win/x86/winpty-agent.exe -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/TestProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide; 6 | 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * Created by tan on 16/9/6. 12 | */ 13 | @Configuration 14 | @SpringBootTest 15 | public class TestProperty { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/annotation/RepositoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.annotation; 6 | 7 | import com.github.springtestdbunit.DbUnitTestExecutionListener; 8 | import com.github.springtestdbunit.annotation.DatabaseSetup; 9 | import org.junit.runner.RunWith; 10 | import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; 11 | import org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureTestDatabase; 12 | import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; 13 | import org.springframework.test.context.ActiveProfiles; 14 | import org.springframework.test.context.TestExecutionListeners; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; 17 | import org.springframework.test.context.support.DirtiesContextTestExecutionListener; 18 | import org.springframework.test.context.transaction.TransactionalTestExecutionListener; 19 | 20 | import java.lang.annotation.*; 21 | 22 | /** 23 | * Created by tan on 16/9/7. 24 | */ 25 | @Target(ElementType.TYPE) 26 | @Retention(RetentionPolicy.RUNTIME) 27 | @Documented 28 | @DataJpaTest(showSql = false) 29 | @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2) 30 | @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, // for dbunit 31 | DirtiesContextTestExecutionListener.class, 32 | TransactionalTestExecutionListener.class, 33 | DbUnitTestExecutionListener.class }) 34 | @DatabaseSetup("classpath:db/workspace.xml") 35 | public @interface RepositoryTest { 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/config/TestPropConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.config; 6 | 7 | import org.springframework.test.context.TestPropertySource; 8 | 9 | /** 10 | * Created by tan on 16/9/10. 11 | */ 12 | @TestPropertySource(value = "classpath:application-test.properties") 13 | public class TestPropConfig { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/repository/ProjectRepositoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.repository; 6 | 7 | import net.coding.ide.annotation.RepositoryTest; 8 | import net.coding.ide.entity.ProjectEntity; 9 | import org.junit.Assert; 10 | import org.junit.Test; 11 | import org.junit.runner.RunWith; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 14 | import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | 17 | import java.sql.DatabaseMetaData; 18 | import java.sql.SQLException; 19 | 20 | @RunWith(SpringRunner.class) 21 | @RepositoryTest 22 | public class ProjectRepositoryTest { 23 | 24 | @Autowired 25 | private TestEntityManager entityManager; 26 | 27 | @Autowired 28 | private ProjectRepository projectRepo; 29 | 30 | @Test 31 | public void testFindBySshUrl() throws SQLException { 32 | ProjectEntity projectEntity = projectRepo.findByUrl("git@coding.net:duwan/coding-ide.git"); 33 | 34 | Assert.assertEquals(0L, (long)projectEntity.getId()); 35 | } 36 | 37 | @Test 38 | public void testDatabase() throws SQLException { 39 | org.hibernate.engine.spi.SessionImplementor sessionImp = 40 | (org.hibernate.engine.spi.SessionImplementor) entityManager.getEntityManager().getDelegate(); 41 | DatabaseMetaData metadata = sessionImp.connection().getMetaData(); 42 | 43 | Assert.assertEquals(metadata.getDatabaseProductName(), "H2"); 44 | } 45 | } -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/repository/WorkspaceRepositoryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.repository; 6 | 7 | import net.coding.ide.annotation.RepositoryTest; 8 | import net.coding.ide.entity.WorkspaceEntity; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.beans.factory.annotation.Value; 13 | import org.springframework.test.context.ActiveProfiles; 14 | import org.springframework.test.context.TestPropertySource; 15 | import org.springframework.test.context.junit4.SpringRunner; 16 | import org.springframework.transaction.annotation.Transactional; 17 | 18 | import static net.coding.ide.entity.WorkspaceEntity.WsWorkingStatus.Offline; 19 | import static org.hamcrest.Matchers.is; 20 | import static org.junit.Assert.assertThat; 21 | 22 | /** 23 | * Created by vangie on 14/12/5. 24 | */ 25 | @RunWith(SpringRunner.class) 26 | @RepositoryTest 27 | public class WorkspaceRepositoryTest { 28 | 29 | @Autowired 30 | private WorkspaceRepository wsRepo; 31 | 32 | @Test 33 | public void testFindBySpaceKey() { 34 | WorkspaceEntity ws = wsRepo.findBySpaceKey("qwerty"); 35 | 36 | assertThat(ws.getId(), is(1L)); 37 | } 38 | 39 | @Test 40 | public void testExist() { 41 | assertThat(wsRepo.isSpaceKeyExist("fadga"), is(false)); 42 | assertThat(wsRepo.isSpaceKeyExist("qwerty"), is(true)); 43 | } 44 | 45 | @Test 46 | public void testIsOnline() { 47 | assertThat(wsRepo.isOnline("qwerty"), is(true)); 48 | assertThat(wsRepo.isOnline("aasdfh"), is(false)); 49 | } 50 | 51 | @Test 52 | public void testUpdateWorkingStatus() { 53 | assertThat(wsRepo.isOnline("qwerty"), is(true)); 54 | wsRepo.updateWorkingStatus("qwerty", Offline); 55 | assertThat(wsRepo.isOnline("qwerty"), is(false)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/service/BaseServiceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.service; 6 | 7 | import net.coding.ide.config.*; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.runners.MockitoJUnitRunner; 10 | import org.springframework.boot.test.context.TestConfiguration; 11 | import org.springframework.test.context.ContextConfiguration; 12 | import org.springframework.test.context.TestPropertySource; 13 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 14 | 15 | /** 16 | * Created by tan on 16/9/8. 17 | */ 18 | @RunWith(SpringJUnit4ClassRunner.class) 19 | @ContextConfiguration(classes = { 20 | TestPropConfig.class 21 | }) 22 | public abstract class BaseServiceTest { 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/service/PackageServiceImplTest.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.service; 2 | 3 | import net.coding.ide.dto.Package; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.io.File; 8 | import java.io.IOException; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.util.List; 12 | 13 | import static net.coding.ide.utils.FilesUtils.createTempDirectoryAndDeleteOnExit; 14 | import static org.junit.Assert.assertEquals; 15 | 16 | public class PackageServiceImplTest { 17 | 18 | 19 | private Path packages = createTempDirectoryAndDeleteOnExit("packages").toPath(); 20 | 21 | private PackageServiceImpl packageService = new PackageServiceImpl(packages.toFile()); 22 | 23 | @Test 24 | public void testFindNoneExistPackages() { 25 | List packages = packageService.findAll(); 26 | assertEquals(0, packages.size()); 27 | } 28 | 29 | @Test 30 | public void testFindExistPackages() throws IOException { 31 | 32 | File package1 = new File(packages.toFile(), "temporary/0.0.1-alpha.03/manifest.json"); 33 | 34 | createPackage(package1, "{\n" + 35 | " \"meta\": {\n" + 36 | " \"name\": \"temporary\",\n" + 37 | " \"version\": \"0.0.1-alpha.03\",\n" + 38 | " \"description\": \"WebIDE-Plugin-Temporary for Coding WebIDE\",\n" + 39 | " \"author\": \"candy\",\n" + 40 | " \"displayName\": \"temporary\"\n" + 41 | " }\n" + 42 | "}"); 43 | 44 | File package2 = new File(packages.toFile(), "env/0.0.1-alpha06/manifest.json"); 45 | 46 | createPackage(package2, "{\n" + 47 | " \"meta\": {\n" + 48 | " \"name\": \"env\",\n" + 49 | " \"version\": \"0.0.1-alpha06\",\n" + 50 | " \"description\": \"WebIDE-Plugin-Env for Coding WebIDE\",\n" + 51 | " \"author\": \"candy\",\n" + 52 | " \"displayName\": \"Environment\"\n" + 53 | " }\n" + 54 | "}"); 55 | 56 | List packages = packageService.findAll(); 57 | 58 | assertEquals(2, packages.size()); 59 | } 60 | 61 | 62 | private void createPackage(File manifest, String content) throws IOException { 63 | 64 | Files.createDirectories(new File(manifest.getParent()).toPath()); 65 | 66 | Files.write(manifest.toPath(), content.getBytes()); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/utils/DataPopulator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import net.coding.ide.entity.ConfigEntity; 8 | import net.coding.ide.entity.ProjectEntity; 9 | import net.coding.ide.entity.WorkspaceEntity; 10 | import net.coding.ide.model.FileInfo; 11 | import net.coding.ide.model.GitLog; 12 | import net.coding.ide.model.PersonIdent; 13 | import org.joda.time.DateTime; 14 | import org.joda.time.DateTimeZone; 15 | 16 | import java.util.Date; 17 | 18 | public class DataPopulator { 19 | private DateTimeZone timeZone = DateTimeZone.forID("UTC"); 20 | 21 | public ProjectEntity populateProjectEntity(){ 22 | ProjectEntity project = new ProjectEntity(); 23 | project.setName("test-project"); 24 | project.setUrl("git@git.coding.net:kevenyoung03/Test02.git"); 25 | project.setOwnerName("phying"); 26 | return project; 27 | } 28 | 29 | public ConfigEntity populateConfigEntity() { 30 | ConfigEntity configEntity = new ConfigEntity(); 31 | configEntity.setId(0L); 32 | configEntity.setKey("key"); 33 | configEntity.setName("name"); 34 | configEntity.setValue("value"); 35 | 36 | return configEntity; 37 | } 38 | 39 | public WorkspaceEntity populateWorkspaceEntity(){ 40 | WorkspaceEntity wsEntity = new WorkspaceEntity(); 41 | wsEntity.setId(1L); 42 | wsEntity.setProject(populateProjectEntity()); 43 | wsEntity.setSpaceKey("qwerty"); 44 | wsEntity.setDescription("qwerty workspace"); 45 | 46 | wsEntity.setCreatedDate(new DateTime("2015-01-21T11:11", timeZone)); 47 | wsEntity.setLastModifiedDate(new DateTime("2015-01-21T11:11", timeZone)); 48 | wsEntity.setWorkingStatus(WorkspaceEntity.WsWorkingStatus.Online); 49 | 50 | return wsEntity; 51 | } 52 | 53 | public FileInfo populateFileInfo(String name, String path, boolean isDir, int size, DateTime date){ 54 | FileInfo fileInfo = new FileInfo(); 55 | fileInfo.setName(name); 56 | fileInfo.setDir(isDir); 57 | fileInfo.setPath(path); 58 | fileInfo.setSize(size); 59 | fileInfo.setLastModified(date); 60 | fileInfo.setLastAccessed(date); 61 | return fileInfo; 62 | } 63 | 64 | public GitLog populateGitLog() { 65 | GitLog gitLog = new GitLog(); 66 | gitLog.setShortName("xxx"); 67 | gitLog.setName("xxxxxx"); 68 | gitLog.setShortName("short message"); 69 | gitLog.setCommitTime((int)new Date().getTime() / 1000); 70 | gitLog.setAuthorIdent(populatePersonIdent()); 71 | gitLog.setCommiterIdent(populatePersonIdent()); 72 | return gitLog; 73 | } 74 | 75 | public PersonIdent populatePersonIdent() { 76 | PersonIdent personIdent = new PersonIdent(); 77 | personIdent.setName("user"); 78 | personIdent.setEmailAddress("user@coding.net"); 79 | return personIdent; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/utils/FilesUtils.java: -------------------------------------------------------------------------------- 1 | package net.coding.ide.utils; 2 | 3 | import org.apache.commons.io.FileUtils; 4 | 5 | import java.io.File; 6 | import java.io.IOException; 7 | 8 | /** 9 | * Created by tan on 16-3-15. 10 | */ 11 | public class FilesUtils { 12 | 13 | public static File createTempDirectoryAndDeleteOnExit(String path) { 14 | try { 15 | File tmpDir = java.nio.file.Files.createTempDirectory(path).toFile(); 16 | 17 | // delete directory when exit jvm 18 | Runtime.getRuntime().addShutdownHook(new Thread(() -> FileUtils.deleteQuietly(tmpDir))); 19 | 20 | return tmpDir; 21 | } catch (IOException e) { 22 | e.printStackTrace(); 23 | } 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/net/coding/ide/utils/RepositoryHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2016 CODING. 3 | */ 4 | 5 | package net.coding.ide.utils; 6 | 7 | import org.eclipse.jgit.api.Git; 8 | import org.eclipse.jgit.api.errors.GitAPIException; 9 | import org.eclipse.jgit.lib.Repository; 10 | import org.eclipse.jgit.storage.file.FileRepositoryBuilder; 11 | 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.nio.file.Files; 15 | 16 | public class RepositoryHelper { 17 | 18 | public static Repository createRepository(File repo) throws IOException, GitAPIException { 19 | if (!repo.exists()) { 20 | Files.createDirectory(repo.toPath()); 21 | } 22 | 23 | Repository repository = FileRepositoryBuilder.create(new File(repo, ".git")); 24 | repository.create(); 25 | 26 | return repository; 27 | } 28 | 29 | @FunctionalInterface 30 | public interface BranchAction { 31 | void apply(Git git, String branch) throws IOException, GitAPIException; 32 | } 33 | 34 | public static void createTestBranch(Repository repository, String branch, BranchAction action) throws IOException, GitAPIException { 35 | Git git = new Git(repository); 36 | 37 | String origin = repository.getBranch(); 38 | 39 | git.checkout().setName(branch).setCreateBranch(true).call(); 40 | 41 | action.apply(git, branch); 42 | 43 | git.checkout().setName(origin).call(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/resources/application-test.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/test/resources/db/workspace.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 12 | 13 | 18 | 19 | 24 | 25 | 30 | 31 | 36 | 37 | 42 | 43 | -------------------------------------------------------------------------------- /src/test/resources/workspace/upload.txt: -------------------------------------------------------------------------------- 1 | hello coding! --------------------------------------------------------------------------------