Merge branch 'master' into spi

Signed-off-by: Alexander Garagatyi <agaragatyi@codenvy.com>
6.19.x
Alexander Garagatyi 2017-04-27 11:57:35 +03:00
commit 7877e47f6a
326 changed files with 2531 additions and 1125 deletions

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent-shared</artifactId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-agent</artifactId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>exec-agent</artifactId>
<name>Agent :: Exec</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>go-agents</artifactId>
<name>Agent :: Golang agents</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>ls-csharp-agent</artifactId>
<name>Language Server C# Agent</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>ls-json-agent</artifactId>
<name>Language Server Json Agent</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>ls-php-agent</artifactId>
<name>Language Server PHP Agent</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>ls-python-agent</artifactId>
<name>Language Server python Agent</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>ls-typescript-agent</artifactId>
<name>Language Server typescript Agent</name>

View File

@ -16,11 +16,11 @@
<parent>
<artifactId>che-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>che-agents-parent</artifactId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Che Agents Parent</name>
<modules>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>ssh-agent</artifactId>
<name>SSH Agent</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>terminal-agent</artifactId>
<name>Agent :: Terminal</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-agents-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>unison-agent</artifactId>
<name>Unison Agent</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-ide-war</artifactId>
<packaging>war</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-main</artifactId>
<packaging>pom</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-wsagent-server</artifactId>
<packaging>pom</packaging>

View File

@ -22,6 +22,8 @@
[ -z "${JPDA_ADDRESS}" ] && JPDA_ADDRESS="4403"
[ -z "${UMASK}" ] && UMASK="022"
#Tomcat options
[ -z "${CATALINA_OPTS}" ] && CATALINA_OPTS="-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.ssl=false \

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-wsagent-war</artifactId>
<packaging>war</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-assembly-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>assembly-wsmaster-war</artifactId>
<packaging>war</packaging>

View File

@ -119,7 +119,7 @@ public class WsMasterModule extends AbstractModule {
bind(org.eclipse.che.security.oauth.OAuthAuthenticatorProvider.class)
.to(org.eclipse.che.security.oauth.OAuthAuthenticatorProviderImpl.class);
bind(org.eclipse.che.api.auth.oauth.OAuthTokenProvider.class)
bind(org.eclipse.che.security.oauth.shared.OAuthTokenProvider.class)
.to(org.eclipse.che.security.oauth.OAuthAuthenticatorTokenProvider.class);
bind(org.eclipse.che.security.oauth.OAuthAuthenticationService.class);

View File

@ -16,12 +16,12 @@
<parent>
<artifactId>che-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.che</groupId>
<artifactId>che-assembly-parent</artifactId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Che IDE :: Parent</name>
<modules>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-core</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-dto-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-dto</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-api-model</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-db-vendor-h2</artifactId>
<name>Che Core :: DB :: Vendor H2</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-db-vendor-postgresql</artifactId>
<name>Che Core :: DB :: Vendor PostgreSQL</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-db</artifactId>
<name>Che Core :: DB</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-typescript-dto-maven-plugin</artifactId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-annotations</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-inject</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-j2ee</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-json</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-lang</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-schedule</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-test</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-commons-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-xml</artifactId>
<packaging>jar</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>che-core-commons-parent</artifactId>

View File

@ -16,12 +16,12 @@
<parent>
<artifactId>che-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-parent</artifactId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Che Core Parent</name>
<modules>

View File

@ -16,12 +16,12 @@
<parent>
<artifactId>che-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.che.dashboard</groupId>
<artifactId>che-dashboard-war</artifactId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>Che Dashboard :: Web App</name>
<inceptionYear>2015</inceptionYear>

View File

@ -29,12 +29,16 @@ export class CreateProjectSamplesController {
* Default constructor that is using resource
* @ngInject for Dependency injection
*/
constructor($scope, $filter: ng.IFilterService, cheAPI: CheAPI) {
constructor($scope: ng.IScope, $filter: ng.IFilterService, cheAPI: CheAPI) {
this.$filter = $filter;
this.templates = cheAPI.getProjectTemplate().getAllProjectTemplates();
if (!this.templates.length) {
cheAPI.getProjectTemplate().fetchTemplates();
const templatesPromise = cheAPI.getProjectTemplate().fetchTemplates();
templatesPromise.finally(() => {
this.templates = cheAPI.getProjectTemplate().getAllProjectTemplates();
this.filterAndSortTemplates();
});
}
$scope.$watch(() => {

View File

@ -9,6 +9,8 @@
* Codenvy, S.A. - initial API and implementation
*/
'use strict';
import {DockerfileParser} from '../../../components/api/environment/docker-file-parser';
const COMPOSE = 'compose';
const DOCKERFILE = 'dockerfile';
@ -21,6 +23,12 @@ const DOCKERIMAGE = 'dockerimage';
*/
export class StackValidationService {
dockerfileParser: DockerfileParser;
constructor() {
this.dockerfileParser = new DockerfileParser();
}
/**
* Return result of recipe validation.
* @param stack {che.IStack}
@ -247,9 +255,13 @@ export class StackValidationService {
if (!recipe.content) {
isValid = false;
errors.push('Unknown recipe content.');
} else if (!/^FROM\s+\w+/m.test(recipe.content)) {
isValid = false;
errors.push('The dockerfile is invalid.');
} else {
try {
this.dockerfileParser.parse(recipe.content);
} catch (e) {
isValid = false;
errors.push(e.message);
}
}
}
if (!recipe.contentType) {

View File

@ -33,7 +33,7 @@ export class EditServerDialogController {
usedReferences: string[];
port: number;
portMin: number = 1024;
portMin: number = 0;
portMax: number = 65535;
protocol: string;
reference: string;

View File

@ -35,7 +35,7 @@
che-place-holder="Port number"
ng-model="editServerDialogController.port"
type="number"
min="1024"
min="0"
max="65535"
custom-validator="editServerDialogController.isUniquePort($value)"
aria-label="Server port number"

View File

@ -1,4 +1,4 @@
<che-popup title="Remove machine" on-close="deleteDevMachineDialogController.hide()">
<che-popup title="Remove machine" on-close="deleteDevMachineDialogController.cancel()">
<div layout="column" flex
class="delete-dev-machine-dialog">

View File

@ -233,7 +233,6 @@ export class WorkspaceMachineConfigController {
* @param {MouseEvent} $event
* @returns {ng.IPromise<any>}
*/
// todo
showDeleteDevMachineDialog($event: MouseEvent): ng.IPromise<any> {
return this.$mdDialog.show({
targetEvent: $event,

View File

@ -48,7 +48,7 @@
<div ng-message="maxlength">Command's preview URL should be less than 256 characters long.</div>
</che-input>
</div>
<che-button-primary che-button-title="{{editCommandDialogController.index === -1 ? 'Add' : 'Edit'}}"
<che-button-primary che-button-title="{{editCommandDialogController.index === -1 ? 'Add' : 'Save'}}"
ng-click="editCommandDialogController.updateCommand()"
ng-disabled="commandForm.$invalid">
</che-button-primary>

View File

@ -9,6 +9,7 @@
* Codenvy, S.A. - initial API and implementation
*/
'use strict';
import {DockerfileParser} from '../../../../../components/api/environment/docker-file-parser';
/**
* @ngdoc controller
@ -20,6 +21,9 @@
export class WorkspaceRecipeAuthoringController {
$timeout: ng.ITimeoutService;
dockerfileParser: DockerfileParser;
recipeValidationError: string;
editingTimeoutPromise: ng.IPromise<any>;
recipeFormat: string;
@ -43,6 +47,8 @@ export class WorkspaceRecipeAuthoringController {
constructor($scope: ng.IScope, $timeout: ng.ITimeoutService) {
this.$timeout = $timeout;
this.dockerfileParser = new DockerfileParser();
this.editorOptions = {
lineWrapping: true,
lineNumbers: true,
@ -81,6 +87,7 @@ export class WorkspaceRecipeAuthoringController {
this.editingTimeoutPromise = this.$timeout(() => {
this.detectFormat(content);
this.validateRecipe(content);
}, 100);
}
@ -98,7 +105,16 @@ export class WorkspaceRecipeAuthoringController {
}
}
onRecipeChange() {
validateRecipe(content: string): void {
this.recipeValidationError = '';
try {
this.dockerfileParser.parse(content);
} catch (e) {
this.recipeValidationError = e.message;
}
}
onRecipeChange(): void {
this.$timeout(() => {
this.detectFormat(this.recipeScriptCopy);
this.recipeChange({

View File

@ -16,6 +16,10 @@
<div ng-message="required">The recipe is required.</div>
</che-error-messages>
</che-input>
<div class="errors-container"
ng-if="workspaceRecipeAuthoringController.recipeValidationError">
{{workspaceRecipeAuthoringController.recipeValidationError}}
</div>
<div class="recipe-docs-link">
<a href="https://www.eclipse.org/che/docs/workspace/stacks/index.html#stack-administration" target="_blank">Custom stack documentation</a>
</div>

View File

@ -17,3 +17,11 @@
.che-label-container-row
align-items center
.errors-container
color $error-color
min-height 20px
margin-right 2px
span
margin-right 2px

View File

@ -51,6 +51,7 @@ export class WorkspaceDetailsController {
namespaceId: string = '';
namespaceLabel: string;
namespaceLabels: Array<string>;
namespaceInfo: String;
onNamespaceChanged: Function;
workspaceId: string = '';
workspaceName: string = '';
@ -188,12 +189,14 @@ export class WorkspaceDetailsController {
this.namespaceId = namespace ? namespace.id : (this.getNamespaces().length ? this.getNamespaces()[0].id : undefined);
this.namespaceLabel = namespace ? namespace.label : (this.getNamespaces().length ? this.getNamespaces()[0].label : undefined);
this.namespaceLabels = this.getNamespaces().length ? this.lodash.pluck(this.getNamespaces(), 'label') : [];
this.fetchNamespaceInfo();
this.onNamespaceChanged = (label: string) => {
let namespace = this.getNamespaces().find((namespace: any) => {
return namespace.label === label;
});
this.namespaceId = namespace ? namespace.id : this.namespaceId;
this.fetchNamespaceInfo();
}
});
}
@ -268,7 +271,7 @@ export class WorkspaceDetailsController {
*/
getWorkspaceStatus(): string {
if (this.isCreationFlow) {
return 'CREATING';
return 'New';
}
let unknownStatus = 'unknown';
@ -312,6 +315,17 @@ export class WorkspaceDetailsController {
}
}
fetchNamespaceInfo() {
if (!this.cheNamespaceRegistry.getAdditionalInfo()) {
this.namespaceInfo = null;
return;
}
this.cheNamespaceRegistry.getAdditionalInfo()(this.namespaceId).then((info: string) => {
this.namespaceInfo = info;
});
}
/**
* Callback when Team button is clicked in Edit mode.
* Redirects to billing details or team details.
@ -657,7 +671,7 @@ export class WorkspaceDetailsController {
return tabs.some((tabIndex: number) => {
return this.checkFormsNotValid(tabIndex);
});
}) || this.isDisableWorkspaceCreation();
}
/**
@ -687,5 +701,41 @@ export class WorkspaceDetailsController {
}
}
/**
* Returns namespaces empty message if set.
*
* @returns {string}
*/
getNamespaceEmptyMessage(): string {
return this.cheNamespaceRegistry.getEmptyMessage();
}
/**
* Returns namespaces caption.
*
* @returns {string}
*/
getNamespaceCaption(): string {
return this.cheNamespaceRegistry.getCaption();
}
/**
* Returns namespaces additional information.
*
* @returns {()=>Function}
*/
getNamespaceAdditionalInfo(): Function {
return this.cheNamespaceRegistry.getAdditionalInfo;
}
/**
* Returns whether workspace creation should be disabled based on namespaces.
*
* @returns {boolean|string}
*/
isDisableWorkspaceCreation(): boolean {
let namespaces = this.cheNamespaceRegistry.getNamespaces();
return (this.isCreationFlow && (!namespaces || namespaces.length === 0) && this.cheNamespaceRegistry.getEmptyMessage());
}
}

View File

@ -62,13 +62,23 @@
</div>
</che-label-container>
<!-- Namespace -->
<che-label-container che-label-name="Team"
ng-if="workspaceDetailsController.getNamespaces().length > 0">
<che-filter-selector che-values="workspaceDetailsController.namespaceLabels"
ng-if="workspaceDetailsController.isCreationFlow"
ng-model="workspaceDetailsController.namespaceLabel"
che-on-change="workspaceDetailsController.onNamespaceChanged"></che-filter-selector>
<che-button-default ng-if="!workspaceDetailsController.isCreationFlow"
<che-label-container che-label-name="{{workspaceDetailsController.getNamespaceCaption()}}"
ng-if="workspaceDetailsController.getNamespaces().length > 0 || workspaceDetailsController.getNamespaceEmptyMessage()">
<div ng-if="workspaceDetailsController.isCreationFlow && workspaceDetailsController.getNamespaces().length > 0"
layout="row" layout-align="start center">
<che-filter-selector che-values="workspaceDetailsController.namespaceLabels"
ng-model="workspaceDetailsController.namespaceLabel"
che-on-change="workspaceDetailsController.onNamespaceChanged"></che-filter-selector>
<span class="namespace-additional-info" ng-if="workspaceDetailsController.namespaceInfo">
{{workspaceDetailsController.namespaceInfo}}
</span>
</div>
<div ng-if="workspaceDetailsController.getNamespaces().length === 0 && workspaceDetailsController.getNamespaceEmptyMessage()" class="empty-namespace-label">
<i class="fa-exclamation-triangle fa fa-2x"></i>
{{workspaceDetailsController.getNamespaceEmptyMessage()}}
</div>
<che-button-default ng-if="!workspaceDetailsController.isCreationFlow && workspaceDetailsController.getNamespaces().length > 0"
che-button-title="{{workspaceDetailsController.getNamespaceLabel(workspaceDetailsController.namespaceId)}}"
ng-disabled="!workspaceDetailsController.getNamespace(workspaceDetailsController.namespaceId) || !workspaceDetailsController.getNamespace(workspaceDetailsController.namespaceId).location"
ng-click="workspaceDetailsController.namespaceOnClick(workspaceDetailsController.namespaceId)"

View File

@ -28,6 +28,18 @@
.namespace-button button
text-transform none !important
.namespace-additional-info
color $label-secondary-color
margin-left 10px
.empty-namespace-label
color $warning-color
margin-bottom 10px
i
margin-right 10px
font-size 16pt
span.workspace-status-text
color lighten($che-ide-background-color, 40%) !important
margin-bottom 4px

View File

@ -34,7 +34,7 @@ describe('If recipe has content', () => {
},
'recipe': {
'type': 'dockerfile',
'content': 'FROM codenvy/ubuntu_jdk8\nENV myName=\"John Doe\" myDog=Rex\\ The\\ Dog \\\n myCat=fluffy',
'content': 'FROM codenvy/ubuntu_jdk8\nENV myName="John Doe" myDog=Rex\\ The\\ Dog \\\n myCat=fluffy',
'contentType': 'text/x-dockerfile'
}
};

View File

@ -17,84 +17,315 @@ import {DockerfileParser} from './docker-file-parser';
* @author Oleksii Kurinnyi
*/
describe('Simper dockerfile parser', () => {
describe('Simple dockerfile parser >', () => {
let parser;
beforeEach(() => {
parser = new DockerfileParser();
});
describe('method _parseArgument()', () => {
it('should parse ENV argument as single variable form #1', () => {
let instruction = 'ENV',
argument = 'name environment variable value';
describe('parsing directives >', () => {
let result = parser._parseArgument(instruction, argument);
it(`should know 'escape' directive`, () => {
const dockerfile = `# escape=\\
FROM codenvy/ubuntu_jdk8`;
let expectedResult = [{
instruction: 'ENV',
argument: ['name', 'environment variable value']
}];
expect(result).toEqual(expectedResult);
});
const result = parser.parse(dockerfile);
it('should parse ENV argument as single variable form #2', () => {
let instruction = 'ENV',
argument = 'SBT_OPTS \'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'';
let result = parser._parseArgument(instruction, argument);
let expectedResult = [{
instruction: 'ENV',
argument: ['SBT_OPTS', '\'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'']
}];
expect(result).toEqual(expectedResult);
});
it('should parse ENV argument as multiple variables form #1', () => {
let instruction = 'ENV',
argument = 'key=value';
let result = parser._parseArgument(instruction, argument);
let expectedResult = [{
instruction: 'ENV',
argument: ['key', 'value']
}];
expect(result).toEqual(expectedResult);
});
it('should parse ENV argument as multiple variables form #2', () => {
let instruction = 'ENV',
argument = 'myName="John Doe" myDog=Rex\ The\ Dog myCat=fluffy';
let result = parser._parseArgument(instruction, argument);
let expectedResult = [{
instruction: 'ENV',
argument: ['myName', 'John Doe']
const expectedResult = [{
directive: '# escape=\\'
}, {
instruction: 'ENV',
argument: ['myDog', 'Rex The Dog']
}, {
instruction: 'ENV',
argument: ['myCat', 'fluffy']
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8'
}];
expect(result).toEqual(expectedResult);
});
it(`should treat unknown directive as a comment`, () => {
const dockerfile = `# directive=value
FROM codenvy/ubuntu_jdk8`;
const result = parser.parse(dockerfile);
const expectedResult = [{
comment: '# directive=value'
}, {
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8'
}];
expect(result).toEqual(expectedResult);
});
it(`should throw an error if there are two identical directives`, () => {
const dockerfile = `# escape=\\
# escape=\`
FROM codenvy/ubuntu_jdk8`;
const parse = () => {
parser.parse(dockerfile);
};
expect(parse).toThrowError(TypeError);
});
it(`should treat known directive as a comment after an empty line`, () => {
const dockerfile = `
# escape=\\
FROM codenvy/ubuntu_jdk8`;
const result = parser.parse(dockerfile);
const expectedResult = [{
emptyLine: true
}, {
comment: '# escape=\\'
}, {
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8'
}];
expect(result).toEqual(expectedResult);
});
it(`should treat known directive as a comment after a comment`, () => {
const dockerfile = `# comment line
# escape=\\
FROM codenvy/ubuntu_jdk8`;
const result = parser.parse(dockerfile);
const expectedResult = [{
comment: '# comment line'
}, {
comment: '# escape=\\'
}, {
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8'
}];
expect(result).toEqual(expectedResult);
});
it(`should treat known directive as a comment after a builder instruction`, () => {
const dockerfile = `FROM codenvy/ubuntu_jdk8
# escape=\\`;
const result = parser.parse(dockerfile);
const expectedResult = [{
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8'
}, {
comment: '# escape=\\'
}];
expect(result).toEqual(expectedResult);
});
});
describe('method parseArgument()', () => {
describe('ENV argument as single variable form >', () => {
it('should parse environment variable #1', () => {
const instruction = 'ENV',
argument = 'name environment variable value';
const result = parser.parseArgument(instruction, argument);
const expectedResult = [{
instruction: 'ENV',
argument: ['name', 'environment variable value']
}];
expect(result).toEqual(expectedResult);
});
it('should parse environment variable #2', () => {
const instruction = 'ENV',
argument = 'SBT_OPTS \'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'';
const result = parser.parseArgument(instruction, argument);
const expectedResult = [{
instruction: 'ENV',
argument: ['SBT_OPTS', '\'-Dhttp.proxyHost=proxy.wdf.sap.corp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.wdf.sap.corp -Dhttps.proxyPort=8080 -Dhttp.nonProxyHosts=nexus.wdf.sap.corp\'']
}];
expect(result).toEqual(expectedResult);
});
it(`should throw an error if incorrect ENV argument is written in single variable form`, () => {
const instruction = 'ENV',
argument = 'myNameJohnDoe'; // space between name and value is missed
const parse = () => {
parser.parseArgument(instruction, argument);
};
expect(parse).toThrowError(TypeError);
});
});
describe('ENV argument as multiple variables form >', () => {
it('should parse single environment variable with backslashes', () => {
const dockerfile = `# escape=\\
FROM codenvy/ubuntu_jdk8
ENV myDog=Rex\\ The\\ Dog`;
const result = parser.parse(dockerfile);
const expectedResult = [{
directive: '# escape=\\'
}, {
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8'
}, {
instruction: 'ENV',
argument: ['myDog', 'Rex The Dog']
}];
expect(result).toEqual(expectedResult);
});
it('should parse single environment variable with backtick', () => {
const dockerfile = `# escape=\`
FROM codenvy/ubuntu_jdk8
ENV myDog=Rex\` The\` Dog`;
const result = parser.parse(dockerfile);
const expectedResult = [{
directive: '# escape=\`'
}, {
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8'
}, {
instruction: 'ENV',
argument: ['myDog', 'Rex The Dog']
}];
expect(result).toEqual(expectedResult);
});
it('should parse ENV argument as multiple variables form #1', () => {
const instruction = 'ENV',
argument = 'key=value';
const result = parser.parseArgument(instruction, argument);
const expectedResult = [{
instruction: 'ENV',
argument: ['key', 'value']
}];
expect(result).toEqual(expectedResult);
});
it('should parse ENV argument as multiple variables form #2', () => {
const instruction = 'ENV',
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog myCat=fluffy';
const result = parser.parseArgument(instruction, argument);
const expectedResult = [{
instruction: 'ENV',
argument: ['myName', 'John Doe']
}, {
instruction: 'ENV',
argument: ['myDog', 'Rex The Dog']
}, {
instruction: 'ENV',
argument: ['myCat', 'fluffy']
}];
expect(result).toEqual(expectedResult);
});
it('should parse ENV argument as multiple variables form #3', () => {
const instruction = 'ENV',
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog \\\n myCat=fluffy';
const result = parser.parseArgument(instruction, argument);
const expectedResult = [{
instruction: 'ENV',
argument: ['myName', 'John Doe']
}, {
instruction: 'ENV',
argument: ['myDog', 'Rex The Dog']
}, {
instruction: 'ENV',
argument: ['myCat', 'fluffy']
}];
expect(result).toEqual(expectedResult);
});
it(`should parse ENV argument as multiple variables form #4`, () => {
const instruction = 'ENV',
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog\\ myCat=fluffy';
const result = parser.parseArgument(instruction, argument);
const expectedResult = [{
instruction: 'ENV',
argument: ['myName', 'John Doe']
}, {
instruction: 'ENV',
argument: ['myDog', 'Rex The Dog myCat=fluffy']
}];
expect(result).toEqual(expectedResult);
});
it(`should parse ENV argument as multiple variables form #5`, () => {
const instruction = 'ENV',
argument = 'myVar=\\\\\\ \\\\\\\\';
const result = parser.parseArgument(instruction, argument);
const expectedResult = [{
instruction: 'ENV',
argument: ['myVar', '\\ \\\\']
}];
expect(result).toEqual(expectedResult);
});
it(`should throw an error if incorrect ENV argument is written in multiple variables form`, () => {
const instruction = 'ENV',
argument = 'myName="John Doe" myDog=Rex\\ The\\ Dog myCat fluffy'; // the 'equal' symbol is missed
const parse = () => {
parser.parseArgument(instruction, argument);
};
expect(parse).toThrowError(TypeError);
});
});
});
it('should parse a dockerfile', () => {
let dockerfile = 'FROM codenvy/ubuntu_jdk8ENV'
+ '\n#ENV myCat fluffy'
+ '\nENV myDog Rex The Dog'
+ '\nENV myName="John Doe"';
const dockerfile = `# escape=\\
let result = parser.parse(dockerfile);
FROM codenvy/ubuntu_jdk8
#ENV myCat fluffy
ENV myDog Rex The Dog
ENV myName="John Doe"
ENV myText long \\
multiline \\
value
ENV myVal=\\\\\\ \\\\\\\\`;
let expectedResult = [{
const result = parser.parse(dockerfile);
const expectedResult = [{
directive: '# escape=\\'
}, {
emptyLine: true
}, {
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8ENV'
argument: 'codenvy/ubuntu_jdk8'
}, {
comment: '#ENV myCat fluffy'
}, {
@ -103,14 +334,24 @@ describe('Simper dockerfile parser', () => {
}, {
instruction: 'ENV',
argument: ['myName', 'John Doe']
}, {
instruction: 'ENV',
argument: ['myText', 'long \nmultiline \nvalue']
}, {
instruction: 'ENV',
argument: ['myVal', '\\ \\\\']
}];
expect(result).toEqual(expectedResult);
});
it('should stringify an object into a dockerfile', () => {
let instructions = [{
const instructions = [{
directive: '# escape=\\'
}, {
emptyLine: true
}, {
instruction: 'FROM',
argument: 'codenvy/ubuntu_jdk8ENV'
argument: 'codenvy/ubuntu_jdk8'
}, {
comment: '#ENV myCat fluffy'
}, {
@ -119,14 +360,26 @@ describe('Simper dockerfile parser', () => {
}, {
instruction: 'ENV',
argument: ['myName', 'John Doe']
}, {
instruction: 'ENV',
argument: ['myText', 'long \nmultiline \nvalue']
}, {
instruction: 'ENV',
argument: ['myVal', '\\ \\\\']
}];
let result = parser.dump(instructions);
const result = parser.dump(instructions);
let expectedResult = 'FROM codenvy/ubuntu_jdk8ENV'
+ '\n#ENV myCat fluffy'
+ '\nENV myDog Rex The Dog'
+ '\nENV myName John Doe';
const expectedResult = `# escape=\\
FROM codenvy/ubuntu_jdk8
#ENV myCat fluffy
ENV myDog Rex The Dog
ENV myName John Doe
ENV myText long \\
multiline \\
value
ENV myVal \\\\\ \\\\\\\\`;
expect(result.trim()).toEqual(expectedResult);
});

View File

@ -19,36 +19,105 @@ interface IRecipeLine {
instruction?: string;
argument?: string | string[];
comment?: string;
directive?: string;
emptyLine?: boolean;
}
export class DockerfileParser {
/**
* RegExp to match the very first instruction to be 'FROM'.
*/
fromRE: RegExp;
backslashLineBreakRE: RegExp;
lineBreakRE: RegExp;
commentLineRE: RegExp;
/**
* RegExp to match an empty line.
*/
emptyLineRE: RegExp;
/**
* RegExp to match a comment line.
*/
commentRE: RegExp;
/**
* RegExp to match a single dockerfile instruction.
*/
instructionRE: RegExp;
envVariablesRE: RegExp;
quotesTestRE: RegExp;
quoteAtStartReplaceRE: RegExp;
quoteAtEndReplaceRE: RegExp;
backslashSpaceRE: RegExp;
/**
* RegExp to match leading and trailing quotes.
*/
quotesRE: RegExp;
/**
* RegExp to match a dockerfile parsing directive.
*/
directiveRE: RegExp;
/**
* RegExp to match an environment variable in form <code>name="quoted value"</code>.
*/
envVariableQuotedValueRE: RegExp;
/**
* RegExp to match any line break.
*/
linebreaksRE: RegExp;
/**
* RegExp to match any escaped whitespace or line break.
*/
escapedWhitespacesAndLinebreaksRE: RegExp;
/**
* RegExp to match any escaped escape symbol.
*/
escapedEscapeSymbolsRE: RegExp;
/**
* RegExp to match escaped line break at start of the line.
*/
escapedLineBreakAtStartRE: RegExp;
/**
* RegExp to match environment variable name.
*/
variableNameRE: RegExp;
/**
* RegExp to match first unescaped whitespace or line break.
*/
unEscapedWhitespaceRE: RegExp;
/**
* Allowed values for parsing directives.
*/
directiveValues: {
escape: string[],
[name: string]: string[]
};
/**
* Parsing directives with default values.
*/
directives: {
escape?: string,
[name: string]: string
};
/**
* Current escape to be used as part of a RegExp
*/
escape: string;
/**
* RegExp to match any escape symbol.
*/
escapeRE: RegExp;
constructor() {
this.fromRE = /^FROM\s+\w+/m;
this.backslashLineBreakRE = /\\\r?\n(\s+)?/;
this.lineBreakRE = /\r?\n/;
this.commentLineRE = /^#/;
this.instructionRE = /(\w+)\s+?(.+)/;
this.envVariablesRE = /(?:^|(?:\s+))([^\s=]+?)=([^=]+?)(?:(?=\s+\w+=)|$)/g;
// | | | |
// | | | \- start of next variable name or end of line
// | | \- variable value
// | \- variable name
// \- start of line or spaces before variable name
this.quotesTestRE = /^(["']).+(\1)$/;
this.quoteAtStartReplaceRE = /^["']/g;
this.quoteAtEndReplaceRE = /["']$/g;
this.backslashSpaceRE = /\\\s/g;
this.fromRE = /^FROM$/i;
this.emptyLineRE = /^\s*\r?\n/;
this.commentRE = /^\s*#/;
this.quotesRE = /^["']|['"]$/g;
this.envVariableQuotedValueRE = /^([^\s=]+?)=['"]([^'"]+?)['"]\s*/;
this.linebreaksRE = /(\r?\n)/g;
this.variableNameRE = /^([a-z_][a-z0-9_]*)=/i;
this.directiveValues = {
escape: ['\\', '`']
};
this.directives = {};
const knownDirectives = 'escape';
this.directiveRE = new RegExp('^\\s*#\\s*(' + knownDirectives + ')\\s*=\\s*([^\\s]+)', 'i');
// set default parsing directive
this.updateDirectives('escape', this.directiveValues.escape[0]);
}
/**
@ -58,105 +127,100 @@ export class DockerfileParser {
* @returns {IRecipeLine[]}
*/
parse(content: string): IRecipeLine[] {
if (!this.fromRE.test(content)) {
throw new TypeError('Dockerfile should start with \'FROM\' instruction. Cannot parse this recipe.');
}
let recipeContent = content;
// join multiline instructions
content = this._joinMultilineInstructions(content);
const instructions: IRecipeLine[] = [];
const uniqueDirectives: string[] = [];
let counter = 1000;
let lookingForDirectives = true;
let firstInstruction = true;
// split dockerfile into separate instruction lines
let instructionLines: string[] = content.split(this.lineBreakRE);
// set default parsing directive
this.updateDirectives('escape', this.directiveValues.escape[0]);
// split instruction line into instruction and argument
let instructions: IRecipeLine[] = [];
instructionLines.forEach((line: string) => {
line = line.trim();
while (recipeContent.length && counter) {
counter--;
// check for comment line
if (this.commentLineRE.test(line)) {
instructions.push({comment: line});
return;
// process parsing directive
if (lookingForDirectives) {
if (!this.emptyLineRE.test(recipeContent) && this.directiveRE.test(recipeContent)) {
const parts = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n')),
directiveStr = parts[0];
recipeContent = parts[1];
let [ , name, value] = this.directiveRE.exec(directiveStr);
if (this.directiveValues[name].indexOf(value) === -1) {
// directive value is not allowed
// hence this line should be treated as comment
instructions.push({comment: directiveStr});
lookingForDirectives = false;
continue;
}
name = name.toLowerCase();
if (uniqueDirectives.indexOf(name) !== -1) {
throw new TypeError(`Directive "${name}" is invalid due to appearing twice.`);
}
uniqueDirectives.push(name);
this.updateDirectives(name, value);
instructions.push({
directive: directiveStr
});
continue;
}
lookingForDirectives = false;
}
let m = line.match(this.instructionRE);
if (m) {
let instruction = m[1],
argument = m[2];
// process empty line
if (this.emptyLineRE.test(recipeContent)) {
const parts = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n'));
recipeContent = parts[1];
instructions.push({emptyLine: true});
continue;
}
// process comment
if (this.commentRE.test(recipeContent)) {
const parts = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n')),
commentStr = parts[0];
recipeContent = parts[1];
instructions.push({comment: commentStr});
continue;
}
// process instruction
if (this.instructionRE.test(recipeContent)) {
const [fullMatch, instruction, argument] = this.instructionRE.exec(recipeContent);
if (firstInstruction && !this.fromRE.test(instruction)) {
throw new TypeError('Dockerfile should start with \'FROM\' instruction.');
}
firstInstruction = false;
// parse argument
let results: IRecipeLine[] = this._parseArgument(instruction, argument);
let results: IRecipeLine[] = this.parseArgument(instruction, argument);
results.forEach((result: IRecipeLine) => {
instructions.push(result);
});
const parts = this.splitBySymbolAtIndex(recipeContent, fullMatch.length);
recipeContent = parts[1];
continue;
}
});
return instructions;
}
/**
* Remove line breaks from lines which end with backslash
*
* @param content {string}
* @returns {string}
* @private
*/
_joinMultilineInstructions(content: string): string {
return content.replace(this.backslashLineBreakRE, '');
}
/**
* Parses an argument string depending on instruction
*
* @param instruction {string}
* @param argumentStr {string}
* @returns {IRecipeLine[]}
* @private
*/
_parseArgument(instruction: string, argumentStr: string): IRecipeLine[] {
let results: IRecipeLine[] = [];
switch (instruction) {
case 'ENV':
let firstSpaceIndex = argumentStr.indexOf(' '),
firstEqualIndex = argumentStr.indexOf('=');
if (firstEqualIndex > -1 && (firstSpaceIndex === -1 || firstEqualIndex < firstSpaceIndex)) {
// this argument string contains one or more environment variables
let match;
while (match = this.envVariablesRE.exec(argumentStr)) {
let name: string = match[1],
value: string = match[2];
if (this.quotesTestRE.test(value)) {
value = value.replace(this.quoteAtStartReplaceRE, '');
value = value.replace(this.quoteAtEndReplaceRE, '');
}
if (this.backslashSpaceRE.test(value)) {
value = value.replace(this.backslashSpaceRE, ' ');
}
results.push({
instruction: instruction,
argument: [name, value]
});
}
} else {
// this argument string contains only one environment variable
results.push({
instruction: instruction,
argument: [argumentStr.slice(0, firstSpaceIndex), argumentStr.slice(firstSpaceIndex + 1)]
});
}
break;
default:
results.push({
instruction: instruction,
argument: argumentStr
});
// got weird line
const [line, ] = this.splitBySymbolAtIndex(recipeContent, this.getSplitIndex(recipeContent, '\n'));
throw new TypeError(`Cannot parse recipe from line: ${line}`);
}
return results;
return instructions;
}
/**
@ -169,29 +233,239 @@ export class DockerfileParser {
let content = '';
instructions.forEach((line: IRecipeLine) => {
if (line.comment) {
if (line.emptyLine) {
content += '\n';
} else if (line.directive) {
content += line.directive + '\n';
} else if (line.comment) {
content += line.comment + '\n';
} else {
content += line.instruction + ' ' + this._stringifyArgument(line) + '\n';
content += line.instruction + ' ' + this.stringifyArgument(line) + '\n';
}
});
return content;
}
/**
* Returns index of given symbol in the string.
*
* @param {string} content a string
* @param {string} delimiter a substring, position of which has to be found.
* @return {number}
*/
private getSplitIndex(content: string, delimiter: string): number {
return content.indexOf(delimiter) === -1 ? content.length : content.indexOf(delimiter);
}
/**
* Builds a RegExp to match any escape symbol.
* Builds a RegExp to match a dockerfile instruction.
* Builds a RegExp to match any escaped whitespace or line break.
* Builds a RegExp to match any escaped whitespace or line break.
* Builds a RegExp to match escaped line break at line start.
* Builds a RegExp to match first unescaped whitespace or line break.
*/
private buildParsingDirectiveSpecificRegExps(): void {
this.escapeRE = new RegExp('(' + this.escape + ')', 'g');
const instructions = 'FROM|RUN|CMD|LABEL|MAINTAINER|EXPOSE|ENV|ADD|COPY|ENTRYPOINT|VOLUME|USER|WORKDIR|ARG|ONBUILD|STOPSIGNAL|HEALTHCHECK|SHELL';
this.instructionRE = new RegExp('^\\s*(' + instructions + ')\\s+((?:.|' + this.escape + '\\r?\\n)+(?!\\r?\\n).)', 'i');
this.escapedWhitespacesAndLinebreaksRE = new RegExp('([^' + this.escape + ']?(?:' + this.escape + '{2})*)' + this.escape + '(\\s|\\r?\\n|' + this.escape + ')', 'g');
this.escapedEscapeSymbolsRE = new RegExp('' + this.escape + '(' + this.escape + ')', 'g');
this.escapedLineBreakAtStartRE = new RegExp('^' + this.escape + '\\r?\\n');
this.unEscapedWhitespaceRE = new RegExp('[^' + this.escape + '](?:' + this.escape + '{2})*(\\s|\\r?\\n)');
}
/**
* Updates parsing directives object and rebuilds appropriate RegExp.
*
* @param {string} directive a directive's name
* @param {string} value a directive's value
*/
private updateDirectives(directive: string, value: string): void {
this.escape = value === '\\' ? '\\\\' : value;
this.directives[directive] = value;
switch (directive) {
case 'escape':
this.buildParsingDirectiveSpecificRegExps();
break;
}
}
/**
* Splits a string by symbol at an index.
*
* @param {string} what a string to be split.
* @param {number} where an index to split the string.
* @return {[string,string]}
*/
private splitBySymbolAtIndex(what: string, where: number): string[] {
if (where < 0 || where >= what.length) {
where = what.length;
return [what, ''];
}
return [what.slice(0, where), what.slice(where + 1)];
}
/**
* Parses an argument string depending on instruction
*
* @param instruction {string}
* @param argumentStr {string}
* @returns {IRecipeLine[]}
*/
private parseArgument(instruction: string, argumentStr: string): IRecipeLine[] {
const results: IRecipeLine[] = [];
switch (instruction) {
case 'ENV':
const variables: string[][] = this.parseENVInstruction(argumentStr);
variables.forEach((variable: string[]) => {
results.push({
instruction: instruction,
argument: variable
});
});
break;
default:
results.push({
instruction: instruction,
argument: argumentStr
});
}
return results;
}
private parseENVInstruction(content: string): string[][] {
const results: string[][] = [];
const firstSpaceIndex = content.indexOf(' '),
firstEqualIndex = content.indexOf('=');
if (firstEqualIndex === -1 && firstSpaceIndex === -1) {
throw new TypeError(`Cannot parse environment variable name and value from string "${content}"`);
}
if (firstSpaceIndex > -1 && (firstEqualIndex === -1 || firstSpaceIndex < firstEqualIndex)) {
// this argument string contains only one environment variable
let [name, value] = [content.slice(0, firstSpaceIndex), content.slice(firstSpaceIndex + 1)];
value = this.unEscapeString(value);
results.push([name, value]);
} else {
// this argument string contains one or more environment variables
let count = 100;
while (content.length && count) {
count--;
// remove a linebreak at the start of string
content = content.replace(this.escapedLineBreakAtStartRE, '');
// remove a whitespace at the start of string
content = content.replace(/^\s+/, '');
// check if string begins with variable name
if (!this.variableNameRE.test(content)) {
throw new TypeError(`Cannot parse environment variable name and value from string "${content}"`);
}
if (this.envVariableQuotedValueRE.test(content)) {
/* variable with quoted value */
let [fullMatch, name, value] = this.envVariableQuotedValueRE.exec(content);
value = value.replace(this.quotesRE, '');
results.push([name, value]);
const parts = this.splitBySymbolAtIndex(content, fullMatch.length - 1);
// cut processed variable from the string
content = parts[1];
} else {
/* variable with escaped value */
// look for the point where the variable ends
const unEscapedWhitespaceMatch = this.unEscapedWhitespaceRE.exec(content);
let variableLength: number;
if (!unEscapedWhitespaceMatch) {
// the rest of string is a single variable
variableLength = content.length;
} else {
variableLength = unEscapedWhitespaceMatch.index + unEscapedWhitespaceMatch.length - 1;
}
const parts = this.splitBySymbolAtIndex(content, variableLength);
const variableStr = parts[0];
// cut processed variable from the string
content = parts[1];
const equalIndex = variableStr.indexOf('=');
const varParts = this.splitBySymbolAtIndex(variableStr, equalIndex);
let name = varParts[0],
value = varParts[1];
value = this.unEscapeString(value);
results.push([name, value]);
}
}
}
return results;
}
/**
* Dumps argument object depending on instruction.
*
* @param line {IRecipeLine}
* @param {IRecipeLine} line
* @returns {string}
* @private
*/
_stringifyArgument(line: IRecipeLine): string {
private stringifyArgument(line: IRecipeLine): string {
switch (line.instruction) {
case 'ENV':
return (line.argument as string[]).join(' ');
const [name, value] = line.argument as string[];
return name + ' ' + this.escapeString(value);
default:
return line.argument as string;
}
}
private escapeString(content: string): string {
// this replace should be the very first
content = content.replace(this.escapeRE, this.directives.escape + '$1');
content = content.replace(this.linebreaksRE, this.directives.escape + '$1');
return content;
}
/**
* Unescapes whitespaces and escape symbols.
*
* @param {string} content a string to process.
*/
private unEscapeString(content: string): string {
content = content.replace(this.escapedWhitespacesAndLinebreaksRE, '$1$2');
// this replace should be the very last
content = content.replace(this.escapedEscapeSymbolsRE, '$1');
return content;
}
}

View File

@ -9,7 +9,6 @@
* Codenvy, S.A. - initial API and implementation
*/
'use strict';
export interface INamespace {
id: string;
label: string;
@ -25,6 +24,9 @@ export class CheNamespaceRegistry {
private $q: ng.IQService;
private fetchPromise: ng.IPromise<any>;
private namespaces : INamespace[];
private emptyMessage: string;
private caption: string;
private getAdditionalInfoFunction: Function;
/**
* Default constructor that is using resource
@ -33,6 +35,8 @@ export class CheNamespaceRegistry {
constructor($q: ng.IQService) {
this.$q = $q;
this.namespaces = [];
this.caption = 'Namespace';
}
/**
@ -76,4 +80,58 @@ export class CheNamespaceRegistry {
getNamespaces() : INamespace[] {
return this.namespaces;
}
/**
* Set empty message (message is displayed, when no namespaces).
*
* @param message empty message
*/
setEmptyMessage(message: string): void {
this.emptyMessage = message;
}
/**
* Returns empty message to display, when no namespaces.
*
* @returns {string}
*/
getEmptyMessage(): string {
return this.emptyMessage ? this.emptyMessage : null;
}
/**
* Set display caption of the namespaces.
*
* @param caption namespaces caption
*/
setCaption(caption: string): void {
this.caption = caption;
}
/**
* Returns the caption of the namespaces.
*
* @returns {string} namepsaces caption
*/
getCaption(): string {
return this.caption;
}
/**
* Sets the function for retrieving available RAM for the namespaces.
*
* @param getAdditionalInfo additional information function
*/
setGetAdditionalInfo(getAdditionalInfo: Function): void {
this.getAdditionalInfoFunction = getAdditionalInfo;
}
/**
* Returns function, that returns promise.
*
* @returns {Function}
*/
getAdditionalInfo(): Function {
return this.getAdditionalInfoFunction;
}
}

View File

@ -68,12 +68,10 @@ pre_cmd_archetype() {
fi
ARCHETYPE_ACTION="all"
ARCHETYPE_ID="che-plugin-ide-menu-archetype"
# ARCHETYPE_VERSION=$(get_image_version)
ARCHETYPE_ID="plugin-menu-archetype"
##############################
# REPLACE THIS WITH $(get_image_version) AFTER CI SYSTEMS GENERATING
ARCHETYPE_VERSION=5.6.0-SNAPSHOT
ARCHETYPE_VERSION=$(get_image_version)
ASSEMBLY_VERSION=$ARCHETYPE_VERSION
ASSEMBLY_GROUP="com.sample"
ASSEMBLY_ID="assembly"

View File

@ -0,0 +1,3 @@
IMAGE_INIT=eclipse/che-init:5.10.0
IMAGE_CHE=eclipse/che-server:5.10.0
IMAGE_COMPOSE=docker/compose:1.8.1

View File

@ -0,0 +1,24 @@
eclipse/alpine_jdk8
eclipse/aspnet
eclipse/centos_jdk8
eclipse/cpp_gcc
eclipse/debian_jdk8
eclipse/debian_jdk8_node
eclipse/debian_jre
eclipse/dotnet_core
eclipse/hadoop-dev
eclipse/meteor
eclipse/node
eclipse/php
eclipse/platformio
eclipse/ruby_rails
eclipse/selenium
eclipse/ubuntu_android
eclipse/ubuntu_go
eclipse/ubuntu_gradle
eclipse/ubuntu_jdk8
eclipse/ubuntu_jre
eclipse/ubuntu_python
eclipse/ubuntu_wildfly8
registry.centos.org/che-stacks/vertx

View File

@ -0,0 +1,3 @@
IMAGE_INIT=eclipse/che-init:5.8.1
IMAGE_CHE=eclipse/che-server:5.8.1
IMAGE_COMPOSE=docker/compose:1.8.1

View File

@ -0,0 +1,24 @@
eclipse/alpine_jdk8
eclipse/aspnet
eclipse/centos_jdk8
eclipse/cpp_gcc
eclipse/debian_jdk8
eclipse/debian_jdk8_node
eclipse/debian_jre
eclipse/dotnet_core
eclipse/hadoop-dev
eclipse/meteor
eclipse/node
eclipse/php
eclipse/platformio
eclipse/ruby_rails
eclipse/selenium
eclipse/ubuntu_android
eclipse/ubuntu_go
eclipse/ubuntu_gradle
eclipse/ubuntu_jdk8
eclipse/ubuntu_jre
eclipse/ubuntu_python
eclipse/ubuntu_wildfly8
registry.centos.org/che-stacks/vertx

View File

@ -1 +1 @@
5.8.0
5.9.0

View File

@ -17,13 +17,13 @@
<parent>
<artifactId>maven-depmgt-pom</artifactId>
<groupId>org.eclipse.che.depmgt</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>dto-typescript</artifactId>
<packaging>pom</packaging>
<name>Che TypeScript DTO</name>
<properties>
<che.version>5.9.0-SNAPSHOT</che.version>
<che.version>5.10.0-SNAPSHOT</che.version>
</properties>
<repositories>
<repository>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-dyna-provider-generator-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-ide-api</artifactId>
@ -53,7 +53,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-auth</artifactId>
<artifactId>che-core-api-auth-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>

View File

@ -21,6 +21,7 @@ import java.util.Objects;
import static java.util.Collections.unmodifiableList;
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_GOAL_ATTRIBUTE_NAME;
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_PREVIEW_URL_ATTRIBUTE_NAME;
/** Data object for {@link Command}. */
public class CommandImpl implements Command {
@ -123,6 +124,18 @@ public class CommandImpl implements Command {
getAttributes().put(COMMAND_GOAL_ATTRIBUTE_NAME, goalId);
}
/** Returns command's preview URL or {@code null} if none. */
@Nullable
public String getPreviewURL() {
return getAttributes().get(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME);
}
/** Sets command's preview URL. */
public void setPreviewURL(String previewURL) {
getAttributes().put(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME, previewURL);
}
/** Returns command's applicable context. */
public ApplicableContext getApplicableContext() {
return context;

View File

@ -22,10 +22,6 @@ import org.eclipse.che.ide.api.debug.Breakpoint;
@DTO
public interface StorableBreakpointDto {
void setActive(boolean active);
boolean isActive();
void setLineNumber(int lineNumber);
int getLineNumber();

View File

@ -16,6 +16,8 @@ import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.project.shared.dto.event.FileStateUpdateDto;
import org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.event.FileContentUpdateEvent;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.resources.ExternalResourceDelta;
@ -25,7 +27,9 @@ import org.eclipse.che.ide.resource.Path;
import org.eclipse.che.ide.util.loging.Log;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import java.util.List;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.EMERGE_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUCCESS;
@ -44,14 +48,19 @@ public class EditorFileStatusNotificationOperation implements JsonRpcRequestBiOp
private final EventBus eventBus;
private final DeletedFilesController deletedFilesController;
private final Provider<EditorAgent> editorAgentProvider;
private final AppContext appContext;
private NotificationManager notificationManager;
@Inject
public EditorFileStatusNotificationOperation(EventBus eventBus, DeletedFilesController deletedFilesController, AppContext appContext) {
public EditorFileStatusNotificationOperation(EventBus eventBus,
DeletedFilesController deletedFilesController,
Provider<EditorAgent> editorAgentProvider,
AppContext appContext) {
this.eventBus = eventBus;
this.deletedFilesController = deletedFilesController;
this.editorAgentProvider = editorAgentProvider;
this.appContext = appContext;
}
@ -88,10 +97,21 @@ public class EditorFileStatusNotificationOperation implements JsonRpcRequestBiOp
appContext.getWorkspaceRoot().synchronize(new ExternalResourceDelta(path, path, REMOVED));
if (notificationManager != null && !deletedFilesController.remove(stringPath)) {
notificationManager.notify("External operation", "File '" + name + "' is removed", SUCCESS, EMERGE_MODE);
closeOpenedEditor(path);
}
break;
}
}
}
private void closeOpenedEditor(Path path) {
final EditorAgent editorAgent = editorAgentProvider.get();
final List<EditorPartPresenter> openedEditors = editorAgent.getOpenedEditors();
for (EditorPartPresenter openEditor : openedEditors) {
if (openEditor.getEditorInput().getFile().getLocation().equals(path)) {
editorAgent.closeEditor(openEditor);
}
}
}
}

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-ide-app</artifactId>
<packaging>jar</packaging>

View File

@ -0,0 +1,93 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.toolbar.previews;
import org.eclipse.che.api.core.model.machine.MachineRuntimeInfo;
import org.eclipse.che.api.core.model.machine.Server;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.machine.DevMachine;
import java.util.Map.Entry;
import java.util.Objects;
/** Represents an item for displaying in the 'Previews' list. */
class PreviewUrl {
private final AppContext appContext;
private final String url;
private final String displayName;
PreviewUrl(String url, AppContext appContext) {
this.url = url;
this.appContext = appContext;
this.displayName = getDisplayNameForPreviewUrl(url);
}
/** Returns actual preview URL. */
String getUrl() {
return url;
}
/**
* Returns preview URL in user-friendly form.
* E.g. {@code dev-machine:8080} instead of {@code http://172.19.21.35:32801}.
*/
String getDisplayName() {
return displayName;
}
private String getDisplayNameForPreviewUrl(String previewUrl) {
final DevMachine devMachine = appContext.getDevMachine();
final MachineRuntimeInfo devMachineRuntime = devMachine.getRuntime();
if (devMachineRuntime == null) {
return previewUrl;
}
for (Entry<String, ? extends Server> entry : devMachineRuntime.getServers().entrySet()) {
Server server = entry.getValue();
String serverUrl = server.getUrl();
if (serverUrl == null) {
continue;
}
if (previewUrl.startsWith(serverUrl)) {
String port = entry.getKey();
// server's port may be in form of '8080/tcp' so need to cut protocol name
final int slashIndex = port.lastIndexOf('/');
if (slashIndex > -1) {
port = port.substring(0, slashIndex);
}
return previewUrl.replace(serverUrl, devMachine.getDisplayName() + ':' + port);
}
}
return previewUrl;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PreviewUrl that = (PreviewUrl)o;
return Objects.equals(url, that.url) &&
Objects.equals(displayName, that.displayName);
}
@Override
public int hashCode() {
return Objects.hash(url, displayName);
}
}

View File

@ -1,47 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.toolbar.previews;
import java.util.Objects;
/** Represents an item for displaying in the 'Previews' list. */
class PreviewUrlItem {
private final String url;
private final String displayName;
PreviewUrlItem(String url, String displayName) {
this.url = url;
this.displayName = displayName;
}
public String getUrl() {
return url;
}
public String getDisplayName() {
return displayName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PreviewUrlItem that = (PreviewUrlItem)o;
return Objects.equals(url, that.url) &&
Objects.equals(displayName, that.displayName);
}
@Override
public int hashCode() {
return Objects.hash(url, displayName);
}
}

View File

@ -24,17 +24,17 @@ import static com.google.gwt.dom.client.Style.Unit.PX;
/**
* Renders widgets for the 'Previews' list. Always returns the same
* instance of the header widget which is shared among all {@link PreviewUrlItem}s.
* instance of the header widget which is shared among all {@link PreviewUrl}s.
*/
class PreviewUrlItemRenderer implements DropdownListItemRenderer {
static final HeaderWidget HEADER_WIDGET = new HeaderWidget();
private final BaseListItem<PreviewUrlItem> item;
private final BaseListItem<PreviewUrl> item;
private Widget listWidget;
PreviewUrlItemRenderer(BaseListItem<PreviewUrlItem> item) {
PreviewUrlItemRenderer(BaseListItem<PreviewUrl> item) {
this.item = item;
}

View File

@ -17,18 +17,13 @@ import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.core.model.machine.MachineRuntimeInfo;
import org.eclipse.che.api.core.model.machine.Server;
import org.eclipse.che.api.core.model.workspace.WorkspaceRuntime;
import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessResponseDto;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.machine.shared.dto.execagent.GetProcessesResponseDto;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseProvider;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.machine.DevMachine;
import org.eclipse.che.ide.api.machine.ExecAgentCommandManager;
import org.eclipse.che.ide.api.machine.events.ProcessFinishedEvent;
import org.eclipse.che.ide.api.machine.events.ProcessStartedEvent;
@ -38,11 +33,9 @@ import org.eclipse.che.ide.api.macro.MacroProcessor;
import org.eclipse.che.ide.api.mvp.Presenter;
import org.eclipse.che.ide.command.toolbar.ToolbarMessages;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.List;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_PREVIEW_URL_ATTRIBUTE_NAME;
/** Drives the UI for displaying preview URLs of the running processes. */
@Singleton
@ -91,76 +84,37 @@ public class PreviewsPresenter implements Presenter, PreviewsView.ActionDelegate
eventBus.addHandler(ProcessFinishedEvent.TYPE, event -> updateView());
}
/** Updates view with preview URLs of all running processes. */
/** Updates view with the preview URLs of running processes. */
private void updateView() {
view.removeAllURLs();
final WorkspaceRuntime runtime = appContext.getActiveRuntime();
if (runtime != null) {
runtime.getMachines().forEach(machine -> execAgentClient.getProcesses(machine.getId(), false).then(processes -> {
processes.forEach(process -> getPreviewUrl(process.getPid(), machine).then(view::addUrl).catchError(ignore -> {}));
}));
runtime.getMachines().forEach(machine -> {
Promise<List<GetProcessesResponseDto>> machineProcesses = execAgentClient.getProcesses(machine.getId(), false);
machineProcesses.then(processes -> {
processes.forEach(process -> getPreviewUrl(process).then(view::addUrl).catchError(ignore -> {
}));
});
});
}
}
/**
* Returns promise that resolves preview URL of the command which has launched
* the process with the given {@code pid} on the specified {@code machine}.
* Returns promise that resolves preview URL for the given process.
* Returns promise that rejects with an error if preview URL isn't available.
*/
private Promise<PreviewUrlItem> getPreviewUrl(int pid, Machine machine) {
return execAgentClient.getProcess(machine.getId(), pid)
// get command's preview URL
.then((Function<GetProcessResponseDto, String>)process -> {
final Optional<CommandImpl> commandOptional = commandManager.getCommand(process.getName());
private Promise<String> getPreviewUrl(GetProcessesResponseDto process) {
final String previewUrl = commandManager.getCommand(process.getName())
.map(CommandImpl::getPreviewURL)
.orElse(null);
return commandOptional.map(command -> command.getAttributes().get(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME))
.orElse(null);
})
// expand macros used in preview URL
.thenPromise(previewUrl -> {
if (!isNullOrEmpty(previewUrl)) {
return macroProcessorProvider.get().expandMacros(previewUrl);
}
return promiseProvider.reject(new Exception(messages.previewsNotAvailableError()));
})
// compose preview URL's display name
.then((Function<String, PreviewUrlItem>)previewUrl -> new PreviewUrlItem(previewUrl,
getPreviewUrlDisplayName(previewUrl)
.orElse(previewUrl)));
}
private Optional<String> getPreviewUrlDisplayName(String previewUrl) {
final DevMachine devMachine = appContext.getDevMachine();
final MachineRuntimeInfo devMachineRuntime = devMachine.getRuntime();
if (devMachineRuntime == null) {
return Optional.empty();
if (!isNullOrEmpty(previewUrl)) {
return macroProcessorProvider.get().expandMacros(previewUrl);
}
for (Entry<String, ? extends Server> entry : devMachineRuntime.getServers().entrySet()) {
Server server = entry.getValue();
String serverUrl = server.getUrl();
if (serverUrl == null) {
continue;
}
if (previewUrl.startsWith(serverUrl)) {
String displayName = previewUrl.replace(serverUrl, devMachine.getDisplayName() + ':' + entry.getKey());
// cut protocol from display name
final int protocolIndex = displayName.lastIndexOf('/');
if (protocolIndex > -1) {
displayName = displayName.substring(0, protocolIndex);
}
return Optional.of(displayName);
}
}
return Optional.empty();
return promiseProvider.reject(new Exception(messages.previewsNotAvailableError()));
}
@Override
@ -169,7 +123,7 @@ public class PreviewsPresenter implements Presenter, PreviewsView.ActionDelegate
}
@Override
public void onUrlChosen(PreviewUrlItem previewUrlItem) {
Window.open(previewUrlItem.getUrl(), "_blank", null);
public void onUrlChosen(String previewUrl) {
Window.open(previewUrl, "_blank", null);
}
}

View File

@ -16,10 +16,10 @@ import org.eclipse.che.ide.api.mvp.View;
public interface PreviewsView extends View<PreviewsView.ActionDelegate> {
/** Add preview URL to the view. */
void addUrl(PreviewUrlItem previewUrlItem);
void addUrl(String previewUrl);
/** Remove preview URL from the view. */
void removeUrl(PreviewUrlItem previewUrlItem);
void removeUrl(String previewUrl);
/** Remove all preview URLs from the view. */
void removeAllURLs();
@ -27,6 +27,6 @@ public interface PreviewsView extends View<PreviewsView.ActionDelegate> {
interface ActionDelegate {
/** Called when preview URL has been chosen. */
void onUrlChosen(PreviewUrlItem previewUrlItem);
void onUrlChosen(String previewUrl);
}
}

View File

@ -16,6 +16,7 @@ import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.command.toolbar.ToolbarMessages;
import org.eclipse.che.ide.ui.Tooltip;
import org.eclipse.che.ide.ui.dropdown.BaseListItem;
@ -34,18 +35,21 @@ import static org.eclipse.che.ide.ui.menu.PositionController.VerticalAlign.BOTTO
public class PreviewsViewImpl implements PreviewsView {
/** Mapping of URL to list item. */
private final Map<PreviewUrlItem, BaseListItem<PreviewUrlItem>> listItems;
private final Map<String, BaseListItem<PreviewUrl>> listItems;
private final DropdownList dropdownList;
private final NoPreviewsItem noPreviewsItem;
private final NoPreviewsItemRenderer noPreviewsItemRenderer;
private final ToolbarMessages messages;
private final AppContext appContext;
private ActionDelegate delegate;
@Inject
public PreviewsViewImpl(ToolbarMessages messages) {
public PreviewsViewImpl(ToolbarMessages messages, AppContext appContext) {
this.messages = messages;
this.appContext = appContext;
listItems = new HashMap<>();
dropdownList = new DropdownList(HEADER_WIDGET, false);
@ -84,23 +88,24 @@ public class PreviewsViewImpl implements PreviewsView {
}
@Override
public void addUrl(PreviewUrlItem previewUrlItem) {
if (listItems.containsKey(previewUrlItem)) {
return; // no sense to add the equals URLs even if they belong to different commands
public void addUrl(String previewUrl) {
if (listItems.containsKey(previewUrl)) {
return;
}
BaseListItem<PreviewUrlItem> listItem = new BaseListItem<>(previewUrlItem);
PreviewUrlItemRenderer renderer = new PreviewUrlItemRenderer(listItem);
final PreviewUrl displayablePreviewUrl = new PreviewUrl(previewUrl, appContext);
final BaseListItem<PreviewUrl> listItem = new BaseListItem<>(displayablePreviewUrl);
final PreviewUrlItemRenderer renderer = new PreviewUrlItemRenderer(listItem);
listItems.put(previewUrlItem, listItem);
listItems.put(previewUrl, listItem);
dropdownList.addItem(listItem, renderer);
checkNoPreviewsItem();
}
@Override
public void removeUrl(PreviewUrlItem previewUrlItem) {
final BaseListItem<PreviewUrlItem> listItem = listItems.remove(previewUrlItem);
public void removeUrl(String previewUrl) {
final BaseListItem<PreviewUrl> listItem = listItems.remove(previewUrl);
if (listItem != null) {
dropdownList.removeItem(listItem);

View File

@ -10,9 +10,7 @@
*******************************************************************************/
package org.eclipse.che.ide.context;
import com.google.common.collect.Sets;
import com.google.gwt.core.client.Callback;
import com.google.gwt.user.client.Random;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@ -50,22 +48,18 @@ import org.eclipse.che.ide.resources.ResourceManagerInitializer;
import org.eclipse.che.ide.resources.impl.ResourceDeltaImpl;
import org.eclipse.che.ide.resources.impl.ResourceManager;
import org.eclipse.che.ide.statepersistance.AppStateManager;
import org.eclipse.che.ide.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.gwt.user.client.Random.nextInt;
import static java.util.Arrays.binarySearch;
import static java.util.Arrays.copyOf;
import static java.util.Arrays.sort;
import static org.eclipse.che.ide.api.resources.Resource.PROJECT;
import static java.util.Collections.addAll;
import static org.eclipse.che.ide.api.resources.ResourceDelta.ADDED;
import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_FROM;
import static org.eclipse.che.ide.api.resources.ResourceDelta.MOVED_TO;
import static org.eclipse.che.ide.api.resources.ResourceDelta.REMOVED;
import static org.eclipse.che.ide.api.resources.ResourceDelta.SYNCHRONIZED;
import static org.eclipse.che.ide.api.resources.ResourceDelta.UPDATED;
/**
@ -85,25 +79,29 @@ public class AppContextImpl implements AppContext,
ResourceManagerInitializer {
private static final String APP_ID = String.valueOf(nextInt(Integer.MAX_VALUE));
private static final Project[] NO_PROJECTS = {};
private final QueryParameters queryParameters;
private final List<String> projectsInImport;
private final EventBus eventBus;
private final ResourceManager.ResourceManagerFactory resourceManagerFactory;
private final Provider<EditorAgent> editorAgentProvider;
private final Provider<AppStateManager> appStateManager;
private final QueryParameters queryParameters;
private final List<String> projectsInImport;
private final List<Project> rootProjects = newArrayList();
private final List<Resource> selectedResources = newArrayList();
private Workspace usersWorkspace;
private Workspace userWorkspace;
private CurrentUser currentUser;
private FactoryDto factory;
private Path projectsRoot;
private ActiveRuntime runtime;
private ResourceManager resourceManager;
/**
* List of actions with parameters which comes from startup URL.
* Can be processed after IDE initialization as usual after starting ws-agent.
*/
private List<StartUpAction> startAppActions;
private Resource currentResource;
private Resource[] currentResources;
@Inject
public AppContextImpl(EventBus eventBus,
QueryParameters queryParameters,
@ -124,31 +122,39 @@ public class AppContextImpl implements AppContext,
eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this);
}
private static native String masterFromIDEConfig() /*-{
if ($wnd.IDE && $wnd.IDE.config) {
return $wnd.IDE.config.restContext;
} else {
return null;
}
}-*/;
@Override
public Workspace getWorkspace() {
return usersWorkspace;
return userWorkspace;
}
@Override
public void setWorkspace(Workspace workspace) {
if (workspace != null) {
usersWorkspace = workspace;
userWorkspace = workspace;
if (workspace.getRuntime() != null) {
runtime = new ActiveRuntime(workspace.getRuntime());
}
} else {
usersWorkspace = null;
userWorkspace = null;
runtime = null;
}
}
@Override
public String getWorkspaceId() {
if (usersWorkspace == null) {
if (userWorkspace == null) {
throw new IllegalArgumentException(getClass() + " Workspace can not be null.");
}
return usersWorkspace.getId();
return userWorkspace.getId();
}
@Override
@ -200,7 +206,6 @@ public class AppContextImpl implements AppContext,
return runtime.getDevMachine();
}
@Override
public void initResourceManager(final Callback<ResourceManager, Exception> callback) {
if (runtime.getDevMachine() == null) {
@ -208,17 +213,18 @@ public class AppContextImpl implements AppContext,
callback.onFailure(new NullPointerException("Dev machine is not initialized"));
}
if (projects != null) {
for (Project project : projects) {
if (!rootProjects.isEmpty()) {
for (Project project : rootProjects) {
eventBus.fireEvent(new ResourceChangedEvent(new ResourceDeltaImpl(project, REMOVED)));
}
projects = null;
rootProjects.clear();
}
resourceManager = resourceManagerFactory.newResourceManager(runtime.getDevMachine());
resourceManager.getWorkspaceProjects().then(projects -> {
AppContextImpl.this.projects = projects;
java.util.Arrays.sort(AppContextImpl.this.projects, ResourcePathComparator.getInstance());
rootProjects.clear();
addAll(rootProjects, projects);
rootProjects.sort(ResourcePathComparator.getInstance());
callback.onSuccess(resourceManager);
eventBus.fireEvent(new WorkspaceReadyEvent(projects));
}).catchError(error -> {
@ -228,7 +234,7 @@ public class AppContextImpl implements AppContext,
@Override
public String getWorkspaceName() {
return usersWorkspace.getConfig().getName();
return userWorkspace.getConfig().getName();
}
/** {@inheritDoc} */
@ -237,82 +243,60 @@ public class AppContextImpl implements AppContext,
final ResourceDelta delta = event.getDelta();
final Resource resource = delta.getResource();
/* Note: There is important to keep projects array in sorted state, because it is mutable and removing projects from it
need array to be sorted. Search specific projects realized with binary search. */
if (!(resource.getResourceType() == PROJECT && resource.getLocation().segmentCount() == 1)) {
return;
}
if (projects == null) {
return; //Normal situation, workspace config updated and project has not been loaded fully. Just skip this situation.
}
if (delta.getKind() == ADDED) {
Project[] newProjects = copyOf(projects, projects.length + 1);
newProjects[projects.length] = (Project)resource;
projects = newProjects;
sort(projects, ResourcePathComparator.getInstance());
} else if (delta.getKind() == REMOVED) {
int size = projects.length;
int index = java.util.Arrays.binarySearch(projects, resource, ResourcePathComparator.getInstance());
int numMoved = projects.length - index - 1;
if (numMoved > 0) {
System.arraycopy(projects, index + 1, projects, index, numMoved);
}
projects = copyOf(projects, --size);
if ((delta.getFlags() & (MOVED_FROM | MOVED_TO)) != 0) {
if (currentResource != null && currentResource.equals(delta.getResource())) {
currentResource = null;
}
if (currentResources != null) {
for (Resource currentResource : currentResources) {
if (currentResource.equals(delta.getResource())) {
currentResources = Arrays.remove(currentResources, currentResource);
for (Project rootProject : rootProjects) {
if (rootProject.getLocation().equals(delta.getFromPath()) && resource.isProject()) {
rootProjects.set(rootProjects.indexOf(rootProject), resource.asProject());
break;
}
}
for (Resource selectedResource : selectedResources) {
if (selectedResource.getLocation().equals(delta.getFromPath())) {
selectedResources.set(selectedResources.indexOf(selectedResource), resource);
break;
}
}
} else if (resource.getLocation().segmentCount() == 1 && resource.isProject()) {
boolean exists = rootProjects.stream().anyMatch(it -> it.getLocation().equals(resource.getLocation()));
if (!exists) {
rootProjects.add(resource.asProject());
rootProjects.sort(ResourcePathComparator.getInstance());
}
}
} else if (delta.getKind() == REMOVED) {
for (Project rootProject : rootProjects) {
if (rootProject.getLocation().equals(resource.getLocation()) && resource.isProject()) {
rootProjects.remove(rootProjects.indexOf(rootProject));
break;
}
}
for (Resource selectedResource : selectedResources) {
if (selectedResource.getLocation().equals(resource.getLocation())) {
selectedResources.remove(selectedResources.indexOf(selectedResource));
break;
}
}
} else if (delta.getKind() == UPDATED) {
int index = -1;
// Project may be moved to another location, so we need to remove previous one and store new project in cache.
if (delta.getFlags() == MOVED_FROM) {
for (int i = 0; i < projects.length; i++) {
if (projects[i].getLocation().equals(delta.getFromPath())) {
index = i;
break;
}
}
} else {
index = binarySearch(projects, resource);
}
if (index != -1) {
projects[index] = (Project)resource;
}
sort(projects, ResourcePathComparator.getInstance());
} else if (delta.getKind() == SYNCHRONIZED && resource.isProject() && resource.getLocation().segmentCount() == 1) {
for (int i = 0; i < projects.length; i++) {
if (projects[i].getLocation().equals(resource.getLocation())) {
projects[i] = (Project)resource;
for (Project rootProject : rootProjects) {
if (rootProject.getLocation().equals(resource.getLocation()) && resource.isProject()) {
rootProjects.set(rootProjects.indexOf(rootProject), resource.asProject());
break;
}
}
if (currentResources != null) {
for (int i = 0; i < currentResources.length; i++) {
if (currentResources[i].getLocation().equals(resource.getLocation())) {
currentResources[i] = resource;
break;
}
for (Resource selectedResource : selectedResources) {
if (selectedResource.getLocation().equals(resource.getLocation())) {
selectedResources.set(selectedResources.indexOf(selectedResource), resource);
break;
}
}
if (currentResource != null && currentResource.getLocation().equals(resource.getLocation())) {
currentResource = resource;
}
}
}
@ -332,54 +316,22 @@ public class AppContextImpl implements AppContext,
return;
}
currentResource = null;
currentResources = null;
selectedResources.clear();
if (selection == null || selection.getHeadElement() == null) {
return;
}
final Object headObject = selection.getHeadElement();
final List<?> allObjects = selection.getAllElements();
if (headObject instanceof HasDataObject) {
Object data = ((HasDataObject)headObject).getData();
if (data instanceof Resource) {
currentResource = (Resource)data;
}
} else if (headObject instanceof Resource) {
currentResource = (Resource)headObject;
}
Set<Resource> resources = Sets.newHashSet();
for (Object object : allObjects) {
if (object instanceof HasDataObject) {
Object data = ((HasDataObject)object).getData();
if (data instanceof Resource) {
resources.add((Resource)data);
if (selection != null) {
for (Object o : selection.getAllElements()) {
if (o instanceof HasDataObject && ((HasDataObject)o).getData() instanceof Resource) {
selectedResources.add((Resource)((HasDataObject)o).getData());
} else if (o instanceof Resource) {
selectedResources.add((Resource)o);
}
} else if (object instanceof Resource) {
resources.add((Resource)object);
}
}
currentResources = resources.toArray(new Resource[resources.size()]);
}
private final EventBus eventBus;
private final ResourceManager.ResourceManagerFactory resourceManagerFactory;
private final Provider<EditorAgent> editorAgentProvider;
private final Provider<AppStateManager> appStateManager;
private ResourceManager resourceManager;
private Project[] projects;
@Override
public Project[] getProjects() {
return projects == null ? new Project[0] : projects;
return rootProjects.toArray(new Project[rootProjects.size()]);
}
@Override
@ -391,22 +343,21 @@ public class AppContextImpl implements AppContext,
@Override
public Resource getResource() {
return currentResource;
return selectedResources.isEmpty() ? null : selectedResources.get(0);
}
@Override
public Resource[] getResources() {
return currentResources;
return selectedResources.toArray(new Resource[selectedResources.size()]);
}
@Override
public Project getRootProject() {
if (projects == null) {
if (rootProjects.isEmpty()) {
return null;
}
if (currentResource == null || currentResources == null) {
if (selectedResources.isEmpty()) {
EditorAgent editorAgent = editorAgentProvider.get();
if (editorAgent == null) {
return null;
@ -421,37 +372,36 @@ public class AppContextImpl implements AppContext,
if (file instanceof SyntheticNode) {
final Path projectPath = ((SyntheticNode)file).getProject();
for (Project project : projects) {
for (Project project : rootProjects) {
if (project.getLocation().equals(projectPath)) {
return project;
}
}
}
}
if (currentResource == null) {
return null;
}
} else {
Project root = null;
Project root = null;
for (Project project : projects) {
if (project.getLocation().isPrefixOf(currentResource.getLocation())) {
root = project;
for (Project project : rootProjects) {
if (project.getLocation().isPrefixOf(selectedResources.get(0).getLocation())) {
root = project;
break;
}
}
}
if (root == null) {
return null;
}
for (int i = 1; i < currentResources.length; i++) {
if (!root.getLocation().isPrefixOf(currentResources[i].getLocation())) {
if (root == null) {
return null;
}
}
return root;
for (int i = 1; i < selectedResources.size(); i++) {
if (!root.getLocation().isPrefixOf(selectedResources.get(i).getLocation())) {
return null;
}
}
return root;
}
}
@Override
@ -467,20 +417,18 @@ public class AppContextImpl implements AppContext,
@Override
public void onWorkspaceStopped(WorkspaceStoppedEvent event) {
appStateManager.get().persistWorkspaceState(getWorkspaceId()).then(ignored -> {
for (Project project : projects) {
for (Project project : rootProjects) {
eventBus.fireEvent(new ResourceChangedEvent(new ResourceDeltaImpl(project, REMOVED)));
}
projects = NO_PROJECTS;
rootProjects.clear();
resourceManager = null;
});
//goto close all editors
final EditorAgent editorAgent = editorAgentProvider.get();
final List<EditorPartPresenter> openedEditors = editorAgent.getOpenedEditors();
for (EditorPartPresenter editor : openedEditors) {
editorAgent.closeEditor(editor);
}
clearRuntime();
}
private void clearRuntime() {
runtime = null;
}
@ -491,7 +439,7 @@ public class AppContextImpl implements AppContext,
@Override
public String getMasterEndpoint() {
String fromUrl = queryParameters.getByName("master");
if(fromUrl == null || fromUrl.isEmpty())
if (fromUrl == null || fromUrl.isEmpty())
return masterFromIDEConfig();
else
return fromUrl;
@ -500,7 +448,7 @@ public class AppContextImpl implements AppContext,
@Override
public String getDevAgentEndpoint() {
String fromUrl = queryParameters.getByName("agent");
if(fromUrl == null || fromUrl.isEmpty())
if (fromUrl == null || fromUrl.isEmpty())
return runtime.getDevMachine().getWsAgentBaseUrl();
else
return fromUrl;
@ -515,13 +463,4 @@ public class AppContextImpl implements AppContext,
public ActiveRuntime getActiveRuntime() {
return runtime;
}
private static native String masterFromIDEConfig() /*-{
if ($wnd.IDE && $wnd.IDE.config) {
return $wnd.IDE.config.restContext;
} else {
return null;
}
}-*/;
}

View File

@ -279,8 +279,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
if (breakpointRenderer != null) {
breakpointRenderer.setLineActive(lineNumber, true);
}
preserveBreakpoints();
}
@Override
@ -311,8 +309,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
currentBreakpoint = null;
}
preserveBreakpoints();
}
@Nullable
@ -502,10 +498,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
List<StorableBreakpointDto> allDtoBreakpoints = new LinkedList<StorableBreakpointDto>();
List<Breakpoint> allBreakpoints = getBreakpointList();
if (currentBreakpoint != null) {
allBreakpoints.add(currentBreakpoint);
}
for (Breakpoint breakpoint : allBreakpoints) {
StorableBreakpointDto dto = dtoFactory.createDto(StorableBreakpointDto.class);
dto.setType(breakpoint.getType());
@ -525,9 +517,6 @@ public class BreakpointManagerImpl implements BreakpointManager,
dto.setFileProjectConfig(projectDto); //TODO need to think to change argument type from dto to model interface
}
}
dto.setActive(breakpoint.isActive());
allDtoBreakpoints.add(dto);
}
@ -558,17 +547,12 @@ public class BreakpointManagerImpl implements BreakpointManager,
return appContext.getWorkspaceRoot().getFile(dto.getPath()).then(new Function<Optional<File>, Void>() {
@Override
public Void apply(Optional<File> file) throws FunctionException {
if (!file.isPresent()) {
return null;
}
if (dto.getType() == Type.CURRENT) {
doSetCurrentBreakpoint(file.get(), dto.getLineNumber());
} else {
if (file.isPresent() && dto.getType() == Type.BREAKPOINT) {
addBreakpoint(new Breakpoint(dto.getType(),
dto.getLineNumber(),
dto.getPath(),
file.get(),
dto.isActive()));
false));
}
return null;
@ -672,12 +656,10 @@ public class BreakpointManagerImpl implements BreakpointManager,
}
@Override
public void onBreakpointDeleted(Breakpoint breakpoint) {
}
public void onBreakpointDeleted(Breakpoint breakpoint) { }
@Override
public void onAllBreakpointsDeleted() {
}
public void onAllBreakpointsDeleted() { }
@Override
public void onPreStepInto() {

View File

@ -64,6 +64,7 @@ import org.eclipse.che.ide.api.preferences.PreferencesManager;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.api.selection.Selection;
import org.eclipse.che.ide.api.workspace.event.WorkspaceStoppedEvent;
import org.eclipse.che.ide.editor.synchronization.EditorContentSynchronizer;
import org.eclipse.che.ide.part.editor.multipart.EditorMultiPartStackPresenter;
import org.eclipse.che.ide.part.explorer.project.ProjectExplorerPresenter;
@ -92,7 +93,8 @@ public class EditorAgentImpl implements EditorAgent,
ActivePartChangedHandler,
SelectionChangedHandler,
WindowActionHandler,
StateComponent {
StateComponent,
WorkspaceStoppedEvent.Handler {
private final EventBus eventBus;
private final WorkspaceAgent workspaceAgent;
@ -138,6 +140,7 @@ public class EditorAgentImpl implements EditorAgent,
eventBus.addHandler(ActivePartChangedEvent.TYPE, this);
eventBus.addHandler(SelectionChangedEvent.TYPE, this);
eventBus.addHandler(WindowActionEvent.TYPE, this);
eventBus.addHandler(WorkspaceStoppedEvent.TYPE, this);
}
@Override
@ -622,6 +625,13 @@ public class EditorAgentImpl implements EditorAgent,
}
}
@Override
public void onWorkspaceStopped(WorkspaceStoppedEvent event) {
for (EditorPartPresenter editor : getOpenedEditors()) {
closeEditor(editor);
}
}
private static class RestoreStateEditorCallBack extends OpenEditorCallbackImpl {
private final int cursorOffset;
private final int topLine;

View File

@ -18,10 +18,12 @@ import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.DefaultActionGroup;
import org.eclipse.che.ide.api.action.IdeActions;
import org.eclipse.che.ide.api.event.FileEvent;
import org.eclipse.che.ide.api.event.FileEvent.FileEventHandler;
import org.eclipse.che.ide.api.resources.File;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.resources.ResourceChangedEvent;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.util.Pair;
import org.eclipse.che.ide.util.loging.Log;
import java.util.Iterator;
import java.util.LinkedList;
@ -30,6 +32,7 @@ import java.util.List;
import static com.google.common.collect.Lists.newLinkedList;
import static org.eclipse.che.ide.api.constraints.Constraints.FIRST;
import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.OPEN;
import static org.eclipse.che.ide.api.resources.ResourceDelta.REMOVED;
/**
* Default implementation of Recent File List.
@ -37,7 +40,7 @@ import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.OPEN;
* @author Vlad Zhukovskiy
*/
@Singleton
public class RecentFileStore implements RecentFileList, FileEventHandler {
public class RecentFileStore implements RecentFileList {
public static final int MAX_FILES_IN_STACK = 25;
public static final int MAX_PATH_LENGTH_TO_DISPLAY = 50;
@ -60,7 +63,32 @@ public class RecentFileStore implements RecentFileList, FileEventHandler {
this.actionManager = actionManager;
this.recentFileActionFactory = recentFileActionFactory;
eventBus.addHandler(FileEvent.TYPE, this);
eventBus.addHandler(FileEvent.TYPE, event -> {
if (event.getOperationType() == OPEN) {
VirtualFile file = event.getFile();
if (file instanceof File) {
add((File)file);
}
}
});
eventBus.addHandler(ResourceChangedEvent.getType(), event -> {
if (event.getDelta().getKind() != REMOVED) {
return;
}
final Resource resource = event.getDelta().getResource();
if (!resource.isFile()) {
return;
}
if (recentStorage.contains(resource.asFile())) {
if (!remove(resource.asFile())) {
Log.warn(getClass(), "File has not been removed from recent list");
}
}
});
}
private void ensureGroupExist() {
@ -69,17 +97,6 @@ public class RecentFileStore implements RecentFileList, FileEventHandler {
}
}
/** {@inheritDoc} */
@Override
public void onFileOperation(FileEvent event) {
if (event.getOperationType() == OPEN) {
VirtualFile file = event.getFile();
if (file instanceof File) {
add((File)file);
}
}
}
/** {@inheritDoc} */
@Override
public boolean isEmpty() {

View File

@ -86,7 +86,6 @@ public class ProjectExplorerStateComponent implements StateComponent {
@Override
public void loadState(@NotNull JsonObject state) {
if (state.hasKey(SHOW_HIDDEN_FILES)) {
projectExplorer.showHiddenFiles(state.getBoolean(SHOW_HIDDEN_FILES));
}

View File

@ -544,7 +544,9 @@ public final class ResourceManager {
checkArgument(!source.getLocation().isRoot(), "Workspace root is not allowed to be copied");
return findResource(destination, true).thenPromise(resource -> {
checkState(!resource.isPresent() || force, "Cannot create '" + destination.toString() + "'. Resource already exists.");
if (resource.isPresent() && !force){
return promises.reject(new IllegalStateException("Cannot create '" + destination.toString() + "'. Resource already exists."));
}
return ps.copy(source.getLocation(), destination.parent(), destination.lastSegment(), force)
.thenPromise(ignored -> findResource(destination, false)

View File

@ -174,4 +174,5 @@ public class WorkspacePresenter implements Presenter, WorkspaceView.ActionDelega
}
}
}
}

View File

@ -321,13 +321,17 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
private JsonObject getPartStackState(PartStack partStack, WorkBenchPartController partController) {
JsonObject state = Json.createObject();
state.put("SIZE", partController.getSize());
state.put("STATE", partStack.getPartStackState().name());
if (partStack.getParts().isEmpty()) {
state.put("HIDDEN", true);
} else {
if (partStack.getActivePart() != null) {
state.put("ACTIVE_PART", partStack.getActivePart().getClass().getName());
}
state.put("HIDDEN", partController.isHidden());
JsonArray parts = Json.createArray();
state.put("PARTS", parts);
int i = 0;
@ -343,18 +347,22 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
@Override
public void loadState(@NotNull JsonObject state) {
if (state.hasKey("PART_STACKS")) {
JsonObject part_stacks = state.getObject("PART_STACKS");
for (String partStackType : part_stacks.keys()) {
JsonObject partStack = part_stacks.getObject(partStackType);
JsonObject partStacksState = state.getObject("PART_STACKS");
// Don't restore part dimensions if perspective is maximized.
boolean perspectiveMaximized = isPerspectiveMaximized(partStacksState);
for (String partStackType : partStacksState.keys()) {
JsonObject partStackState = partStacksState.getObject(partStackType);
switch (PartStackType.valueOf(partStackType)) {
case INFORMATION:
restorePartController(partStacks.get(INFORMATION), belowPartController, partStack);
loadPartStackState(partStacks.get(INFORMATION), belowPartController, partStackState, perspectiveMaximized);
break;
case NAVIGATION:
restorePartController(partStacks.get(NAVIGATION), leftPartController, partStack);
loadPartStackState(partStacks.get(NAVIGATION), leftPartController, partStackState, perspectiveMaximized);
break;
case TOOLING:
restorePartController(partStacks.get(TOOLING), rightPartController, partStack);
loadPartStackState(partStacks.get(TOOLING), rightPartController, partStackState, perspectiveMaximized);
break;
}
}
@ -370,9 +378,38 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
}
}
private void restorePartController(PartStack partStack, WorkBenchPartController controller, JsonObject partStackJSON) {
if (partStackJSON.hasKey("PARTS")) {
JsonArray parts = partStackJSON.get("PARTS");
/**
* Determines whether perspective is maximized.
*
* @param partStacksState part stack state
* @return <b>true</b> is perspective has maximized part stack
*/
private boolean isPerspectiveMaximized(JsonObject partStacksState) {
for (String partStackType : partStacksState.keys()) {
JsonObject partStackState = partStacksState.getObject(partStackType);
if (partStackState.hasKey("STATE") && PartStack.State.MAXIMIZED.name().equals(partStackState.getString("STATE"))) {
return true;
}
}
return false;
}
/**
* Set part stack state.
*
* @param partStack
* @param controller
* @param partStackState
* @param skipRestoreDimensions
*/
private void loadPartStackState(PartStack partStack,
WorkBenchPartController controller,
JsonObject partStackState,
boolean skipRestoreDimensions) {
if (partStackState.hasKey("PARTS")) {
JsonArray parts = partStackState.get("PARTS");
for (int i = 0; i < parts.length(); i++) {
JsonObject value = parts.get(i);
if (value.hasKey("CLASS")) {
@ -389,8 +426,8 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
}
// restore part stack's active part
if (partStackJSON.hasKey("ACTIVE_PART")) {
String activePart = partStackJSON.getString("ACTIVE_PART");
if (partStackState.hasKey("ACTIVE_PART")) {
String activePart = partStackState.getString("ACTIVE_PART");
Provider<PartPresenter> provider = dynaProvider.getProvider(activePart);
if (provider != null) {
partStack.setActivePart(provider.get());
@ -403,13 +440,17 @@ public abstract class AbstractPerspective implements Presenter, Perspective,
return;
}
if (partStackJSON.hasKey("HIDDEN") && partStackJSON.getBoolean("HIDDEN")) {
if (skipRestoreDimensions) {
return;
}
if (partStackState.hasKey("HIDDEN") && partStackState.getBoolean("HIDDEN")) {
partStack.minimize();
return;
}
if (partStackJSON.hasKey("SIZE")) {
double size = partStackJSON.getNumber("SIZE");
if (partStackState.hasKey("SIZE")) {
double size = partStackState.getNumber("SIZE");
// Size of the part must not be less 100 pixels.
if (size <= MIN_PART_SIZE) {

View File

@ -1,40 +0,0 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.toolbar.previews;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/** Tests for {@link PreviewUrlItem}. */
public class PreviewUrlItemTest {
private static final String URL = "http://preview.com";
private static final String DISPLAY_NAME = "dev-machine:8080";
private PreviewUrlItem previewUrlItem;
@Before
public void setUp() {
previewUrlItem = new PreviewUrlItem(URL, DISPLAY_NAME);
}
@Test
public void testGetUrl() throws Exception {
assertEquals(URL, previewUrlItem.getUrl());
}
@Test
public void testGetDisplayName() throws Exception {
assertEquals(DISPLAY_NAME, previewUrlItem.getDisplayName());
}
}

View File

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.toolbar.previews;
import org.eclipse.che.api.machine.shared.dto.MachineRuntimeInfoDto;
import org.eclipse.che.api.machine.shared.dto.ServerDto;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.machine.DevMachine;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/** Tests for {@link PreviewUrl}. */
@RunWith(MockitoJUnitRunner.class)
public class PreviewUrlTest {
private static final String PREVIEW_URL = "http://preview.com/param";
private static final String MACHINE_NAME = "dev-machine";
private static final String SERVER_PORT = "8080";
@Mock
private AppContext appContext;
private PreviewUrl previewUrl;
@Before
public void setUp() {
ServerDto server = mock(ServerDto.class);
when(server.getUrl()).thenReturn("http://preview.com");
Map<String, ServerDto> servers = new HashMap<>();
servers.put(SERVER_PORT + "/tcp", server);
MachineRuntimeInfoDto machineRuntimeInfo = mock(MachineRuntimeInfoDto.class);
when(machineRuntimeInfo.getServers()).thenReturn(servers);
DevMachine devMachine = mock(DevMachine.class);
when(devMachine.getDisplayName()).thenReturn(MACHINE_NAME);
when(devMachine.getRuntime()).thenReturn(machineRuntimeInfo);
when(appContext.getDevMachine()).thenReturn(devMachine);
previewUrl = new PreviewUrl(PREVIEW_URL, appContext);
}
@Test
public void testGetUrl() throws Exception {
assertEquals(PREVIEW_URL, previewUrl.getUrl());
}
@Test
public void testGetDisplayName() throws Exception {
assertEquals(MACHINE_NAME + ':' + SERVER_PORT + "/param", previewUrl.getDisplayName());
}
}

View File

@ -153,4 +153,5 @@ public class AppStateManagerTest {
assertThat(jsonObject.hasKey("key1")).isTrue();
assertThat(jsonObject.getString("key1")).isEqualTo("value1");
}
}

View File

@ -26,6 +26,7 @@ import org.eclipse.che.ide.api.constraints.Constraints;
import org.eclipse.che.ide.api.editor.AbstractEditorPresenter;
import org.eclipse.che.ide.api.event.ActivePartChangedEvent;
import org.eclipse.che.ide.api.parts.PartPresenter;
import org.eclipse.che.ide.api.parts.PartStack;
import org.eclipse.che.ide.api.parts.PartStackView;
import org.eclipse.che.ide.part.PartStackPresenter;
import org.eclipse.che.ide.workspace.PartStackPresenterFactory;
@ -39,6 +40,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mock;
import static org.mockito.Mockito.never;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@ -110,6 +112,8 @@ public class AbstractPerspectivePersistenceTest {
when(view.getInformationPanel()).thenReturn(simpleLayoutPanel);
when(view.getToolPanel()).thenReturn(simplePanel);
when(partStackPresenter.getPartStackState()).thenReturn(PartStack.State.NORMAL);
when(controllerFactory.createController(Matchers.<SplitLayoutPanel>anyObject(),
Matchers.<SimplePanel>anyObject())).thenReturn(workBenchController);
@ -224,4 +228,40 @@ public class AbstractPerspectivePersistenceTest {
verify(partStackPresenter).addPart(partPresenter);
}
@Test
public void shouldRestoreMaximizedPartStack() throws Exception {
JsonObject state = Json.createObject();
JsonObject parts = Json.createObject();
state.put("PART_STACKS", parts);
JsonObject partStack = Json.createObject();
parts.put("INFORMATION", partStack);
partStack.put("STATE", PartStack.State.MAXIMIZED.name());
JsonArray partsArray = Json.createArray();
partStack.put("PARTS", partsArray);
JsonObject part = Json.createObject();
partsArray.set(0, part);
part.put("CLASS", "foo.Bar");
partStack.put("SIZE", 142);
// partStackPresenter.getParts() must return non empty list
final List<PartPresenter> partPresenters = new ArrayList<>();
partPresenters.add(partPresenter);
when(partStackPresenter.getParts()).thenAnswer(new Answer<List<? extends PartPresenter>>() {
@Override
public List<? extends PartPresenter> answer(InvocationOnMock invocationOnMock) throws Throwable {
return partPresenters;
}
});
perspective.loadState(state);
verify(workBenchController, never()).setSize(142d);
}
}

View File

@ -106,4 +106,5 @@ public class WorkspacePresenterPersistenceTest {
verify(perspective1).loadState(perspective1State);
}
}

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-ide-generators</artifactId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-ide-stacks</artifactId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-ide-templates</artifactId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-ide-ui</artifactId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-orion-editor</artifactId>
<packaging>jar</packaging>

View File

@ -198,7 +198,7 @@
}
.textViewTooltipOnHover {
overflow: visible;
overflow: auto;
}
.orionCodenvy .textViewFind {

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.che.core</groupId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-core-ide-parent</artifactId>
<groupId>org.eclipse.che.core</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
</parent>
<artifactId>che-core-commons-gwt</artifactId>
<name>Che Core :: Commons :: GWT</name>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<groupId>org.eclipse.che.core</groupId>

View File

@ -16,7 +16,7 @@
<parent>
<artifactId>che-infrastructures-parent</artifactId>
<groupId>org.eclipse.che</groupId>
<version>5.9.0-SNAPSHOT</version>
<version>5.10.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>infrastructure-docker</artifactId>

View File

@ -12,6 +12,7 @@ package org.eclipse.che.workspace.infrastructure.docker;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.eclipse.che.api.core.NotFoundException;
@ -33,7 +34,9 @@ import org.eclipse.che.plugin.docker.client.UserSpecificDockerRegistryCredential
import org.eclipse.che.plugin.docker.client.exception.ContainerNotFoundException;
import org.eclipse.che.plugin.docker.client.exception.ImageNotFoundException;
import org.eclipse.che.plugin.docker.client.json.ContainerConfig;
import org.eclipse.che.plugin.docker.client.json.ContainerInfo;
import org.eclipse.che.plugin.docker.client.json.HostConfig;
import org.eclipse.che.plugin.docker.client.json.ImageConfig;
import org.eclipse.che.plugin.docker.client.json.PortBinding;
import org.eclipse.che.plugin.docker.client.json.Volume;
import org.eclipse.che.plugin.docker.client.json.container.NetworkingConfig;
@ -76,6 +79,7 @@ import java.util.regex.Pattern;
import static java.lang.String.format;
import static java.lang.Thread.sleep;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
@ -95,6 +99,28 @@ public class ServiceStarter {
public static final Pattern SNAPSHOT_LOCATION_PATTERN = Pattern.compile("(.+/)?" + MACHINE_SNAPSHOT_PREFIX + ".+");
static final String CONTAINER_EXITED_ERROR = "We detected that a machine exited unexpectedly. " +
"This may be caused by a container in interactive mode " +
"or a container that requires additional arguments to start. " +
"Please check the container recipe.";
// CMDs and entrypoints that lead to exiting of container right after start
private Set<List<String>> badCMDs = ImmutableSet.of(singletonList("/bin/bash"),
singletonList("/bin/sh"),
singletonList("bash"),
singletonList("sh"),
Arrays.asList("/bin/sh", "-c", "/bin/sh"),
Arrays.asList("/bin/sh", "-c", "/bin/bash"),
Arrays.asList("/bin/sh", "-c", "bash"),
Arrays.asList("/bin/sh", "-c", "sh"));
private Set<List<String>> badEntrypoints =
ImmutableSet.<List<String>>builder().addAll(badCMDs)
.add(Arrays.asList("/bin/sh", "-c"))
.add(Arrays.asList("/bin/bash", "-c"))
.add(Arrays.asList("sh", "-c"))
.add(Arrays.asList("bash", "-c"))
.build();
private final DockerConnector docker;
private final UserSpecificDockerRegistryCredentialsProvider dockerCredentials;
private final ExecutorService executor;
@ -273,6 +299,8 @@ public class ServiceStarter {
docker.startContainer(StartContainerParams.create(container));
checkContainerIsRunning(container);
readContainerLogsInSeparateThread(container,
workspaceId,
service.getId(),
@ -478,6 +506,8 @@ public class ServiceStarter {
addStaticDockerConfiguration(config);
setNonExitingContainerCommandIfNeeded(config);
return docker.createContainer(CreateContainerParams.create(config)
.withContainerName(service.getContainerName()))
.getId();
@ -523,6 +553,36 @@ public class ServiceStarter {
composeService.getNetworks().addAll(additionalNetworks);
}
// We can detect certain situation when container exited right after start.
// We can detect
// - when no command/entrypoint is set
// - when most common shell interpreters are used and require additional arguments
// - when most common shell interpreters are used and they require interactive mode which we don't support
// When we identify such situation we change CMD/entrypoint in such a way that it runs "tail -f /dev/null".
// This command does nothing and lasts until workspace is stopped.
// Images such as "ubuntu" or "openjdk" fits this situation.
protected void setNonExitingContainerCommandIfNeeded(ContainerConfig containerConfig) throws IOException {
ImageConfig imageConfig = docker.inspectImage(containerConfig.getImage()).getConfig();
List<String> cmd = imageConfig.getCmd() == null ?
null : Arrays.asList(imageConfig.getCmd());
List<String> entrypoint = imageConfig.getEntrypoint() == null ?
null : Arrays.asList(imageConfig.getEntrypoint());
if ((entrypoint == null || badEntrypoints.contains(entrypoint)) && (cmd == null || badCMDs.contains(cmd))) {
containerConfig.setCmd("tail", "-f", "/dev/null");
containerConfig.setEntrypoint((String[])null);
}
}
// Inspect container right after start to check if it is running,
// otherwise throw error that command should not exit right after container start
protected void checkContainerIsRunning(String container) throws IOException, ServerException {
ContainerInfo containerInfo = docker.inspectContainer(container);
if ("exited".equals(containerInfo.getState().getStatus())) {
throw new ServerException(CONTAINER_EXITED_ERROR);
}
}
private void readContainerLogsInSeparateThread(String container,
String workspaceId,
String machineId,

Some files were not shown because too many files have changed in this diff Show More