<commit_before>package com.sixsq.slipstream.run;

/*
 * +=================================================================+
 * SlipStream Server (WAR)
 * =====
 * Copyright (C) 2013 SixSq Sarl (sixsq.com)
 * =====
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * -=================================================================-
 */

import java.io.IOException;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.Delete;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.Put;
import org.restlet.resource.ResourceException;

import com.sixsq.slipstream.exceptions.CannotAdvanceFromTerminalStateException;
import com.sixsq.slipstream.exceptions.InvalidStateException;
import com.sixsq.slipstream.exceptions.NotFoundException;
import com.sixsq.slipstream.exceptions.SlipStreamClientException;
import com.sixsq.slipstream.exceptions.ValidationException;
import com.sixsq.slipstream.persistence.PersistenceUtil;
import com.sixsq.slipstream.persistence.Run;
import com.sixsq.slipstream.persistence.RuntimeParameter;
import com.sixsq.slipstream.statemachine.StateMachine;
import com.sixsq.slipstream.statemachine.States;

public class RuntimeParameterResource extends RunBaseResource {

	private RuntimeParameter runtimeParameter;

	private String key;

	@Override
	public void initializeSubResource() throws ResourceException {

		long start = System.currentTimeMillis();
		long before;

		before = System.currentTimeMillis();
		parseRequest();
		logTimeDiff("parseRequest", before);

		before = System.currentTimeMillis();
		fetchRepresentation();
		logTimeDiff("fetchRepresentation", before);

		before = System.currentTimeMillis();
		raiseConflictIfAbortIsSet();
		logTimeDiff("raiseConflictIfAbortIsSet", before);

		logTimeDiff("initialize on runtime parameter", start);
	}

	private void parseRequest() {
		extractAndSetIgnoreAbort();

		key = getAttribute("key");
	}

	private void fetchRepresentation() {
		runtimeParameter = loadRuntimeParameter(key);
		setExisting(runtimeParameter != null);
	}

	private void raiseConflictIfAbortIsSet() {
		if (!getIgnoreAbort()) {
			if (isAbortSet()) {
				throw (new ResourceException(Status.CLIENT_ERROR_CONFLICT,
						"Abort flag raised!"));
			}
		}
	}

	private void abortOrReset(String abortMessage, EntityManager em) {
		String nodename = RuntimeParameter.extractNodeNamePart(key);
		Run.abortOrReset(abortMessage, nodename, em, getUuid());
	}


	@Delete
	public void resetRuntimeParameter() throws ResourceException {
		runtimeParameter.setValue("");
		runtimeParameter.setIsSet(false);
		runtimeParameter.store();

		getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
	}

	@Get
	public String represent() throws ResourceException, NotFoundException,
			ValidationException {

		long start = System.currentTimeMillis();

		if (!runtimeParameter.isSet()) {
			throw new ResourceException(
					Status.CLIENT_ERROR_PRECONDITION_FAILED, "key " + key
							+ " not yet set");
		}

		logTimeDiff("processing get on runtime parameter", start);
		return runtimeParameter.getValue();
	}

	@Put
	public void update(Representation entity) throws ResourceException,
			NotFoundException, ValidationException {

		EntityManager em = PersistenceUtil.createEntityManager();
		EntityTransaction transaction = em.getTransaction();
		transaction.begin();
		runtimeParameter = em.merge(runtimeParameter);

		String value = (entity == null ? "" : extractValueFromEntity(entity));

		boolean isGlobalAbort = RuntimeParameter.GLOBAL_ABORT_KEY.equals(key);
		boolean isNodeAbort = (runtimeParameter.getNodeName()
				+ RuntimeParameter.NODE_PROPERTY_SEPARATOR + RuntimeParameter.ABORT_KEY)
				.equals(key);

		if (isGlobalAbort || isNodeAbort) {
			if (!runtimeParameter.isSet()) {
				abortOrReset(value, em);
				setValue(value);
			}
		} else {
			setValue(value);
		}

		transaction.commit();
		em.close();
		getResponse().setEntity(null, MediaType.ALL);
	}

	private void setValue(String value) {
		runtimeParameter.setValue(value);
		RuntimeParameterMediator.processSpecialValue(runtimeParameter);
	}

	private String extractValueFromEntity(Representation entity) {
		try {
			return entity.getText();
		} catch (IOException e) {
			throw (new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
					"Bad value for key: " + key + " with details: "
							+ e.getMessage()));
		}
	}

	@Post
	public void completeCurrentNodeState(Representation entity) {

		String nodeName = runtimeParameter.getNodeName();
		States newState = null;

		newState = attemptCompleteCurrentNodeState(nodeName);

		getResponse().setEntity(newState.toString(), MediaType.TEXT_PLAIN);
	}

	private States attemptCompleteCurrentNodeState(String nodeName) {
		EntityManager em = PersistenceUtil.createEntityManager();
		Run run = Run.loadFromUuid(getUuid(), em);
		StateMachine sc = StateMachine.createStateMachine(run);
		em.close();
		return completeCurrentNodeState(nodeName, sc);
	}

	public States completeCurrentNodeState(String nodeName, StateMachine sc) {
		States state;
		try {
			state = sc.updateState(nodeName);
		} catch (CannotAdvanceFromTerminalStateException e) {
			throw new ResourceException(Status.CLIENT_ERROR_CONFLICT,
					e.getMessage());
		} catch (InvalidStateException e) {
			throw new ResourceException(Status.CLIENT_ERROR_CONFLICT,
					e.getMessage());
		} catch (SlipStreamClientException e) {
			throw new ResourceException(Status.CLIENT_ERROR_CONFLICT,
					e.getMessage());
		}
		return state;
	}

	@Override
	protected String getPageRepresentation() {<commit_msg>POST on ss:state triggers Ready->Provisioning transition if run is mutable.<commit_after>package com.sixsq.slipstream.run;

/*
 * +=================================================================+
 * SlipStream Server (WAR)
 * =====
 * Copyright (C) 2013 SixSq Sarl (<EMAIL>)
 * =====
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * -=================================================================-
 */

import java.io.IOException;
import java.util.logging.Logger;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;

import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.resource.Delete;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.Put;
import org.restlet.resource.ResourceException;

import com.sixsq.slipstream.exceptions.CannotAdvanceFromTerminalStateException;
import com.sixsq.slipstream.exceptions.InvalidStateException;
import com.sixsq.slipstream.exceptions.NotFoundException;
import com.sixsq.slipstream.exceptions.SlipStreamClientException;
import com.sixsq.slipstream.exceptions.ValidationException;
import com.sixsq.slipstream.persistence.PersistenceUtil;
import com.sixsq.slipstream.persistence.Run;
import com.sixsq.slipstream.persistence.RuntimeParameter;
import com.sixsq.slipstream.statemachine.StateMachine;
import com.sixsq.slipstream.statemachine.States;

public class RuntimeParameterResource extends RunBaseResource {

	private RuntimeParameter runtimeParameter;

	private String key;

	@Override
	public void initializeSubResource() throws ResourceException {

		long start = System.currentTimeMillis();
		long before;

		before = System.currentTimeMillis();
		parseRequest();
		logTimeDiff("parseRequest", before);

		before = System.currentTimeMillis();
		fetchRepresentation();
		logTimeDiff("fetchRepresentation", before);

		before = System.currentTimeMillis();
		raiseConflictIfAbortIsSet();
		logTimeDiff("raiseConflictIfAbortIsSet", before);

		logTimeDiff("initialize on runtime parameter", start);
	}

	private void parseRequest() {
		extractAndSetIgnoreAbort();

		key = getAttribute("key");
	}

	private void fetchRepresentation() {
		runtimeParameter = loadRuntimeParameter(key);
		setExisting(runtimeParameter != null);
	}

	private void raiseConflictIfAbortIsSet() {
		if (!getIgnoreAbort()) {
			if (isAbortSet()) {
				throw (new ResourceException(Status.CLIENT_ERROR_CONFLICT,
						"Abort flag raised!"));
			}
		}
	}

	private void abortOrReset(String abortMessage, EntityManager em) {
		String nodename = RuntimeParameter.extractNodeNamePart(key);
		Run.abortOrReset(abortMessage, nodename, em, getUuid());
	}


	@Delete
	public void resetRuntimeParameter() throws ResourceException {
		runtimeParameter.setValue("");
		runtimeParameter.setIsSet(false);
		runtimeParameter.store();

		getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
	}

	@Get
	public String represent() throws ResourceException, NotFoundException,
			ValidationException {

		long start = System.currentTimeMillis();

		if (!runtimeParameter.isSet()) {
			throw new ResourceException(
					Status.CLIENT_ERROR_PRECONDITION_FAILED, "key " + key
							+ " not yet set");
		}

		logTimeDiff("processing get on runtime parameter", start);
		return runtimeParameter.getValue();
	}

	@Put
	public void update(Representation entity) throws ResourceException,
			NotFoundException, ValidationException {

		EntityManager em = PersistenceUtil.createEntityManager();
		EntityTransaction transaction = em.getTransaction();
		transaction.begin();
		runtimeParameter = em.merge(runtimeParameter);

		String value = (entity == null ? "" : extractValueFromEntity(entity));

		boolean isGlobalAbort = RuntimeParameter.GLOBAL_ABORT_KEY.equals(key);
		boolean isNodeAbort = (runtimeParameter.getNodeName()
				+ RuntimeParameter.NODE_PROPERTY_SEPARATOR + RuntimeParameter.ABORT_KEY)
				.equals(key);

		if (isGlobalAbort || isNodeAbort) {
			if (!runtimeParameter.isSet()) {
				abortOrReset(value, em);
				setValue(value);
			}
		} else {
			setValue(value);
		}

		transaction.commit();
		em.close();
		getResponse().setEntity(null, MediaType.ALL);
	}

	private void setValue(String value) {
		runtimeParameter.setValue(value);
		RuntimeParameterMediator.processSpecialValue(runtimeParameter);
	}

	private String extractValueFromEntity(Representation entity) {
		try {
			return entity.getText();
		} catch (IOException e) {
			throw (new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
					"Bad value for key: " + key + " with details: "
							+ e.getMessage()));
		}
	}

	@Post
	public void completeCurrentNodeStateOrChangeGlobalState(Representation entity) {

		String nodeName = runtimeParameter.getNodeName();
		States newState;

		if (RuntimeParameter.GLOBAL_NAMESPACE.equals(nodeName)) {
			newState = attemptChangeGlobalStateReadyToProvisioning();
		} else {
			newState = attemptCompleteCurrentNodeState(nodeName);
		}

		getResponse().setEntity(newState.toString(), MediaType.TEXT_PLAIN);
	}

	private States attemptChangeGlobalStateReadyToProvisioning() {
		States newState;
		Run run = Run.loadFromUuid(getUuid());
		String stateFromTo = String.format("from %s to %s", States.Ready, States.Provisioning);
		if (run.isMutable() && States.Ready == run.getState()) {
            StateMachine sc = StateMachine.createStateMachine(run);
            try {
                sc.tryAdvanceToProvisionning();
            } catch (InvalidStateException |CannotAdvanceFromTerminalStateException e) {
                e.printStackTrace();
                throw (new ResourceException(Status.SERVER_ERROR_INTERNAL,
                        String.format("Failed to advance state %s: %s", stateFromTo, e.getMessage())));
            }
            newState = sc.getState();
            if (States.Provisioning != newState) {
                throw (new ResourceException(Status.SERVER_ERROR_INTERNAL,
                        String.format("Failed to advance state %s: requested doesn't match reached %s.",
								stateFromTo, newState)));
            }
        } else {
            throw (new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST,
                    "Bad request: via API a state can be advanced only on a mutable run and only " + stateFromTo ));
        }
		return newState;
	}

	private States attemptCompleteCurrentNodeState(String nodeName) {
		EntityManager em = PersistenceUtil.createEntityManager();
		Run run = Run.loadFromUuid(getUuid(), em);
		StateMachine sc = StateMachine.createStateMachine(run);
		em.close();
		return completeCurrentNodeState(nodeName, sc);
	}

	public States completeCurrentNodeState(String nodeName, StateMachine sc) {
		States state;
		try {
			state = sc.updateState(nodeName);
		} catch (CannotAdvanceFromTerminalStateException e) {
			throw new ResourceException(Status.CLIENT_ERROR_CONFLICT,
					e.getMessage());
		} catch (InvalidStateException e) {
			throw new ResourceException(Status.CLIENT_ERROR_CONFLICT,
					e.getMessage());
		} catch (SlipStreamClientException e) {
			throw new ResourceException(Status.CLIENT_ERROR_CONFLICT,
					e.getMessage());
		}
		return state;
	}

	@Override
	protected String getPageRepresentation() {
{'fraction_non_alphanumeric': 0.11762612965203159, 'fraction_numerical': 0.0011385469294812496, 'mean_word_length': 4.868058455114823, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '20399', 'n_tokens_mistral': 4251, 'n_tokens_neox': 3901, 'n_words': 893}